[Linux] I2C设备读写及文件节点创建

Linux Kernel Version:3.0.35

Platform:Freescale DSA2L

通过I2C读取VGA屏的EDID信息(主要是分辨率),解析后喂给CH7036芯片(LVDS转VGA,DVI,HDMI芯片),提供文件节点给User Space。

代码流程

由于EDID协议规定I2C的读取Slave地址为0x50,所以先添加Device注册代码:

i2c.h

#define I2C_BOARD_INFO(dev_type, dev_addr) \
    .type = dev_type, .addr = (dev_addr)

board-mx6q_dsa2l.c

static struct i2c_board_info mxc_i2c2_board_info[] __initdata = {
    {
        I2C_BOARD_INFO("mxc_edid_i2c", 0x50),
        .platform_data = NULL,
        .irq = DSA2L_VGA_CABLE_IN, //not irq just a gpio
    },
};
i2c_register_board_info(2, mxc_i2c2_board_info,
            ARRAY_SIZE(mxc_i2c2_board_info)); //注册i2c设备

编写驱动文件mxcfb_ch7036_edid.c

添加I2C驱动:

static const struct i2c_device_id mxc_edid_i2c_id[] = {
    { "mxc_edid_i2c", 0 },
    {},
};
MODULE_DEVICE_TABLE(i2c, mxc_edid_i2c_id);

static struct i2c_driver mxc_edid_i2c_driver = {
    .driver = {
           .name = "mxc_edid_i2c",
           },
    .probe = mxc_edid_i2c_probe,
    .remove = mxc_edid_i2c_remove,
    .id_table = mxc_edid_i2c_id,
};

static int __init mxc_edid_i2c_init(void)
{
    return i2c_add_driver(&mxc_edid_i2c_driver);
}

static void __exit mxc_edid_i2c_exit(void)
{
    i2c_del_driver(&mxc_edid_i2c_driver);
}

module_init(mxc_edid_i2c_init);
module_exit(mxc_edid_i2c_exit);

probe函数,创建了文件节点给用户空间读取:

static int __devinit mxc_edid_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id){
    int ret;
    edid_vga_client = client;

    if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE | I2C_FUNC_I2C))
        return -ENODEV;

    edid_all_buf = kmalloc(edid_buf_size, GFP_KERNEL);

    edid_vga_class = class_create(THIS_MODULE, "mxc_edid_class");
    edid_vga_dev = device_create(edid_vga_class, NULL, MKDEV(I2C_MAJOR, 50), NULL, "mxc_edid_dev");

    ret = device_create_file(edid_vga_dev, &dev_attr_all_register_value);
    if (ret < 0){
        dev_warn(&client->dev, "cound not create sys node for dev_attr_all_register_value\n");
    }
    ret = device_create_file(edid_vga_dev, &dev_attr_plug_state);
    if (ret < 0){
        dev_warn(&client->dev, "cound not create sys node for dev_attr_plug_state\n");
    }
    ret = device_create_file(edid_vga_dev, &dev_attr_timing);
    if (ret < 0){
        dev_warn(&client->dev, "cound not create sys node for dev_attr_timing\n");
    }

    return 0;
}

DEVICE_ATTR,列举一个:

static DEVICE_ATTR(all_register_value, S_IRUGO, mxc_edid_show_state, NULL);

读取文件节点对应的处理函数:

static ssize_t mxc_edid_show_state(struct device *dev, struct device_attribute *attr, char *buf)
{
    int ret, i;
    ret = mxc_edid_edidread(edid_vga_client);
    if (ret <= 0)
        return ret;

    for(i = 0; i < edid_buf_size; i++){
        if(i!=0 && i%10 == 0){
            printk("\n");
            printk("%02X ", edid_all_buf[i]);
        }else{
            printk("%02X ", edid_all_buf[i]);
        }
    }
    printk("\n");
    return strlen(buf);
}

通过I2C读取EDID:

static inline int edid_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num){
    int ret = 0;

    ret = gpio_get_value(edid_vga_client->irq); // 读取vga_detect pin,低电平表示接入vga
    if(!ret){
        ret = i2c_transfer(adap, msgs, num);
        if (ret != num) {
            return -EIO;
        }
    }
    return ret;
}

