写设备驱动:
四部曲:
  1. 构建i2c_driver
  2. 注册i2c_driver
  3. 构建i2c_client ( 第一种方法:注册字符设备驱动、第二种方法:通过板文件的i2c_board_info填充,然后注册)
  4. 注销i2c_driver

具体如下:

●    构建i2c_driver

static struct i2c_driver pca953x_driver = {
                .driver = {
                                    .name= "pca953x", //名称
                                },
                .id= ID_PCA9555,//id号
                .attach_adapter= pca953x_attach_adapter, //调用适配器连接设备
                .detach_client= pca953x_detach_client,//让设备脱离适配器
        };

●    注册i2c_driver

static int __init pca953x_init(void)
        {
                return i2c_add_driver(&pca953x_driver);
        }
        module_init(pca953x_init);
 
执行i2c_add_driver(&pca953x_driver)后,如果内核中已经注册了i2c适配器,则顺序调用这些适配器来连接我们的i2c设备。此过程是通过调用i2c_driver中的attach_adapter方法完成的。具体实现形式如下:
 
static int pca953x_attach_adapter(struct i2c_adapter *adapter)
        {
                return i2c_probe(adapter, &addr_data, pca953x_detect);
                /*
                adapter:适配器
                addr_data:地址信息
                pca953x_detect:探测到设备后调用的函数
                */
        }
 
地址信息addr_data是由下面代码指定的。
        /* Addresses to scan */
        static unsigned short normal_i2c[] = {0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,I2C_CLIENT_END};
        I2C_CLIENT_INSMOD;
 
注意:normal_i2c里的地址必须是你i2c芯片的地址。否则将无法正确探测到设备。而I2C_ CLIENT_INSMOD是一个宏,它会利用normal_i2c构建addr_data。

●    构建i2c_client,并注册字符设备驱动

i2c_probe在探测到目标设备后,后调用pca953x_detect,并把当时的探测地址address作为参数传入。

static int pca953x_detect(struct i2c_adapter *adapter, int address, int kind)
        {
                struct i2c_client *new_client;
                struct pca953x_chip *chip; //设备结构体
                int err = 0,result;
                dev_t pca953x_dev=MKDEV(pca953x_major,0);//构建设备号,根据具体情况设定,这里我只考虑了normal_i2c中只有一个地址匹配的情况。主次设备号来源
                if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA| I2C_FUNC_SMBUS_WORD_DATA))//判定适配器能力
                goto exit;
                if (!(chip = kzalloc(sizeof(struct pca953x_chip), GFP_KERNEL))) {
                        err = -ENOMEM;
                        goto exit;
                }
                /****构建i2c-client****/
                chip->client=kzalloc(sizeof(struct i2c_client),GFP_KERNEL);
                new_client = chip->client;
                i2c_set_clientdata(new_client, chip);
                new_client->addr = address;
                new_client->adapter = adapter;
                new_client->driver = &pca953x_driver;
                new_client->flags = 0;
                strlcpy(new_client->name, "pca953x", I2C_NAME_SIZE);
                if ((err = i2c_attach_client(new_client)))//注册i2c_client
                goto exit_kfree;
                if (err)
                goto exit_detach;
                if(pca953x_major)
                {
                        result=register_chrdev_region(pca953x_dev,1,"pca953x");
                }
                else{
                        result=alloc_chrdev_region(&pca953x_dev,0,1,"pca953x");
                        pca953x_major=MAJOR(pca953x_dev);
                }
                if (result < 0) {
                        printk(KERN_NOTICE "Unable to get pca953x region, error %d/n", result);
                        return result;
                }
                pca953x_setup_cdev(chip,0); //注册字符设备,此处不详解
                return 0;
                exit_detach:
                i2c_detach_client(new_client);
        exit_kfree:
                kfree(chip);
        exit:
                return err;
        }

  

i2c_check_functionality用来判定设配器的能力,这一点非常重要。你也可以直接查看对应设配器的能力,如

static const struct i2c_algorithm smbus_algorithm = {
                .smbus_xfer= i801_access,
                .functionality= i801_func,
        };
        static u32 i801_func(struct i2c_adapter *adapter)
        {
                        return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
                               I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
                               I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK|(isich4 ? I2C_FUNC_SMBUS_HWPEC_CALC : 0);
        }

  

字符驱动的具体实现

struct file_operations pca953x_fops = {
                .owner = THIS_MODULE,
                .ioctl= pca953x_ioctl,
                .open= pca953x_open,
                .release =pca953x_release,
        };

  

字符设备驱动本身没有什么好说的,这里主要想说一下,如何在驱动中调用i2c设配器帮我们完成数据传输。

