linux i2c 24c08 tiny6410 一个24c08的设备驱动

之前在做i2c测试的时候,都是利用内核源码/driver/i2c/i2c-dev.c这一份已经写好的设备驱动,它提供了我们所要使用的open,read,write,ioctl的接口函数。

这种测试的代码见:                http://blog.csdn.net/weiqing1981127/article/details/8010762

在文章的最后,前面对内核代码的分析也推荐大家看看。这种测试的方法就是要在用户程序定义和内核空间一样的i2c_rdwr_ioctl_data(见i2c-dev.c),不能自己随便来,显得很不方便。


以下是我自己写的一份I2C设备驱动

1.添加自己的i2c设备:打开内核源码文件arch/arm/mach-s3c64xx/mach-mini6410.c,添加如下代码,最后一个就是我自己添加的


然后还是在这个文件的mini6410_machine_init函数中增加如下代码

这是newstyle的添加i2c设备的方法,参考这个:    http://blog.csdn.net/lanmanck/article/details/7836734

接下来在重新编译下内核,在内核源码根目录下make zImage,会在arch/arm/boot下生成zImage文件,利用SDCARD或者其他方法,烧写该内核到板子上,之所以要进行这一步是因为板子上的内核没有你添加的那个board_info(即24c08_my),至于怎样烧写就不用我再详细说明了吧,能学到i2c的不至于不会吧。。。真不会的留言

2.第二步奉上源码和测试程序

24c08.c:

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

#define   MAJOR_24C08  155
unsigned int major = MAJOR_24C08;
static  struct  i2c_driver  at24c08_driver;
static  struct  i2c_adapter  *at24c08_adapter;
static  unsigned  short  addr;
static  struct  class  *at24c08_cls  =  NULL;
static unsigned char r_addr = 0;

ssize_t  at24c08_read  (struct  file  *filp,  char  __user  *buf,  size_t  sz,  loff_t  *off)
{
    struct i2c_msg msg[2];
    unsigned  char  args,  data[2];
    printk("[read]\n");
    printk("[addr:0x%x]\n",addr);
    // if(copy_from_user((void  *)&args,  buf,  1))//这部分语句已经没用了,下面我将用别的方法把读地址
        // return -EFAULT;                           发回用户空间
    args = 0x01;
    /* 先传读地址 */
    msg[0].addr    =  addr;
    msg[0].buf    =  &r_addr; //原来的语句是这样msg[0].buf = &args;
    msg[0].len    =  1;
    msg[0].flags    =  0;
    // printk("read_addr=%x,",*buf);  //buf被清零了,所以打印出来的read_addr是0
    printk("read_addr=%x,",r_addr);
    /* 再 读 */
    msg[1].addr    =  addr;
    msg[1].buf    =  &data[0];
    msg[1].len    =  1;
    msg[1].flags    =  1;        /* 读 */
    if  (2  ==  i2c_transfer(at24c08_adapter,  msg,  2))  {
        /* 读成功 */
        printk("read_value=%x\n",data[0]);
        data[1] = r_addr;
        if(copy_to_user((void  *)buf,  &data,  sz))
            return -EFAULT;
        return  1;
    }
    else
        return  -EIO;
}

ssize_t  at24c08_write  (struct  file  *filp,  const  char  __user  *buf,  size_t  sz,  loff_t  *off)
{
    struct i2c_msg msg;
    unsigned  char  args[2];
    printk("[write]\n");
    printk("[addr:0x%x]\n",addr);
    if(copy_from_user((void  *)&args,  buf,  sz))
        return -EFAULT;
    printk("write_addr=%x,write_value=%x\n",args[0],args[1]);
    //以下一步非常重要,它将提供读地址给read函数,根据我的理解,默认情况下,虽然用户空间的read函数
    //传进来了读地址buf,不过它会在内核空间的read函数被清零,这就导致读地址一直是0不是我们需要读
    //的地址
    r_addr = args[0];
    /* args[0] = addr, args[1] = val */
    msg.addr  =  addr;
    msg.buf    =  args;
    msg.len    =  sz;
    msg.flags  =  0;        /* 写 */
    if(1  ==  i2c_transfer(at24c08_adapter,&msg,  1))
        return  2;
    else
        return  -EIO;
   
}
static  struct  file_operations  at24c08_fops  =  {
    .owner  =  THIS_MODULE,
    .read    =  at24c08_read,
    .write  =  at24c08_write,
};