static int mxc_edid_edidread(struct i2c_client *client){
    int ret = 0;
    unsigned char offset = 0;
    struct i2c_adapter *adp = client->adapter;
    struct i2c_msg msg[2] = {
        {
        .addr    = edid_addr,
        .flags    = 0,
        .len    = 1,
        .buf    = &offset,
        }, {
        .addr    = edid_addr,
        .flags    = I2C_M_RD,
        .len    = edid_buf_size,
        .buf    = edid_all_buf,
        },
    };

    ret = edid_i2c_transfer(adp, msg, ARRAY_SIZE(msg));

    return ret;
}

知识细节

I2C时序图

所以前面 i2c_msg 数组定义有两个msg,先是写一个寄存器地址,然后是读取指定长度。

i2c_transfer 和 i2c_smbus_write_byte_data

smbus 是 i2c 的子集,是轻量级的i2c,最大传输速度比i2c慢,常见于电源管理。

追踪代码可以发现 i2c_smbus_write_byte_data 其实本质就是对 i2c_transfer 的封装,先不做深究。

关于/dev/i2c-x

用户空间通过操作/dev/i2c-x这些i2c适配器(比如ioctl),也可以对i2c设备进行操作。TODO:研究此部分

关于文件节点的操作

代码流程:

struct device *edid_vga_dev;
static struct class *edid_vga_class;

static DEVICE_ATTR(all_register_value, S_IRUGO, mxc_edid_show_state, NULL);

edid_vga_class = class_create(THIS_MODULE, "mxc_edid_class");
edid_vga_dev = device_create(edid_vga_class, NULL, MKDEV(I2C_MAJOR, 50), NULL, "mxc_edid_dev");

ret = device_create_file(edid_vga_dev, &dev_attr_all_register_value);

class_create:创建一个class,如“mxc_edid_class”,这样,会在对应的设备目录下创建class目录,比如:/sys/devices/virtual/mxc_edid_class

device_create:创建一个device,第一个参数:class,参数二:parent device,参数三,设备号,参数四:相关连的device,参数五:device name

device_create_file:创建sysfs attribute file

关于DEVICE_ATTR,device.h:

#define DEVICE_ATTR(_name, _mode, _show, _store) struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)

sysfs.h:

#define __ATTR(_name,_mode,_show,_store) { \
    .attr = {.name = __stringify(_name), .mode = _mode },        .show    = _show,                        .store    = _store,                    }

所以,第一个参数为名字,第二个参数是访问权限,第三个参数是用户空间 cat 时调用的函数,第四个参数是用户空间 echo 时调用的函数。

时间: 2024-11-08 23:05:01

[Linux] I2C设备读写及文件节点创建的相关文章

Linux I2C设备驱动编写(二)

在(一)中简述了Linux I2C子系统的三个主要成员i2c_adapter.i2c_driver.i2c_client.三者的关系也在上一节进行了描述.应该已经算是对Linux I2C子系统有了初步的了解.下面再对他们之间的关系进行代码层的深入分析,我认为对他们的关系了解的越好,越有助于I2C设备的驱动开发及调试. 带着问题去分析可能会更有帮助吧,通过对(一)的了解后,可能会产生以下的几点疑问: i2c_adapter驱动如何添加? i2c_client与i2c_board_info究竟是什么

Linux I2C设备驱动编写(一)

在Linux驱动中I2C系统中主要包含以下几个成员: I2C adapter 即I2C适配器 I2C driver 某个I2C设备的设备驱动,可以以driver理解. I2C client 某个I2C设备的设备声明,可以以device理解. I2C adapter 是CPU集成或外接的I2C适配器,用来控制各种I2C从设备,其驱动需要完成对适配器的完整描述,最主要的工作是需要完成i2c_algorithm结构体.这个结构体包含了此I2C控制器的数据传输具体实现,以及对外上报此设备所支持的功能类型

【转】Linux I2C设备驱动编写(二)