目前设配器主要支持两种传输方法:smbus_xfer和master_xfer。一般来说,如果设配器支持了master_xfer那么它也可以模拟支持smbus的传输。但如果只实现smbus_xfer,则不支持一些i2c的传输。

int (*master_xfer)(struct i2c_adapter *adap,struct i2c_msg *msgs,int num);
int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data * data);

master_xfer中的参数设置,和前面的用户空间编程一致。现在只是要在驱动中构建相关的参数然后调用i2c_transfer来完成传输既可。

int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num)

smbus_xfer中的参数设置及调用方法如下:

static int pca953x_write_reg(struct pca953x_chip *chip, int reg, uint16_t val)
        {
                int ret;
                ret = i2c_smbus_write_word_data(chip->client, reg << 1, val);
                if (ret < 0) {
                                dev_err(&chip->client->dev, "failed writing register/n");
                                        return -EIO;
                                }
                return 0;
        }

  

上面函数完成向芯片的地址为reg的寄存器写一个16bit的数据。i2c_smbus_write_word_data的实现如下:

s32 i2c_smbus_write_word_data(struct i2c_client *client, u8 command, u16 value)
        {
                union i2c_smbus_data data;
                data.word = value;
                return i2c_smbus_xfer(client->adapter,client->addr,client->flags,
                 I2C_SMBUS_WRITE,command,I2C_SMBUS_WORD_DATA,&data);
        }

  

从中可以看出smbus传输一个16位数据的方法。其它操作如:字符写、字符读、字读、块操作等,可以参考内核的i2c-core.c中提供的方法。

注释:i2c_client 信息通常在BSP的板文件中通过i2c_board_info 填充,如:
定义一个I2C设备ID为“ad7142_joystick”、地址为0x2C、中断号为IRQ_PF5的i2c_client
 
static struct i2c_board_info __initdata xxx_i2c_board_info[] = {
    {
        I2C_BOARD_INFO(“ad7142_joystick”,0x2C),
        .irq = IRQ_PF5,
    },
.........
};
然后注册
i2c_register_board_info(1, i2c_devs1, ARRAY_SIZE(i2c_devs1));
通过这个就完成了i2c_client 的注册
 

●    注销i2c_driver

static void __exit pca953x_exit(void)
        {
                i2c_del_driver(&pca953x_driver);
        }
        module_exit(pca953x_exit);

  

顺序调用内核中注册的适配器来断开我们注册过的i2c设备。此过程通过调用i2c_driver中的attach_adapter方法完成的。具体实现形式如下:

static int pca953x_detach_client(struct i2c_client *client)
        {
                int err;
                struct pca953x_chip *data;
                if ((err = i2c_detach_client(client)))//断开i2c_client
                return err;
                data=i2c_get_clientdata(client);
                cdev_del(&(data->cdev));
                unregister_chrdev_region(MKDEV(pca953x_major, 0), 1);
                kfree(data->client);
                kfree(data);
                return 0;
        }

  

其实主芯片的i2c的驱动基本上都支持啦,哈哈,所以剩下的工作量不是很大,只需完成从芯片的i2c的驱动操作就ok啦,那个只是分析如何编写的便于深入理解。

另外:

几个重要的结构体:i2c_msg(设置设备地址的)、i2c_client(从机设备的地址,一般采用平台设备的形式,用probe函数探测)、i2c_driver自己构建
几个重要的方法:i2c_add_driver添加设备、i2c_transfer用于进行I2C适配器和I2C设备之间的一组消息的交互
 
I2C与SCCB协议区别:从机地址因为I2C是7位地址,最后一位是读写位,而SCCB是8位地址,比如ov9650,他是SCCB协议,他的地址是0x60,那么如果挂接到I2C总线上,他的地址就变成0x30了,这样算的:
SCCB地址:::  0x60:   0 1 1 0_0 0 0 0     这个0还是地址位
I2C地址::::                 0 1 1 0_0 0 0 0最后红色的0是读写位,那么地址变成了7 位 +读写位 即 0 1 1_ 0 0 0 0 +0( 读写位 )  所以从机地址变成了0x30  
 
 
 
linux内核I2C驱动基本上就这些了!

