linux驅動開發:24cxx設備的操作一

24cxx驅動編程.
1.首先實例化一個i2c設備.
2.編寫對應的i2c設備驅動.
3.通過match規則匹配設備和驅動。對應驅動會調用自己的probe函數完成初始化
4.i2c設備的操作實現,write和read.

1)如何實例化一個i2c設備
在\Documentation\i2c\instantiating-devices文件中介紹了4種方法
實例化一個IIC設備
Method 1: Declare the I2C devices by bus number
Method 2: Instantiate the devices explicitly
Method 3: Probe an I2C bus for certain devices
Method 4: Instantiate from user-space

我們使用第一種方式:

Example (from omap2 h4):

static struct i2c_board_info __initdata h4_i2c_board_info[] = {
    {
        I2C_BOARD_INFO("isp1301_omap", 0x2d),
        .irq        = OMAP_GPIO_IRQ(125),
    },
    {   /* EEPROM on mainboard */
        I2C_BOARD_INFO("24c01", 0x52),
        .platform_data  = &m24c01,
    },
    {   /* EEPROM on cpu card */
        I2C_BOARD_INFO("24c01", 0x57),
        .platform_data  = &m24c01,
    },
};

static void __init omap_h4_init(void)
{
    (...)
    i2c_register_board_info(1, h4_i2c_board_info,
            ARRAY_SIZE(h4_i2c_board_info));
    (...)
}

I2C_BOARD_INFO(dev_type, dev_addr) 用來初始化 i2c_board_info結構,填充 type類型和 dev_addr地址.
根據線路圖連接,確定 dev addr.
這裏寫圖片描述
實際板子上是24c08.確認address:0x50.
這裏需要說明一下,地址計算不算最後一個bit。也就是讀寫位,當發送讀cmd時,最後一位置1,當發送寫cmd時,最後一位置0.也就是說1010 000 x.高7bit代表地址,則1010000->0x50.
內核在發送read時,會 addr =0x50<<1 |1;當write 時,會 addr=0x50<<1 |0;

static struct i2c_board_info smdkv210_i2c_devs0[] __initdata = {
    { I2C_BOARD_INFO("24c08", 0x50), },
};
    i2c_register_board_info(0, smdkv210_i2c_devs0,
            ARRAY_SIZE(smdkv210_i2c_devs0));

i2c_register_board_info(int busnum, struct i2c_board_info const *info,unsigned n)

s5pv210有3組iic總線,我們的連接到iic0上。所以這邊的0是有意義的.

只要這樣,我們便可以添加一個新的i2c dev到 iic bus上.

當然內核中肯定有24cxx的驅動,我們可以直接拿來用。注意下match規格即可.當然也可以自己寫這個驅動.

IIC總線的匹配規則:

這裏寫圖片描述

IIC 驅動註冊流程:

