目前我正在为 Arm 9 嵌入式应用程序开发两个驱动程序。它们都是 i2c 驱动程序,每个都使用 IO 扩展器 pcf8575。我已经独立测试了驱动程序,但是当我将它们都编译到内核中并运行我的主应用程序时,我得到了下面的核心转储。它通常不会在第一次驱动程序访问期间发生。这似乎是随机发生的。当我的驱动程序调用 i2c 驱动程序在 I2c 总线上传输时肯定会发生这种情况(i2c_master_send(client, &buff[0], count) )。我将附上我的两个驱动程序的主要文件。它们很相似,都非常简单。我已经独立地对它们进行了很多测试。通过在两个非常不同的硬件上获得相同的结果,我已将其排除为硬件问题。我想我 我什至不确定它何时说内核分页请求甚至开始查找。我很难相信问题出在 i2c-core 驱动程序中,但这是它正在死去的代码部分。
> Unable to handle kernel paging request at virtual address 000b9a81
> pgd = cfb80000 [000b9a81] *pgd=3fba1031,
> *pte=3f0e43cf, *ppte=3f0e4ffe Internal error: Oops: 17 [#1] Modules linked
> in: i2c_lcd gpio CPU: 0 Not tainted
> (2.6.28.10 #1036) PC is at s3c24xx_i2c_irq+0x308/0x5c4 LR is at
> handle_IRQ_event+0x44/0x80 pc :
> [<c01e7ce8>] lr : [<c00a870c>]
> psr: 80000093 sp : cfbe1d08 ip :
> cfbe1d2c fp : cfbe1d28 r10: cfbe0000
> r9 : 00000000 r8 : 00000004 r7 :
> ffffffea r6 : 00000000 r5 : c0312cf4
> r4 : 000000f0 r3 : 000b9a80 r2 :
> cfbe1ec4 r1 : d1200000 r0 : 00000001
> Flags: Nzcv IRQs off FIQs on Mode
> SVC_32 ISA ARM Segment user Control:
> c000717f Table: 3fbf0000 DAC:
> 00000015 Process QEC_core_app (pid:
> 457, stack limit = 0xcfbe0260)
> Stack: (0xcfbe1d08 to 0xcfbe2000)
> 1d00: cfab65c0 00000000 00000000 0000002b c0318760 cfbe1d48
> 1d20: cfbe1d2c c00a870c c01e79f0 c03098a0 0000002b cfab65c0 c032bb2c cfbe1d68
> 1d40: cfbe1d4c c00a9bc0 c00a86d8 0000002b 00000000 08000000 cf83acc0 cfbe1d80
> 1d60: cfbe1d6c c0072064 c00a9ac0 ffffffff f4000000 cfbe1df4 cfbe1d84 c0072a44
> 1d80: c0072010 cfaf4080 cfbe0000 80000013 cf96b360 cfb7a500 cfaf4080 cf96b360
> 1da0: cf83acc0 c0318760 00000000 cfbe0000 cfbe1df4 00000000 cfbe1dcc c0261ccc
> 1dc0: c0261d00 a0000013 ffffffff ffff2f7e cfbe1df8 c03081f8 000003e8 cfbe0000
> 1de0: cfbe1e48 cfbe1e3c cfbe1e2c cfbe1df8 c0261f40 c0261aa4 c0329fa0 c0314c54
> 1e00: ffff2f7e c008eb54 cfaf4080 c0329620 000003e8 c0312cf4 00000000 00000000
> 1e20: cfbe1e78 cfbe1e30 c01e78fc c0261eb8 00000001 cfbe1ec4 c0312d30 00000000
> 1e40: cfaf4080 c0099368 c0312cf4 c0312cf4 00000057 c02c04b3 00000002 c0312d30
> 1e60: cfbe1ed0 c0312d70 00000001 cfbe1ec0 cfbe1e7c c01e4050 c01e77a0 00000057
> 1e80: 00000022 00000002 c02c04b3 c0312dfc 00000001 cfbe1ec4 00000002 000b9a60
> 1ea0: 40015104 cfb7fb60 000b9a60 cfbe0000 bd9ffe20 cfbe1ee0 cfbe1ec4 c01e4e04
> 1ec0: c01e3f60 00000022 cfb70002 000b9a80 bf003e34 cfbe1efc cfbe1ee4 bf00323c
> 1ee0: c01e4dd0 000b9a80 00000002 cfb7fb60 cfbe1f18 cfbe1f00 c00d8ef0 bf00315c
> 1f00: 00000008 cfb7fb60 40015104 cfbe1f80 cfbe1f1c c00d9370 c00d8e90 00e5a510
> 1f20: 00000000 cfb3bf24 00000000 00000000 0649c1a8 0000001c 0648fe58 0000001c
> 1f40: c009bc58 c03089e4 00000000 cfbe1f4c cfbe1f4c 00000000 00000000 00000008
> 1f60: 000b9a60 40015104 cfb7fb60 c0073004 bd9ffe20 cfbe1fa4 cfbe1f84 c00d9404
> 1f80: c00d8f38 00000001 40095dbc bd9ffe20 40096008 00000036 00000000 cfbe1fa8
> 1fa0: c0072e60 c00d93d4 40095dbc bd9ffe20 00000008 40015104 000b9a60 00000008
> 1fc0: 40095dbc bd9ffe20 40096008 00000000 00000000 00000000 bd9ffe20 bd9ffd0c
> 1fe0: 0007f680 bd9ffce4 0003cfc8 400a8e2c 80000010 00000008 ff00ffff ffffffff
> Backtrace:
> [<c01e79e0>] (s3c24xx_i2c_irq+0x0/0x5c4) from [<c00a870c>] handle_IRQ_event+0x44/0x80)
> r8:c0318760 r7:0000002b r6:00000000
> r5:00000000 r4:cfab65c0
> [<c00a86c8>] (handle_IRQ_event+0x0/0x80) from [<c00a9bc0>] (handle_edge_irq+0x110/0x14c)
> r7:c032bb2c r6:cfab65c0 r5:0000002b
> r4:c03098a0 [<c00a9ab0>]
> (handle_edge_irq+0x0/0x14c) from [<c0072064>] (__exception_text_start+0x64/0x84)
> r7:cf83acc0 r6:08000000 r5:00000000
> r4:0000002b
> [<c0072000>] (__exception_text_start+0x0/0x84) from [<c0072a44>] (__irq_svc+0x24/0xa0)
> Exception stack(0xcfbe1d84 to 0xcfbe1dcc)
> 1d80: cfaf4080 cfbe0000 80000013 cf96b360 cfb7a500 cfaf4080 cf96b360
> 1da0: cf83acc0 c0318760 00000000 cfbe0000 cfbe1df4 00000000 cfbe1dcc c0261ccc
> 1dc0: c0261d00 a0000013 ffffffff
> r5:f4000000 r4:ffffffff
> [<c0261a94>] (schedule+0x0/0x2b8) from [<c0261f40>] (schedule_timeout+0x98/0xc4)
> [<c0261ea8>] (schedule_timeout+0x0/0xc4) from [<c01e78fc>](s3c24xx_i2c_xfer+0x16c/0x250)
> r7:00000000 r6:00000000 r5:c0312cf4
> r4:000003e8
> [<c01e7790>] (s3c24xx_i2c_xfer+0x0/0x250) from [<c01e4050>] (i2c_transfer+0x100/0x148)
> [<c01e3f50>] (i2c_transfer+0x0/0x148) from [<c01e4e04>] (i2c_master_send+0x44/0x54)
> [<c01e4dc0>] (i2c_master_send+0x0/0x54) from [<bf00323c>] (pcf8575_ioctl+0xf0/0x11c [i2c_lcd])
> r4:bf003e34
> [<bf00314c>] (pcf8575_ioctl+0x0/0x11c [i2c_lcd]) from [<c00d8ef0>] (vfs_ioctl+0x70/0x80)
> r4:cfb7fb60
> [<c00d8e80>] (vfs_ioctl+0x0/0x80) from [<c00d9370>] (do_vfs_ioctl+0x448/0x49c)
> r6:40015104 r5:cfb7fb60 r4:00000008
> [<c00d8f28>] (do_vfs_ioctl+0x0/0x49c) from [<c00d9404>] (sys_ioctl+0x40/0x5c)
> [<c00d93c4>] (sys_ioctl+0x0/0x5c) from [<c0072e60>] (ret_fast_syscall+0x0/0x2c)
> r7:00000036 r6:40096008 r5:bd9ffe20
> r4:40095dbc Code: e1500003 2a00000c
> e5923008 e5951028 (e7d32000) Kernel
> panic - not syncing: Fatal exception in interrupt
这是pcf8575 lcd驱动程序文件(另一个驱动程序几乎是一个剪切和粘贴,它被设置为读取而不是写入)
/*
* linux/drivers/i2c/chips/i2c_lcd.c
*
* Copyright (C) 2011 Dan Dodge <ddodge@quintechelectronics.com>
*
* based on linux/drivers/i2c/chips/ds1337.c
* Copyright (C) 2005 James Chapman <jchapman@katalix.com>
*
* based on linux/drivers/acron/char/pcf8583.c
* Copyright (C) 2000 Russell King
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Driver for Philips pcf8575 i2c io expander
*/
//TODO: added these in because we found them in led-class.c
#include <linux/device.h>
#include <linux/sysdev.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/string.h>
#include <linux/rtc.h>
#include "i2c_lcd.h"
/*
* (Hopefully) unused major number to provide /dev/'*' access to the chip
*/
#define PCF8575_LCD_MAJOR 122
/*
* I2C client structure allocated when driver's detect function is called.
* Because this driver is designed/hacked to replace /dev/rtc, the ioctl() file
* system operation has no way to access the client structure without saving its
* address here.
*/
static struct i2c_client *pcf8575_client;
// 0x21 should be the keys
// 0x22 should be the lcd
static unsigned short normal_i2c[] = {0x22, I2C_CLIENT_END}; // 0x21 for keypad 0x22 for lcd
I2C_CLIENT_INSMOD_1(pcf8575);
static int pcf8575_attach_adapter(struct i2c_adapter *adapter);
static int pcf8575_detect(struct i2c_adapter *adapter, int address, int kind);
static void pcf8575_init_client(struct i2c_client *client);
static int pcf8575_detach_client(struct i2c_client *client);
static int pcf8575_command(struct i2c_client *client, unsigned int cmd,
void *arg);
static struct i2c_driver pcf8575_driver = {
.driver = {
.name = "pcf8575_lcd",
},
.attach_adapter = pcf8575_attach_adapter,
.detach_client = pcf8575_detach_client,
.command = pcf8575_command,
};
// this will hold some different messages for testing
char LCDMsg[4][21];
/******************************************************************************
Purpose:
Read an 8-bit value from the given register.
Parameters:
client => Address of I2C subsystem client structure.
reg ====> Offset of register to read.
value ==> Address where value read should be stored.
Return Value:
0
Success.
-EIO
Chip access failed.
******************************************************************************/
static inline int
pcf8575_read(struct i2c_client *client, u8 reg, u8 *value) {
s32 data;
data = i2c_smbus_read_byte_data(client, reg);
if (data < 0)
return -EIO;
*value = data;
return 0;
}
/******************************************************************************
Purpose:
Write an 8-bit value to the given register.
Parameters:
client => Address of I2C subsystem client structure.
reg ====> Offset of register to write.
value ==> Value to write.
Return Value:
0
Success.
-EIO
Chip access failed.
******************************************************************************/
static inline int
pcf8575_write(struct i2c_client *client, u8 reg, u8 value) {
s32 error;
error = i2c_smbus_write_byte_data(client, reg, value);
if (error < 0) {
return -EIO;
}
return 0;
}
/******************************************************************************
Purpose:
Read a single character from the Key pad
Parameters:
client => Address of I2C subsystem client structure
char => pointer to place single character in
Return value:
0
Success
-EINVAL
character pointer is NULL
-EIO
Read failed
******************************************************************************/
static inline int
pcf8575_write_char ( struct i2c_client *client, const char *buff, unsigned count) {
int result; // captures an error from a read
//printk ( KERN_ERR "pcf8575-lcd: at the top of pcf8575_write_char\n");
if (!buff) {
return -EINVAL;
}
//printk ( KERN_ERR "pcf8575-lcd: survived the if(!buff) statement\n");
/*
* Read the keypad
*/
result = i2c_master_send(client, &buff[0], count);
//printk ( KERN_ERR "pcf8575-lcd: survived the i2c_master_send statement\n");
if(result < 0) {
return -EIO;
}
//printk ( KERN_ERR "pcf8575-lcd: at the bottom of pcf8575_write_char\n");
return result;
}
/******************************************************************************
Purpose:
Write a single character from the Key pad
Parameters:
client => Address of I2C subsystem client structure
char => pointer to place single character in
Return value:
0
Success
- ..........
******************************************************************************/
static inline int
pcf8575_read_char(struct i2c_client *client, char *key) {
printk( KERN_ERR "pcf8575: nothing to do on a read for the key pad\n");
return 0;
}
/******************************************************************************
Purpose:
Implement command functionality for I2C sensors subsystem.
Parameters:
client => Address of I2C subsystem client structure.
cmd ====> Command to perform.
arg ====> Address of argument to command.
Return Value:
-ENOSYS
Functionality is not implemented.
NOTE:
This function always returns an error condition because the driver is
designed/hacked to replace /dev/rtc rather than be another I2C sensor.
******************************************************************************/
static int
pcf8575_command(struct i2c_client *client, unsigned int cmd, void *arg) {
return -ENOSYS;
}
/******************************************************************************
Purpose:
Check for presence of devices on I2C bus we want to attach to.
Parameters:
adapter => Address of I2C subsystem adapter structure.
Return Value:
Please see i2c_detect() for information on possible values returned.
******************************************************************************/
static int
pcf8575_attach_adapter(struct i2c_adapter *adapter) {
return i2c_probe(adapter, &addr_data, pcf8575_detect);
}
/******************************************************************************
Purpose:
Probe for pcf8575 device at given I2C bus slave address.
Parameters:
adapter => Address of I2C subsystem adapter structure.
address => I2C bus slave address to probe.
kind ====> Type of probe to perform.
Return Value:
0
Success.
-ENOMEM
I2C client structure memory allocation failed.
-EIO
Chip access failed.
******************************************************************************/
static int
pcf8575_detect(struct i2c_adapter *adapter, int address, int kind) {
struct i2c_client *new_client;
int err = 0;
/*
* Check for required I2C functionality
*/
if (
! i2c_check_functionality(
adapter, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_I2C
)
) {
printk(
KERN_ERR "pcf8575: Required I2C functionality not present.\n"
);
err = -ENOSYS;
goto exit;
}
/*
* Allocate memory for I2C client structure. This allows pcf8575_ioctl() to
* access the chip
*/
//new_client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
new_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
if (! new_client) {
printk(KERN_ERR "pcf8575: I2C client structure allocation failed.\n");
err = -ENOMEM;
goto exit;
}
/*
* Partially set up I2C client structure
*/
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &pcf8575_driver;
new_client->flags = 0;
/*
* No attempt is made to read chip registers and verify that we are talking
* to a pcf8575. We simply assume one is present at the I2C slave address.
* This allows the driver to be fully functional so that the registers can
* be fixed if they ever become corrupted.
*/
strlcpy(new_client->name, "pcf8575_lcd", I2C_NAME_SIZE);
/*
* Register client with I2C core
*/
err = i2c_attach_client(new_client);
if (err)
goto exit_free;
/*
* Do chip initialization
*/
pcf8575_init_client(new_client);
return 0;
exit_free:
kfree(new_client);
exit:
return err;
}
/******************************************************************************
Purpose:
Initialize a chip after it has been probed successfully.
Parameters:
client => Address of I2C subsystem client structure.
Return Value:
None.
******************************************************************************/
static void
pcf8575_init_client(struct i2c_client *client) {
/*
* Save I2C client structure address so that pcf8575_ioctl() can access the
* chip
*/
pcf8575_client = client;
/*
* TODO : remove this debug
*/
printk(KERN_ERR "pcf7585: QEC LCD driver successfully initialized\n");
}
/******************************************************************************
Purpose:
Callback function to handle I2C bus or client driver removal.
Parameters:
client => Address of I2C subsystem client structure.
Return Value:
Please see i2c_detach_client() for information on possible values returned.
******************************************************************************/
static int
pcf8575_detach_client(struct i2c_client *client) {
int error;
/*
* Unregister client with I2C core
*/
error = i2c_detach_client(client);
if (error) {
printk(KERN_ERR "pcf8575: Client unregistration failed.\n");
}
return error;
}
/******************************************************************************
Purpose:
Handle /dev/rtc ioctl() system calls.
Parameters:
inode ====> Address of file's inode structure.
file =====> Address of kernel's file structure.
request ==> Request code.
argument => Argument passed in from user space.
Return Value:
0
Success.
-ENODEV
The chip was not initialized.
-EFAULT
argument is not a valid user address.
-EINVAL
request is not valid.
******************************************************************************/
static int
pcf8575_ioctl(
struct inode *inode,
struct file *file,
unsigned int request,
unsigned long argument
) {
struct i2c_lcd_struct i2c_lcd_data;
/*
* Sanity check for device initialization to prevent a kernel oops from
* occurring
*/
if (! pcf8575_client) {
return -ENODEV;
}
switch (request) {
case LCD_READ:
i2c_lcd_data.string = "!!!";
i2c_lcd_data.count = 4;
if (
copy_to_user(
(struct i2c_lcd_struct *) argument,
&i2c_lcd_data,
sizeof(i2c_lcd_data)
)
) {
return -EFAULT;
}
break;
case LCD_WRITE:
//printk ( KERN_ERR "pcf8575-lcd: at the top of write ioctl\n");
if (
copy_from_user(
&i2c_lcd_data,
(struct i2c_lcd_struct *) argument,
sizeof(i2c_lcd_data)
)
) {
return -EFAULT;
}
//printk ( KERN_ERR "pcf8575-lcd: at the bottom of write ioctl\n");
return pcf8575_write_char(pcf8575_client, i2c_lcd_data.string, i2c_lcd_data.count);
break;
default:
return -EINVAL;
break;
}
return 0;
}
static struct file_operations pcf8575_fops = {
.owner = THIS_MODULE,
.ioctl = pcf8575_ioctl
};
static struct class *pcf8575_class;
/******************************************************************************
Purpose:
Perform actions required on driver initialization.
Parameters:
None.
Return Value:
Please see i2c_add_driver() and register_chrdev() for information on
possible values returned.
******************************************************************************/
static int __init
pcf8575_init(void) {
int error;
/*
* Assume no I2C client structure allocated yet
*/
pcf8575_client = NULL;
/*
* Register this driver with I2C subsystem
*/
error = i2c_add_driver(&pcf8575_driver);
if (error) {
printk(KERN_ERR "pcf8575: I2C driver registration failed.\n");
return error;
}
/*
* Register /dev/rtc character device major number
*/
error = register_chrdev(PCF8575_LCD_MAJOR, "pcf8575_lcd", &pcf8575_fops);
if (error) {
printk(KERN_ERR "pcf8575: Major number registration failed.\n");
}
pcf8575_class = class_create(THIS_MODULE, "lcd-pcf8575");
if (IS_ERR(pcf8575_class)) {
printk(KERN_ERR "Error creating pcf8575 class.\n");
unregister_chrdev(PCF8575_LCD_MAJOR, "pcf8575_lcd");
return PTR_ERR(pcf8575_class);
}
device_create(pcf8575_class, NULL, MKDEV(PCF8575_LCD_MAJOR, 0), NULL, "lcd");
return error;
}
/******************************************************************************
Purpose:
Perform actions required on driver deinitialization.
Parameters:
None.
Return Value:
None.
******************************************************************************/
static void __exit
pcf8575_exit(void) {
/*
* Unregister /dev/rtc character device major number
*/
device_destroy(pcf8575_class, MKDEV(PCF8575_LCD_MAJOR,0));
class_destroy(pcf8575_class);
unregister_chrdev(PCF8575_LCD_MAJOR, "pcf8575_lcd");
/*
* Unregister this driver with I2C subsystem
*/
i2c_del_driver(&pcf8575_driver);
/*
* Free I2C client structure memory
*/
kfree(pcf8575_client);
}
MODULE_AUTHOR("Dan Dodge <ddodge@quintechelectronics.com>");
MODULE_DESCRIPTION("pcf8575 io expander");
MODULE_LICENSE("GPL");
module_init(pcf8575_init);
module_exit(pcf8575_exit);