linux内核I2C子系统学习(三)的更多相关文章

  1. 嵌入式Linux内核I2C子系统详解

    1.1 I2C总线知识 1.1.1  I2C总线物理拓扑结构     I2C总线在物理连接上非常简单,分别由SDA(串行数据线)和SCL(串行时钟线)及上拉电阻组成.通信原理是通过对SCL和SDA线高 ...

  2. 《Linux内核分析》第三周学习报告

    <Linux内核分析>第三周学习报告                                    ——构造一个简单的Linux系统MenuOS 姓名:王玮怡  学号:201351 ...

  3. 《Linux内核分析》第三周学习笔记

    <Linux内核分析>第三周学习笔记 构造一个简单的Linux系统MenuOS 郭垚 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.stud ...

  4. 浅谈 Linux 内核无线子系统

    浅谈 Linux 内核无线子系统 本文目录 1. 全局概览 2. 模块间接口 3. 数据路径与管理路径 4. 数据包是如何被发送? 5. 谈谈管理路径 6. 数据包又是如何被接收? 7. 总结一下 L ...

  5. Linux 内核无线子系统

    Linux 内核无线子系统 浅谈 Linux 内核无线子系统 Table of Contents 1. 全局概览 2. 模块间接口 3. 数据路径与管理路径 4. 数据包是如何被发送? 5. 谈谈管理 ...

  6. (转)浅谈 Linux 内核无线子系统

    前言 Linux 内核是如何实现无线网络接口呢?数据包是通过怎样的方式被发送和接收呢? 刚开始工作接触 Linux 无线网络时,我曾迷失在浩瀚的基础代码中,寻找具有介绍性的材料来回答如上面提到的那些高 ...

  7. Linux内核中影响tcp三次握手的一些协议配置

    在Linux的发行版本中,都存在一个/proc/目录,有的也称它为Proc文件系统.在 /proc 虚拟文件系统中存在一些可调节的内核参数.这个文件系统中的每个文件都表示一个或多个参数,它们可以通过 ...

  8. Linux内核分析第一次学习报告

    Linux内核分析第一次学习报告 学生 黎静 学习内容 1.存储程序计算机工作模型 冯诺依曼体系结构:核心思想为存储程序计算机. CPU抽象为for循环,总是执行下一条指令,内存保存指令和数据,CPU ...

  9. Linux内核初探 之 进程(三) —— 进程调度算法

    一.基本概念 抢占 Linux提供抢占式多任务,基于时间片和优先级对进程进行强制挂起 非抢占的系统需要进程自己让步(yielding) 进程类型 IO消耗型 经常处于可运行态,等待IO操作过程会阻塞 ...

随机推荐

  1. HDU 5525 Product 数论

    题意: 给出一个长度为\(n(1 \leq n \leq 10^5)\)的序列\(A_i\),\(N=\prod\limits_{i=1}^{n}i^{A_i}\).求\(N\)的所有约数的乘积. 分 ...

  2. django html render_to_response

    #coding=utf-8 from django.shortcuts import render from blog.models import BlogPost from django.short ...

  3. -bash: ./start.sh: /bin/sh^M: bad interpreter: No such file or directory 错误解决方案

    问题描述:sh文件中,在win环境下,用WinSCP编辑,出现如下错误: -bash: ./start.sh: /bin/sh^M: bad interpreter: No such file or ...

  4. 【Luogu】P3047附近的牛(树形DP)

    题目链接 树形DP,设f[i][j]是当前在i点,j步之内有多少牛.从相邻点to的f[to][j-1]转移而来,减去重复计算即可. #include<cstdio> #include< ...

  5. IDA Pro使用技巧及大杂烩

    IDA Pro使用技巧及大杂烩 IDA Pro基本简介 IDA加载完程序后,3个立即可见的窗口分别为IDA-View,Named,和消息输出窗口(output Window). IDA图形视图会有执行 ...

  6. [USACO11DEC] Grass Planting (树链剖分)

    题目描述 Farmer John has N barren pastures (2 <= N <= 100,000) connected by N-1 bidirectional road ...

  7. java面试题之如何实现处理线程的返回值?

    有三种实现方式: 主线程等待法: 使用Thread类的join方法阻塞当前线程以等待子线程处理完毕: 通过Callable接口实现,通过FutureTask 或者线程池:

  8. Linux System Programming 学习笔记(三) 标准缓冲I/O

    1. partial block operations are inefficient. The operating system has to “fix up” your I/O by ensuri ...

  9. 挑战程序设计2 KD树

    Range Query - Range Search (kD Tree) Time Limit : 1 sec, Memory Limit : 262144 KB Japanese version i ...

  10. 【CF732D】Exams(线性扫描,贪心,二分)

    题意:有m门需要过的课程,n天的时间可以选择复习.考试(如果的d[i]为0则只能复习),一门课至少要复习a[i]天才能通过(可以不连续的复习得到a[i]),问最早什么时候可以把所有课程都通过,如果不能 ...