原文网址:http://www.cnblogs.com/biglucky/p/4059582.html 在(一)中简述了Linux I2C子系统的三个主要成员i2c_adapter.i2c_driver.i2c_client.三者的关系也在上一节进行了描述.应该已经算是对Linux I2C子系统有了初步的了解.下面再对他们之间的关系进行代码层的深入分析,我认为对他们的关系了解的越好,越有助于I2C设备的驱动开发及调试. 带着问题去分析可能会更有帮助吧,通过对(一)的了解后,可能会产生以下的几点疑

【转】Linux I2C设备驱动编写(三)-实例分析AM3359

原文网址:http://www.cnblogs.com/biglucky/p/4059586.html TI-AM3359 I2C适配器实例分析 I2C Spec简述 特性: 兼容飞利浦I2C 2.1版本规格 支持标准模式(100K bits/s)和快速模式(400K bits/s) 多路接收.发送模式 支持7bit.10bit设备地址模式 32字节FIFO缓冲区 可编程时钟发生器 双DMA通道,一条中断线 三个I2C模块实例I2C0\I2C1\I2C2 时钟信号能够达到最高48MHz,来自PR

Linux I2C设备驱动编写(三)-实例分析AM3359

TI-AM3359 I2C适配器实例分析 I2C Spec简述 特性: 兼容飞利浦I2C 2.1版本规格 支持标准模式(100K bits/s)和快速模式(400K bits/s) 多路接收.发送模式 支持7bit.10bit设备地址模式 32字节FIFO缓冲区 可编程时钟发生器 双DMA通道,一条中断线 三个I2C模块实例I2C0\I2C1\I2C2 时钟信号能够达到最高48MHz,来自PRCM 不支持 SCCB协议 高速模式(3.4MBPS) 管脚 管脚 类型 描述 I2Cx_SCL I/O

【转】Linux I2C设备驱动编写(一)

原文网址:http://www.cnblogs.com/biglucky/p/4059576.html 在Linux驱动中I2C系统中主要包含以下几个成员: I2C adapter 即I2C适配器 I2C driver 某个I2C设备的设备驱动,可以以driver理解. I2C client 某个I2C设备的设备声明,可以以device理解. I2C adapter 是CPU集成或外接的I2C适配器,用来控制各种I2C从设备,其驱动需要完成对适配器的完整描述,最主要的工作是需要完成i2c_alg

linux块设备读写流程

在学习块设备原理的时候,我最关系块设备的数据流程,从应用程序调用Read或者Write开始,数据在内核中到底是如何流通.处理的呢?然后又如何抵达具体的物理设备的呢?下面对一个带Cache功能的块设备数据流程进行分析. 1. 用户态程序通过open()打开指定的块设备,通过systemcall机制陷入内核,执行blkdev_open()函数,该函数注册到文件系统方法(file_operations)中的open上.在blkdev_open函数中调用bd_acquire()函数,bd_acquire

《Linux4.0设备驱动开发详解》笔记--第十五章:Linux I2C核心、总线与设备驱动

15.1 Linux I2C体系结构 I2C核心 I2C核心提供了I2C总线驱动和设备驱动的注册.注销的方法,I2C通信(Algorithm)方法上层的与具体适配器无关代码以及探测设备.检测设备地址的上层代码等 I2C总线驱动 是对I2C体系结构中适配器端的实现,适配器可由CPU控制,甚至可以直接集成在CPU内部 总线驱动包含I2C适配器数据结构i2c_adapter.I2C适配器的Algorithm数据结构i2c_algorithm和控制I2C适配器产生通信信号的函数 I2C设备驱动 它是对I

linux字符设备-自动创建设备号和设备节点

Linux字符设备-自动创建设备号和设备节点 先写一个自动分配字符设备号和设备节点的例子及APP 手动安装步骤: Insmod my_char_dev.ko 不需要再安装设备节点 然后是测试app ./my_char_dev_app 1 1 #include <linux/module.h> 2 #include <linux/init.h> 3 #include <linux/io.h> 4 #include <linux/fs.h> 5 #include