static  struct  cdev  cdev;
static  int  at24c08_probe(struct  i2c_client  *client,  const  struct  i2c_device_id  *dev_id)
{
    dev_t devno = MKDEV(major,0);
    printk("[probe]\n");
    printk("[client->addr:0x%x]\n",client->addr);
    if(major)
        register_chrdev_region(devno,  1,  "at24c08");
    else{
        alloc_chrdev_region(&devno,0,1,"at24c08");
        major = MAJOR(devno);
    }
    cdev_init(&cdev,  &at24c08_fops);
    cdev_add(&cdev,  MKDEV(major,  0),  1);
    at24c08_cls = class_create(THIS_MODULE,"at24c08_cls");
    device_create(at24c08_cls,  NULL,  MKDEV(major,  0),  NULL,  "24c08_dev");
    at24c08_adapter  =  client->adapter;
    addr = client->addr;  //这2步至关重要,read,write的msg.addr要靠这个赋值
    return    0;
}

static  int  at24c08_remove(struct  i2c_client  *client)
{
    printk("[remove]\n");
    device_destroy(at24c08_cls,  MKDEV(major,  0));
    class_destroy(at24c08_cls);
    cdev_del(&cdev);
    unregister_chrdev_region(MKDEV(major,  0),  1);
   
    return    0;
}

static  const  struct  i2c_device_id  at24c08_id[]  =  {
    {"24c08_my",  0},
    {}
};
MODULE_DEVICE_TABLE(i2c,at24c08_id);

static  struct  i2c_driver  at24c08_driver=  {
    .probe    =  at24c08_probe,
    .remove    =  at24c08_remove,
    .driver  =  {
        .name  =  "24c08_driver",
    },
    .id_table    =  at24c08_id,
};

static  int  at24c08_init(void)
{
    printk("[init]\n");
    i2c_add_driver(&at24c08_driver);
    return  0;
}

static  void  at24c08_exit(void)
{
    printk("[exit]\n");
    i2c_del_driver(&at24c08_driver);
    return  ;
}

module_init(at24c08_init);
module_exit(at24c08_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ZHYANG");


代码看起来似乎比较长,但其实不是的,大家可以把那些我在调试的时候用到的打印信息删掉看起来就舒服多了,read,write函数中的注释最好看下,比较重要,原因是read函数中从用户程序传过来的buf会被清零掉,这个buf原本是存放我们要读的地址。

makefile:

obj-m = 24c08.o

KDIR := /opt/FriendlyARM/mini6410/linux/linux-2.6.38/

all:
    make -C $(KDIR) SUBDIRS=$(shell pwd) modules

clean:
    rm -f *.o *.ko *.mod.* *.symvers *.order


测试程序test_24c08_my.c:

#include  <stdio.h>
#include  <fcntl.h>
#include  <stdlib.h>

int  main(int  argc,  char  *argv[])
{
    int  fd;
    unsigned  char  dat[2];
   
    fd  =  open("/dev/24c08_dev",  O_RDWR);
    if  (-1  ==  fd)  {
       
        printf("failed to open device.\n");
        return  -1;
    }
         
        dat[0]  =  0x11;   //要写入的地址
        dat[1]  =  0x64; //写入的值
        write(fd,  dat,  2);
        sleep(1);
        // dat[0]  =  0x11;  //这一步其实没意义了,读地址在内核空间会被传递
        // dat[1]  =  dat[0]; //内核空间也会被读地址发过来,用户空间不用再指定读地址
        dat[0] = dat[1] = 0;   //清零
        read(fd,  dat,2);
        printf("read address 0x%2x:  %x\n",dat[1],dat[0]);
    return  0;
}


有不懂的问题,或者有什么错误指出,欢迎大家在评论中指教!谢谢!

猜你喜欢

转载自blog.csdn.net/u011068616/article/details/45892971