Pages

Sunday, April 15, 2012

Before I go to bed, I want to share a linux device driver I wrote sometime back for an old Zichuan ZCC210N I2C compass. Unfortunately, it stopped working before I could test the driver. I don't have the tools to debug the module, but the driver compiles and loads fine. Since, this was one of the early compass modules available in hobby stores, I thought someone out there might find this code useful.


#include 
#include 
#include 
#include 

#define ZCC210_I2C_ADDRESS   0x60
#define ZCC210_DIRECTION_COMMAND 0X77
#define ZCC210_DRIVER_NAME   "zcc210"

/* Addresses to scan */
static const unsigned short normal_i2c[] = {ZCC210_I2C_ADDRESS, I2C_CLIENT_END};

/*Device interface functions*/
static s32 zcc210_read_direction(struct i2c_client *client)
{
    /* Read direction bytes*/
    return i2c_smbus_read_word_data(client, ZCC210_DIRECTION_COMMAND);
}

/*sysfs callback functions*/
static ssize_t show_values(struct device *dev, struct device_attribute *attr, char *buf)
{
 struct i2c_client *client = to_i2c_client(dev);

    s32 result = zcc210_read_direction(client);

    if(result > 0){
        return sprintf(buf, "%d\n",
                (s32)swab32((u32)result));
    }
    return result;
}

static DEVICE_ATTR(values, S_IRUGO, show_values, NULL);

//static struct i2c_board_info zcc210_i2c_board_info = {  
//   I2C_BOARD_INFO(ZCC210_DRIVER_NAME, ZCC210_I2C_ADDRESS),      
// };

static struct attribute *zcc210_attributes[] = {
 &dev_attr_values.attr, 
 NULL
};
 
static const struct attribute_group zcc210_attr_group = {
.attrs = zcc210_attributes,
};

static const struct i2c_device_id zcc210_id[] = {
    { ZCC210_DRIVER_NAME, 0},
    {}
};

static int zcc210_detect(struct i2c_client *client, struct i2c_board_info *info)
{ 
 if (client->addr != ZCC210_I2C_ADDRESS)
 return -ENODEV;
  
 strlcpy(info->type, ZCC210_DRIVER_NAME, I2C_NAME_SIZE);
 return 0;
}

static int zcc210_probe(struct i2c_client *client, const struct i2c_device_id *id)
{  
 int err;
 
    printk(KERN_INFO "%s: new instance found!\n", ZCC210_DRIVER_NAME);

 /* Register sysfs hooks */
 err = sysfs_create_group(&client->dev.kobj, &zcc210_attr_group);
 if (err)
 {
  return err;
 }

 return 0;
}

static int zcc210_remove(struct i2c_client *client)
{
 sysfs_remove_group(&client->dev.kobj, &zcc210_attr_group); 
    return 0;
}

static struct i2c_driver zcc210_driver ={
    .driver.name = ZCC210_DRIVER_NAME,
    .id_table = zcc210_id,
    .probe = zcc210_probe,
    .remove = zcc210_remove,

 .class = I2C_CLASS_HWMON,
 .detect = zcc210_detect,
 .address_list = normal_i2c,
};

static int __init zcc210_init(void)
{
    printk(KERN_INFO "%s: init!", ZCC210_DRIVER_NAME ); 
 return i2c_add_driver(&zcc210_driver);
}

static void __exit zcc210_exit(void)
{
    printk(KERN_INFO "%s: exit!", ZCC210_DRIVER_NAME );
 i2c_del_driver(&zcc210_driver);
}

MODULE_AUTHOR("Karthik S Prakash <karthik.s.prakash@yahoo.com");
MODULE_DESCRIPTION("Zhichuan ZCC210 I2C Compass driver");
MODULE_LICENSE("GPL");

module_init(zcc210_init);
module_exit(zcc210_exit);


No comments:

Post a Comment