//驅動註冊流程
i2c_add_driver
{
    --->i2c_register_driver
    {
        --->driver_register
        {
            --->bus_add_driver
            {
                --->driver_attach
                {
                    --->bus_for_each_dev
                    {
                        --->__driver_attach
                        {
                            --->driver_match_device
                            //when match ok,then go the next
                            --->driver_probe_device
                            {
                                --->really_probe
                                {
                                    --->dev->bus->probe(dev)//調用probe函數
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

i2c driver 編寫:

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

#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <mach/gpio.h>
#include <linux/gpio.h>
#include <asm/io.h>
#include <linux/irq.h>
#include <asm/bitops.h>




/*const*/

const struct i2c_device_id at24cxx_id[] =
{
    {"24c08",0},
    {}
};
//device number

static dev_t at_dev_num;

struct at24cxx_dev
{
    struct cdev at_dev;//char device 
    struct i2c_client * at_client;
};

static struct at24cxx_dev *at24cxx_struct = NULL;

//auto set up the device node file use
static struct class *at_class =NULL;
static struct device *at_device =NULL;

/*****ssize_t (*read) (struct file *, char __user *, size_t, loff_t *)*****/
static ssize_t at24cxx_read(struct file *filp, char __user *buf, size_t size, loff_t *offset)
{
    unsigned char addr;
    unsigned char data;
    int ret=0;
    struct i2c_msg msgs[2];

    ret =copy_from_user(&addr,buf,1);

    msgs[0].addr    = at24cxx_struct->at_client->addr;
    msgs[0].flags   = 0;
    msgs[0].buf     = &addr;
    msgs[0].len     = 1;

    msgs[1].addr    = at24cxx_struct->at_client->addr;
    msgs[1].flags   = 1;
    msgs[1].buf     = &data;
    msgs[1].len     = 1;

    i2c_transfer(at24cxx_struct->at_client->i2c_adapter,msgs,ARRAY_SIZE(msgs));

    ret =copy_to_user(buf,&data,1);

}

/*****ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *)*****/
static ssize_t at24cxx_write(struct file *filp, const char __user *buf, size_t size, loff_t *offset)
{
    unsigned char kbuf[2];
    int ret=0;
    struct i2c_msg msgs[1];

    if(size != 2)
        return -EINVAL;

    ret = copy_from_user(kbuf,buf,size);

    //slave dev addr
    msgs[0].addr =at24cxx_struct->at_client->addr;
    //write
    msgs[0].flags=0;

    msgs[0].buf =kbuf;

    msgs[0].len =2;


    i2c_transfer(at24cxx_struct->at_client->i2c_adapter,msgs,1);


    return size;
}

static struct file_operations at24cxx_fops =
{
    .owner  =THIS_MODULE,
    .read   =at24cxx_read,
    .write  =at24cxx_write,
};

/*****int (*probe)(struct i2c_client *, const struct i2c_device_id *)*****/

static int at24cxx_probe(struct i2c_client *uc_i2c_client, const struct i2c_device_id * uc_i2c_id_table)
{

    /* alloc the dev number*/
    alloc_chrdev_region(&at_dev_num,0,unsigned 1,"at24c02")
    /* get the buf for at24cxx_struct */
    at24cxx_struct = kzalloc(sizeof(struct at24cxx_dev),GFP_KERNEL);

    at24cxx_struct->at_client = uc_i2c_client;


    /* initial cdev struct,add cdev to kernel */
    cdev_init(&(at24cxx_struct->at_dev),&at24cxx_fops);

    cdev_add(&(at24cxx_struct->at_dev),at_dev_num,1);

    /*create device node file use*/
    at_class =class_create(THIS_MODULE,"at24cxx");
    at_device =device_create(at_class,NULL,at_dev_num,NULL,"at24cxx");

    return 0;
}
/*****int (*remove)(struct i2c_client *)*****/
static int at24cxx_remove(struct i2c_client *uc_i2c_client)
{
    device_destroy(at_class,at_dev_num);

    class_destroy(at_class);

    cdev_del(&(at24cxx_struct->at_dev));

    kfree(at24cxx_struct);

    unregister_chrdev_region(at_dev_num,1);

    return 0;
}
static struct i2c_driver at24cxx_drv =
{
    .driver =
    {
        .name ="at24cxx",   
        .owner= THIS_MODULE,
    },
    .probe  = at24cxx_probe,
    .remove = at24cxx_remove,
    //match use
    .id_table = at24cxx_id,
};

int __init  at24cxx_init(void)
{

    i2c_add_driver(&at24cxx_drv);

    return 0;
}

void __exit at24cxx_exit(void)
{

    i2c_del_driver(&at24cxx_drv);
}

module_init(at24cxx_init);
module_exit(at24cxx_exit);
MODULE_LICENSE("GPL");

這裏寫圖片描述
加載模塊.
這裏寫圖片描述
0-0050:這裏就是我們添加的設備.
at24cxx: 這裏就是我們添加的驅動.
這裏寫圖片描述
dev下面有我們創建的設備
測試程序:
這裏寫圖片描述

測試程序:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>


void print_usage(char *file)
{
    printf("%s w addr val \n",file);
    printf("%s r addr \n",file);
}
//write: a.out w 10 0x55
//read: a.out r  10
int main(int argc ,char ** argv)
{
    int fd;
    unsigned char buf[2];

    if(argc !=3 && argc !=4)
    {
        print_usage(argv[0]);   
        return -1;
    }
    fd =open("/dev/at24cxx",O_RDWR);

    if(strcmp(argv[1],"r") == 0)
    {
        buf[0] =strtoul(argv[2],NULL,0);
        read(fd,buf,1);
        printf("data:%d,0x%2x\n",buf[0],buf[0]);

    }
    else if(strcmp(argv[1],"w") == 0)
    {
        buf[0] =strtoul(argv[2],NULL,0);    
        buf[1] =strtoul(argv[3],NULL,0);
        write(fd,buf,2);
    }
    else
    {
        print_usage(argv[0]);   
        return -1;  
    }
    return 0;
}
發佈了37 篇原創文章 · 獲贊 5 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章