В настоящее время я разрабатываю два драйвера для встроенного приложения Arm 9. Оба они являются драйверами i2c, каждый из которых использует расширитель ввода-вывода 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);