1

I have read the Linux documentation on writing I2C clients (writing-clients) and have used this blog link http://renjucnair.blogspot.in/2012/01/writing-i2c-driver.html and other resources I could find. I am trying to access the device using the SM-Bus interface and accessing the device file using the file operations used for standard character drivers as shown by the third type in the answer to this I2C device linux driver question. I am trying to communicate with a magnetometer HMC5883l. I have written the following code finally:

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/version.h>
#include <linux/types.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>


#define REG_CONF_A 0x00 
#define REG_CONF_B 0x01
#define REG_MODE 0x02
#define REG_FIELD_X_LSB 0x03
#define REG_FIELD_X_MSB 0x04
#define REG_FIELD_Y_LSB 0x05
#define REG_FIELD_Y_MSB 0x06
#define REG_FIELD_Z_LSB 0x07
#define REG_FIELD_Z_MSB 0x08
#define REG_STATUS 0x09
#define REG_ID_A 0x0A
#define REG_ID_B 0x0B
#define REG_ID_C 0x0C

static char c = 's';

static dev_t first;
static struct cdev c_dev; 
static struct class *cl;
static struct i2c_client *client;

static const struct i2c_device_id hmc5883l_id[] = {
    { "hmc5883l", 0 },
    { }
};

MODULE_DEVICE_TABLE(i2c, hmc5883l_id);
 
static const struct of_device_id hmc5883l_of_match[] = {
    { .compatible = "honeywell,hmc5883l", .data = 0 },
    {}
};
MODULE_DEVICE_TABLE(of, hmc5883l_of_match);

static struct i2c_driver hmc5883l_driver = {
    .driver = {
    .owner      = THIS_MODULE,
    .name       = "hmc5883l",
    .of_match_table = hmc5883l_of_match,
    },
    .id_table   = hmc5883l_id
};

static int hmc5883l_open(struct inode *i, struct file *f)
{
    printk(KERN_INFO "Driver: open()\n");
    return 0;
}
static int hmc5883l_release(struct inode *i, struct file *f)
{
    printk(KERN_INFO "Driver: close()\n");
    return 0;
}

static ssize_t hmc5883l_read(struct file *f, char __user *buf, size_t len, loff_t *off)
{

    struct i2c_adapter *my_adap = i2c_get_adapter(0); 
    client = i2c_new_dummy(my_adap, 0x1e);
        
    printk(KERN_INFO "Driver: read()\n");
    if (*off == 0) 
    {
    i2c_smbus_write_byte_data(client, 0x0a, 0);
    c = i2c_smbus_read_byte(client);
    copy_to_user(buf,&c,1); 
    (*off)++;
    return 1;   
    }
    else  
    return 0;
}

static const struct file_operations hmc5883l_fops = {
    .owner      = THIS_MODULE,
    .read       = hmc5883l_read,
    .open       = hmc5883l_open,
    .release    = hmc5883l_release,
};


static int __init hmc5883l_init(void)
{
    int ret;
    struct device *dev_ret;

    printk(KERN_INFO "Namaskar: mmeter driver registered");
    if ((ret = alloc_chrdev_region(&first, 0, 1, "Shweta")) < 0)
    {
        return ret;
    }
    if (IS_ERR(cl = class_create(THIS_MODULE, "chardrv")))
    {
        unregister_chrdev_region(first, 1);
        return PTR_ERR(cl);
    }
    if (IS_ERR(dev_ret = device_create(cl, NULL, first, NULL, "mynull")))
    {
        class_destroy(cl);
        unregister_chrdev_region(first, 1);
        return PTR_ERR(dev_ret);
    }

    cdev_init(&c_dev, &hmc5883l_fops); //this is the step of initializing the character driver.
    
    if ((ret = cdev_add(&c_dev, first, 1)) < 0)
    {
        device_destroy(cl, first);
        class_destroy(cl);
        unregister_chrdev_region(first, 1);
        return ret;
    }
    
    return i2c_add_driver(&hmc5883l_driver);
}

static void __exit hmc5883l_exit(void)
{
    i2c_del_driver(&hmc5883l_driver);
    cdev_del(&c_dev);
    device_destroy(cl, first);
    class_destroy(cl);
    unregister_chrdev_region(first, 1);
    printk(KERN_INFO "Alvida: mmeter driver unregistered");
    
}


module_init(hmc5883l_init);
module_exit(hmc5883l_exit);

