在长沙阳光医院做网站编辑,wordpress自动化采集,网站获取访客手机号源码,搭建网站需要什么技术以下内容源于朱有鹏嵌入式课程的学习与整理#xff0c;如有侵权请告知删除。
一、前言 由I2C总线设备的驱动框架可知#xff0c;I2C总线设备的驱动框架涉及的文件如下#xff1a; #xff08;1#xff09;I2C设备驱动层相关的文件 x210开发板的电容触摸屏gslX680采用I2C接…以下内容源于朱有鹏嵌入式课程的学习与整理如有侵权请告知删除。
一、前言 由I2C总线设备的驱动框架可知I2C总线设备的驱动框架涉及的文件如下 1I2C设备驱动层相关的文件 x210开发板的电容触摸屏gslX680采用I2C接口因此以这个I2C设备为例进行分析。 其对应的驱动源代码文件是x210_kernel\drivers\input\touchscreen\gslX680.c文件。 它由触摸屏IC原厂工程师提供代码中涉及到很多触摸屏专业方面的知识但无需理会。 gslX680.c文件进行了I2C设备驱动的注册相应的I2C设备注册是在mach-x210.c文件中。 2I2C核心层相关的文件 I2C核心层相关的文件是i2c-core.c文件它由内核开发者提供和具体的硬件操作无关。 3I2C总线驱动层相关的文件 algos目录存放的是I2C通信的算法主要是时序等内容。 busses目录存放的是各种已经编写好的、将来要向i2c核心层注册的适配器文件。 我们主要分析drivers\i2c\busses\i2c-s3c2410.c文件。 下面将对I2C总线驱动层的文件进行分析主要分析drivers\i2c\busses\i2c-s3c2410.c。 二、I2C总线驱动层代码分析 I2C总线驱动是I2C适配器I2C控制器的软件实现提供I2C适配器与从设备的数据通信功能。 I2C总线驱动由 I2C适配器模块 和 I2C通信算法 来描述。 1、I2C适配器模块的注册 从下面代码位于i2c-s3c2410.c文件中可知adapter的注册实现为模块的方式因此可以进行动态的加载和卸载并且是基于platform平台总线的因为I2C控制器属于内部外设。。i2c-s3c2410.c文件提供的是平台驱动的注册平台设备的注册一般在板级文件即mach文件中实现比如mach-x210.c文件。 static struct platform_driver s3c24xx_i2c_driver {.probe s3c24xx_i2c_probe, //平台总线驱动的probe函数.remove s3c24xx_i2c_remove,.id_table s3c24xx_driver_ids,//平台总线驱动所支持的设备列表.driver {.owner THIS_MODULE,.name s3c-i2c, //平台总线驱动的名字与平台总线设备的名字相同.pm S3C24XX_DEV_PM_OPS,},
};static int __init i2c_adap_s3c_init(void)
{return platform_driver_register(s3c24xx_i2c_driver);
}
subsys_initcall(i2c_adap_s3c_init); 2、s3c24xx_i2c_probe函数分析 该函数内容如下主要的内容是 1填充一个i2c_adapter结构体并且调用接口去注册之。 2从platform_device接收硬件信息做必要的处理request_mem_region ioremap、request_irq等。 3对硬件做初始化直接操作210内部I2C控制器的寄存器。 static int s3c24xx_i2c_probe(struct platform_device *pdev)
{struct s3c24xx_i2c *i2c; // 次结构体是三星对本SoC中的i2c控制器的一个描述是一个用来在多文件中进行数据传递的全局结构体struct s3c2410_platform_i2c *pdata; // 此结构体用来表示平台设备的私有数据struct resource *res;int ret;pdata pdev-dev.platform_data; // 获取到平台设备层中的平台数据if (!pdata) {dev_err(pdev-dev, no platform data\n);return -EINVAL;}i2c kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL); // 给s3c24xx_i2c类型指针申请分配内存空间if (!i2c) {dev_err(pdev-dev, no memory for state\n);return -ENOMEM;}// 以下主要是对s3c24xx_i2c 结构体中的i2c_adapter变量的一个填充strlcpy(i2c-adap.name, s3c2410-i2c, sizeof(i2c-adap.name)); // 设置适配器的名字 s3c2410-i2ci2c-adap.owner THIS_MODULE;i2c-adap.algo s3c24xx_i2c_algorithm; // 通信算法i2c-adap.retries 2;i2c-adap.class I2C_CLASS_HWMON | I2C_CLASS_SPD; // 该适配器所支持的次设备类有哪些i2c-tx_setup 50;spin_lock_init(i2c-lock); // 初始化互斥锁init_waitqueue_head(i2c-wait); // 初始化工作队列/* find the clock and enable it */i2c-dev pdev-dev; // 通过s3c24xx_i2c-dev 指针指向平台设备的device结构体i2c-clk clk_get(pdev-dev, i2c);if (IS_ERR(i2c-clk)) {dev_err(pdev-dev, cannot get clock\n);ret -ENOENT;goto err_noclk;}dev_dbg(pdev-dev, clock source %p\n, i2c-clk);clk_enable(i2c-clk); // 使能时钟/* map the registers */res platform_get_resource(pdev, IORESOURCE_MEM, 0); // 获取平台设备资源if (res NULL) {dev_err(pdev-dev, cannot find IO resource\n);ret -ENOENT;goto err_clk;}i2c-ioarea request_mem_region(res-start, resource_size(res), // 物理地址到虚拟地址的映射请求pdev-name);if (i2c-ioarea NULL) {dev_err(pdev-dev, cannot request IO\n);ret -ENXIO;goto err_clk;}i2c-regs ioremap(res-start, resource_size(res)); // 地址映射if (i2c-regs NULL) {dev_err(pdev-dev, cannot map IO\n);ret -ENXIO;goto err_ioarea;}dev_dbg(pdev-dev, registers %p (%p, %p)\n,i2c-regs, i2c-ioarea, res);/* setup info block for the i2c core */i2c-adap.algo_data i2c; // 将s3c24xx_i2c 结构体变量作为s3c24xx_i2c中内置的i2c_adapter适配器中的私有数据i2c-adap.dev.parent pdev-dev; // 指定适配器设备的父设备是平台设备device : /sys/devices/platform/s3c2410-i2cn这个目录下/* initialise the i2c controller */ret s3c24xx_i2c_init(i2c); // i2c控制器(适配器) 寄存器相关的配置if (ret ! 0)goto err_iomap;/* find the IRQ for this unit (note, this relies on the init call to* ensure no current IRQs pending*/i2c-irq ret platform_get_irq(pdev, 0); // 获取平台设备中的i2c中断号(这个中断是I2C控制器产生的中断)if (ret 0) {dev_err(pdev-dev, cannot find IRQ\n);goto err_iomap;}ret request_irq(i2c-irq, s3c24xx_i2c_irq, IRQF_DISABLED, // 申请中断dev_name(pdev-dev), i2c);if (ret ! 0) {dev_err(pdev-dev, cannot claim IRQ %d\n, i2c-irq);goto err_iomap;}ret s3c24xx_i2c_register_cpufreq(i2c); // 这个不清楚if (ret 0) {dev_err(pdev-dev, failed to register cpufreq notifier\n);goto err_irq;}/* Note, previous versions of the driver used i2c_add_adapter()* to add the bus at any number. We now pass the bus number via* the platform data, so if unset it will now default to always* being bus 0.*/i2c-adap.nr pdata-bus_num; // 确定i2c主机(适配器)的编号ret i2c_add_numbered_adapter(i2c-adap); // 向i2c核心注册i2c适配器 /sys/devices/platform/s3c2410-i2cn/s3c2410-i2c 因为在函数内会将 i2c-%d作为适配器的名字if (ret 0) {dev_err(pdev-dev, failed to add bus to i2c core\n);goto err_cpufreq;}platform_set_drvdata(pdev, i2c); // 将s3c24xx_i2c变量作为平台设备私有数据中的设备驱动私有数据 dev-p-driver_data// 因为这个变量还会在本文件中其他函数中会用到了clk_disable(i2c-clk);dev_info(pdev-dev, %s: S3C I2C adapter\n, dev_name(i2c-adap.dev));return 0;err_cpufreq:s3c24xx_i2c_deregister_cpufreq(i2c);err_irq:free_irq(i2c-irq, i2c);err_iomap:iounmap(i2c-regs);err_ioarea:release_resource(i2c-ioarea);kfree(i2c-ioarea);err_clk:clk_disable(i2c-clk);clk_put(i2c-clk);err_noclk:kfree(i2c);return ret;
} 3、struct i2c_algorithm结构体 上面的probe函数中有一句代码 i2c-adap.algo s3c24xx_i2c_algorithm; 其中变量s3c24xx_i2c_algorithm的定义如下可见它是struct i2c_algorithm类型变量。 static const struct i2c_algorithm s3c24xx_i2c_algorithm {.master_xfer s3c24xx_i2c_xfer,.functionality s3c24xx_i2c_func,
};1s3c24xx_i2c_xfer函数 s3c24xx_i2c_xfer函数内部调用了s3c24xx_i2c_doxfer函数后者是信息传输函数。 /* s3c24xx_i2c_xfer** first port of call from the i2c bus code when an message needs* transferring across the i2c bus.
*/static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg *msgs, int num)
{struct s3c24xx_i2c *i2c (struct s3c24xx_i2c *)adap-algo_data;int retry;int ret;clk_enable(i2c-clk);for (retry 0; retry adap-retries; retry) {ret s3c24xx_i2c_doxfer(i2c, msgs, num);if (ret ! -EAGAIN)goto out;dev_dbg(i2c-dev, Retrying transmission (%d)\n, retry);udelay(200);}ret -EREMOTEIO;
out:clk_disable(i2c-clk);return ret;
} /* s3c24xx_i2c_doxfer** this starts an i2c transfer
*/static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c,struct i2c_msg *msgs, int num)
{unsigned long timeout;int ret;if (i2c-suspended)return -EIO;ret s3c24xx_i2c_set_master(i2c);//设置成主机模式if (ret ! 0) {dev_err(i2c-dev, cannot get bus (error %d)\n, ret);ret -EAGAIN;goto out;}spin_lock_irq(i2c-lock);i2c-msg msgs; //填充i2c-msg_num num;i2c-msg_ptr 0;i2c-msg_idx 0;i2c-state STATE_START;s3c24xx_i2c_enable_irq(i2c);s3c24xx_i2c_message_start(i2c, msgs);spin_unlock_irq(i2c-lock);timeout wait_event_timeout(i2c-wait, i2c-msg_num 0, HZ * 5);ret i2c-msg_idx;/* having these next two as dev_err() makes life very* noisy when doing an i2cdetect */if (timeout 0)dev_dbg(i2c-dev, timeout\n);else if (ret ! num)dev_dbg(i2c-dev, incomplete xfer (%d)\n, ret);/* ensure the stop has been through the bus */udelay(10);out:return ret;
} 注意其中的struct i2c_msg结构体其表示一个通信周期的数据相关的结构体包括从设备的信息通信数据长度等等。其内容如下 struct i2c_msg {__u16 addr; /* slave address 设备地址 */__u16 flags; /* 本次消息的标志位就是下面的这些 */
#define I2C_M_TEN 0x0010 /* 设置了这个标志位表示从设备的地址是10bit */
#define I2C_M_RD 0x0001 /* 设置了这个标志位表示本次通信i2c控制器是处于接收方否则就是发送方 */
#define I2C_M_NOSTART 0x4000
#define I2C_M_REV_DIR_ADDR 0x2000 /* 设置这个标志位表示需要将读写标志位反转过来 */
#define I2C_M_IGNORE_NAK 0x1000 /* 设置这个标志意味当前i2c_msg忽略I2C器件的ack和nack信号 */
#define I2C_M_NO_RD_ACK 0x0800 /* 设置这个标志位表示在读操作中主机不用ACK */
#define I2C_M_RECV_LEN 0x0400__u16 len; /* 数据长度 */__u8 *buf; /* 数据缓冲区指针 */
}; 在s3c24xx_i2c_doxfer函数中填充的通信算法会在i2c主设备与从设备通信的时候调用。 2s3c24xx_i2c_func函数 该函数表明I2C接口支持哪些特性内容如下 /* declare our i2c functionality */
static u32 s3c24xx_i2c_func(struct i2c_adapter *adap)
{return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING;
}