5

目前我正在为 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);
4

1 回答 1

0

此代码是否在内核空间中运行并且您是否已固定内存映射地址?这一行Unable to handle kernel paging request at virtual address 000b9a81似乎表明您正在尝试直接访问此内存位置(虚拟内存不会喜欢)。您需要将此地址固定在内存中或将地址移动到已固定的地址空间(通常在内存中较高)。

于 2011-05-28T20:29:05.313 回答