MODULE_AUTHOR("Shubham Sharma");
MODULE_DESCRIPTION("Mmeter Driver");
MODULE_LICENSE("GPL");

I have also made the requisite additions in the device tree as below:

/dts-v1/;
/include/ "system-conf.dtsi"
/ {
    amba: amba {
        compatible = "simple-bus";
        #address-cells = <1>;
        #size-cells = <1>;
        interrupt-parent = <&intc>;
        ranges;
i2c0: i2c@e0004000 {
            compatible = "cdns,i2c-r1p10";
            status = "okay";
            clocks = <&clkc 38>;
            interrupt-parent = <&intc>;
            interrupts = <0 25 4>;
            reg = <0xe0004000 0x1000>;
            #address-cells = <1>;
            #size-cells = <0>;
        
hmc5883l@1e {
        compatible = "honeywell,hmc5883l";
        reg = <0x1e>;
};
};
};

};

This code compiles on my laptop and following are the outputs that I get upon running the following commands when the driver is installed on petalinux on the Zedboard.

root@I2C:~# i2cdetect -r -y 0
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- 1e -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- --



root@I2C:/dev# cat mynull
Driver: open()
i2c i2c-0: Failed to register i2c client dummy at 0x1e (-16)
Driver: read()
Unable to handle kernel NULL pointer dereference at virtual address 00000002
pgd = 5d898000
[00000002] *pgd=1e4aa831, *pte=00000000, *ppte=00000000
Internal error: Oops - BUG: 17 [#1] PREEMPT SMP ARM
Modules linked in: ipv6 mmeter_driver(O)
CPU: 1 PID: 982 Comm: cat Tainted: G           O   3.19.0-xilinx #19
Hardware name: Xilinx Zynq Platform
task: 5e46f180 ti: 5e4f2000 task.ti: 5e4f2000
PC is at i2c_smbus_write_byte_data+0x8/0x3c
LR is at hmc5883l_read+0x48/0xb4 [mmeter_driver]
pc : [<40332068>]    lr : [<3f000078>]    psr: 600d0013
sp : 5e4f3f10  ip : 00000000  fp : 00000001
r10: 00000000  r9 : 5e4f2000  r8 : 4000dee4
r7 : 3ec43c60  r6 : 3f000628  r5 : 3ec43c60  r4 : 5e4f3f88
r3 : 00000000  r2 : 00000000  r1 : 0000000a  r0 : 00000000
Flags: nZCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment user
Control: 18c5387d  Table: 1d89804a  DAC: 00000015
Process cat (pid: 982, stack limit = 0x5e4f2238)
Stack: (0x5e4f3f10 to 0x5e4f4000)
3f00:                                     5e4f2000 00000000 00000001 4004f0cc
3f20: 3f0003c9 5e4f3f44 00000000 4046fa08 3f0003c9 5e4f3f44 5e4f3f88 3f00005c
3f40: 3f0003c9 5ebef000 0000000a 3f000078 3ec43c60 5e65be80 5e4f3f88 400b9fe4
3f60: 5e65be80 3ec43c60 00001000 5e65be80 5e65be80 00001000 3ec43c60 4000dee4
3f80: 5e4f2000 400ba08c 00000000 00000000 00001000 000aba18 00001000 3ec43c60
3fa0: 00000003 4000dd60 000aba18 00001000 00000003 3ec43c60 00001000 01000000
3fc0: 000aba18 00001000 3ec43c60 00000003 00000003 00000001 00000000 00000001
3fe0: 00000000 3ec43c44 00011a68 36e8125c 600d0010 00000003 00000000 00000000
[<40332068>] (i2c_smbus_write_byte_data) from [<3f000078>] (hmc5883l_read+0x48/0xb4 [mmeter_driver])
[<3f000078>] (hmc5883l_read [mmeter_driver]) from [<400b9fe4>] (vfs_read+0x84/0xec)
[<400b9fe4>] (vfs_read) from [<400ba08c>] (SyS_read+0x40/0x80)
[<400ba08c>] (SyS_read) from [<4000dd60>] (ret_fast_syscall+0x0/0x34)
Code: e28dd03c e49df004 e52de004 e24dd03c (e1d0c0b2) 
---[ end trace 7a4ba311ad6063f0 ]---
Driver: close()
Segmentation fault

I am unable to understand the error that I am making. Any help will be highly appreciated. Thanks.

P.S. : This is my first question here, so if the question is wrong in the way I posted I am sorry and I will provide anymore details.

Community
  • 1
  • 1

0 Answers0