技术标签: iic驱动 i2c驱动 Linux驱动 i2cbus 框架 linux驱动
了解i2c框架时,在别的博客看到一张图,非常好,引用如下:
其中,i2c_client是具体的设备实例,是通过i2c总线连接到i2c_adapter的。无论是什么i2c设备,都可以通过i2c_adapter来访问i2c总线,i2c_adapter屏蔽了底层i2c总线控制时序,向上层提供一个统一的接口。
以kernel4.8.17为例:
在mach-smdk2440.c文件,
static struct platform_device *smdk2440_devices[] __initdata = {
&s3c_device_ohci,
&s3c_device_lcd,
&s3c_device_wdt,
&s3c_device_i2c0,
&s3c_device_iis,
&smdk2440_device_eth,
};
static void __init smdk2440_map_io(void)
{
s3c24xx_init_io(smdk2440_iodesc, ARRAY_SIZE(smdk2440_iodesc));
s3c24xx_init_uarts(smdk2440_uartcfgs, ARRAY_SIZE(smdk2440_uartcfgs));
samsung_set_timer_source(SAMSUNG_PWM3, SAMSUNG_PWM4);
}
static void __init smdk2440_machine_init(void)
{
s3c24xx_fb_set_platdata(&smdk2440_fb_info);
s3c_i2c0_set_platdata(NULL);
platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));
smdk_machine_init();
}
在smdk2440_map_io函数里,调用s3c24xx_init_io函数:
void __init s3c24xx_init_io(struct map_desc *mach_desc, int size)
{
arm_pm_idle = s3c24xx_default_idle;
/* initialise the io descriptors we need for initialisation */
iotable_init(mach_desc, size);
iotable_init(s3c_iodesc, ARRAY_SIZE(s3c_iodesc));
if (cpu_architecture() >= CPU_ARCH_ARMv5) {
samsung_cpu_id = s3c24xx_read_idcode_v5();
} else {
samsung_cpu_id = s3c24xx_read_idcode_v4();
}
s3c_init_cpu(samsung_cpu_id, cpu_ids, ARRAY_SIZE(cpu_ids));
samsung_pwm_set_platdata(&s3c24xx_pwm_variant);
}
其中有s3c_init_cpu(samsung_cpu_id, cpu_ids, ARRAY_SIZE(cpu_ids)),
参数cpu_ids是:
static struct cpu_table cpu_ids[] __initdata = {
/*太多了,省略......*/
{
.idcode = 0x32440000,
.idmask = 0xffffffff,
.map_io = s3c2440_map_io,
.init_uarts = s3c244x_init_uarts,
.init = s3c2440_init,
.name = name_s3c2440
},
/*太多了,省略......*/
继续跟踪s3c_init_cpu函数:
void __init s3c_init_cpu(unsigned long idcode,
struct cpu_table *cputab, unsigned int cputab_size)
{
cpu = s3c_lookup_cpu(idcode, cputab, cputab_size);
if (cpu == NULL) {
printk(KERN_ERR "Unknown CPU type 0x%08lx\n", idcode);
panic("Unknown S3C24XX CPU");
}
printk("CPU %s (id 0x%08lx)\n", cpu->name, idcode);
if (cpu->init == NULL) {
printk(KERN_ERR "CPU %s support not enabled\n", cpu->name);
panic("Unsupported Samsung CPU");
}
if (cpu->map_io)
cpu->map_io();
}
就是最后一行,会调用map_io函数,即前面的s3c2440_map_io函数:
void __init s3c2440_map_io(void)
{
s3c244x_map_io();
s3c24xx_gpiocfg_default.set_pull = s3c24xx_gpio_setpull_1up;
s3c24xx_gpiocfg_default.get_pull = s3c24xx_gpio_getpull_1up;
}
进入s3c244x_map_io函数:
void __init s3c244x_map_io(void)
{
/* register our io-tables */
iotable_init(s3c244x_iodesc, ARRAY_SIZE(s3c244x_iodesc));
/* rename any peripherals used differing from the s3c2410 */
s3c_device_sdi.name = "s3c2440-sdi";
s3c_device_i2c0.name = "s3c2440-i2c";
s3c_nand_setname("s3c2440-nand");
s3c_device_ts.name = "s3c2440-ts";
s3c_device_usbgadget.name = "s3c2440-usbgadget";
s3c2410_device_dclk.name = "s3c2440-dclk";
}
这里,即是把s3c_device_i2c0结构体的名字改为了”s3c2440-i2c” !!!
好了,回到文章最开头的mach-smdk2440.c文件,看下smdk2440_machine_init函数,
里面会通过s3c_i2c0_set_platdata函数,设置default_i2c_data结构体的bus_num为0,以及设置i2c的IO口:npd->cfg_gpio = s3c_i2c0_cfg_gpio;
接着就会调用platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));在platform平台下进行设备注册,设备名字为”s3c2440-i2c”
有了platform-device,自然有driver!
在i2c-s3c2410.c文件:
static const struct platform_device_id s3c24xx_driver_ids[] = {
{
.name = "s3c2410-i2c",
.driver_data = 0,
}, {
.name = "s3c2440-i2c",
.driver_data = QUIRK_S3C2440,
}, {
.name = "s3c2440-hdmiphy-i2c",
.driver_data = QUIRK_S3C2440 | QUIRK_HDMIPHY | QUIRK_NO_GPIO,
}, { },
};
static struct platform_driver s3c24xx_i2c_driver = {
.probe = s3c24xx_i2c_probe,
.remove = s3c24xx_i2c_remove,
.id_table = s3c24xx_driver_ids,
.driver = {
.name = "s3c-i2c",
.pm = S3C24XX_DEV_PM_OPS,
.of_match_table = of_match_ptr(s3c24xx_i2c_match),
},
};
static int __init i2c_adap_s3c_init(void)
{
return platform_driver_register(&s3c24xx_i2c_driver);
}
可以看到,s3c24xx_driver_ids里是有”s3c2440-i2c”的,所以能和之前的device匹配成功,调用probe函数:
static int s3c24xx_i2c_probe(struct platform_device *pdev)
{
/*太长了,部分省略......*/
strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));
i2c->adap.owner = THIS_MODULE;
i2c->adap.algo = &s3c24xx_i2c_algorithm;
i2c->adap.retries = 2;
i2c->adap.class = I2C_CLASS_DEPRECATED;
i2c->tx_setup = 50;
/* setup info block for the i2c core */
i2c->adap.algo_data = i2c;
i2c->adap.dev.parent = &pdev->dev;
i2c->pctrl = devm_pinctrl_get_select_default(i2c->dev);
i2c->adap.nr = i2c->pdata->bus_num;
i2c->adap.dev.of_node = pdev->dev.of_node;
platform_set_drvdata(pdev, i2c);
pm_runtime_enable(&pdev->dev);
ret = i2c_add_numbered_adapter(&i2c->adap);
}
这里主要注意两个地方:
一是:i2c->adap.algo = &s3c24xx_i2c_algorithm;
这里的s3c24xx_i2c_algorithm是:
/* i2c bus registration info */
static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
.master_xfer = s3c24xx_i2c_xfer,
.functionality = s3c24xx_i2c_func,
};
还记得文章开始的那张图吗?
i2c_adapter和i2c_algorithm 都是操作i2c bus的结构体,前者定义一个i2c模块,后者定义操作模块的方法。(或者理解为:i2c_adapter对应于物理上的一个适配器,而i2c_algorithm对应一套通信方法。)
这里就是i2c_algorithm!!
i2c的底层实现函数,进行封装好,
.master_xfer 用于i2c总线传输,传递给它的i2c_msg数组中每个I2C消息。
.functionality 用于返回algorithm所支持的通信协议,如I2C_FUNC_I2C、I2C_FUNC_10BIT_ADDR、I2C_FUNC_SMBUS_READ_BYTE、I2C_FUNC_SUMBUS_WRITE_BYTE等。
二是:ret = i2c_add_numbered_adapter(&i2c->adap);
这就是i2c_adapter了,进去看下函数实现:
int i2c_add_numbered_adapter(struct i2c_adapter *adap)
{
if (adap->nr == -1) /* -1 means dynamically assign bus id */
return i2c_add_adapter(adap);
return __i2c_add_numbered_adapter(adap);
}
static int __i2c_add_numbered_adapter(struct i2c_adapter *adap)
{
int id;
mutex_lock(&core_lock);
id = idr_alloc(&i2c_adapter_idr, adap, adap->nr, adap->nr + 1, GFP_KERNEL);
mutex_unlock(&core_lock);
if (WARN(id < 0, "couldn't get idr"))
return id == -ENOSPC ? -EBUSY : id;
return i2c_register_adapter(adap);
}
即最后调用i2c_register_adapter(adap)函数,在i2c_bus总线上注册,名字为:
dev_set_name(&adap->dev, “i2c-%d”, adap->nr);
这里说下i2c_adapter与i2c_client的关系:i2c_client依附于i2c_adapter,由于一个适配器上可以连接多个I2C设备,所以一个i2c_adapter也可以被多个i2c_client依附,i2c_adapter中包含依附于它的i2c_client的链表。
.
好咯,i2c框架差不多就是这样咯,我们以一个kernel里的例子来看下:
at24.c函数:
static struct i2c_driver at24_driver = {
.driver = {
.name = "at24",
.acpi_match_table = ACPI_PTR(at24_acpi_ids),
},
.probe = at24_probe,
.remove = at24_remove,
.id_table = at24_ids,
};
static int __init at24_init(void)
{
if (!io_limit) {
pr_err("at24: io_limit must not be 0!\n");
return -EINVAL;
}
io_limit = rounddown_pow_of_two(io_limit);
return i2c_add_driver(&at24_driver);
}
这里调用i2c_add_driver函数在i2c_bus总线下注册,然后看下他的读写函数,以读函数为例:
static ssize_t at24_eeprom_read_i2c(struct at24_data *at24, char *buf,
unsigned int offset, size_t count)
{
unsigned long timeout, read_time;
struct i2c_client *client;
struct i2c_msg msg[2];
int status, i;
u8 msgbuf[2];
memset(msg, 0, sizeof(msg));
client = at24_translate_offset(at24, &offset);
if (count > io_limit)
count = io_limit;
i = 0;
if (at24->chip.flags & AT24_FLAG_ADDR16)
msgbuf[i++] = offset >> 8;
msgbuf[i++] = offset;
msg[0].addr = client->addr;
msg[0].buf = msgbuf;
msg[0].len = i;
msg[1].addr = client->addr;
msg[1].flags = I2C_M_RD;
msg[1].buf = buf;
msg[1].len = count;
loop_until_timeout(timeout, read_time) {
status = i2c_transfer(client->adapter, msg, 2);
if (status == 2)
status = count;
dev_dbg(&client->dev, "read %zu@%d --> %d (%ld)\n",
count, offset, status, jiffies);
if (status == count)
return count;
}
return -ETIMEDOUT;
}
里面就是会调用到i2c_transfer函数了,函数里面以i2c_msg(即I2C消息)为单位通信,i2c_transfer函数里又会调用到__i2c_transfer函数:
int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
unsigned long orig_jiffies;
int ret, try;
if (adap->quirks && i2c_check_for_quirks(adap, msgs, num))
return -EOPNOTSUPP;
if (static_key_false(&i2c_trace_msg)) {
int i;
for (i = 0; i < num; i++)
if (msgs[i].flags & I2C_M_RD)
trace_i2c_read(adap, &msgs[i], i);
else
trace_i2c_write(adap, &msgs[i], i);
}
/* Retry automatically on arbitration loss */
orig_jiffies = jiffies;
for (ret = 0, try = 0; try <= adap->retries; try++) {
ret = adap->algo->master_xfer(adap, msgs, num);
if (ret != -EAGAIN)
break;
if (time_after(jiffies, orig_jiffies + adap->timeout))
break;
}
if (static_key_false(&i2c_trace_msg)) {
int i;
for (i = 0; i < ret; i++)
if (msgs[i].flags & I2C_M_RD)
trace_i2c_reply(adap, &msgs[i], i);
trace_i2c_result(adap, i, ret);
}
return ret;
}
就是这里,里面实现:ret = adap->algo->master_xfer(adap, msgs, num);
这就是之前说的i2c_algorithm 里实现的.master_xfer函数里,实现i2c总线传输函数。
文章浏览阅读178次。一、概述window接口的devicePixelRatio返回当前显示设备的物理像素分辨率与CSS像素分辨率之比。 此值也可以解释为像素大小的比率:一个CSS像素的大小与一个物理像素的大小。 简单来说,它告诉浏览器应使用多少屏幕实际像素来绘制单个CSS像素。二、值一个双精度浮点值,指示显示器的物理像素分辨率与CSS像素分辨率之比。 值1表示经典96 DPI(在某些平台上为76 DPI)显示,而对于HiDPI / Retina显示屏则期望值为2。 在异常低分辨率的显示器中,或更常见的是,当屏幕的像_pixelratio
文章浏览阅读3.6k次。有时候由于公司代码无法由于对应业务、无法发布到npm公有仓库中,此时就可以使用nexus 搭建好私服后、将公用包发送的私服使用。1、添加私服源安装 nrm# 安装 nrmnpm i -g nrm添加 npm 私服源# 添加 npm 私服源 nrm add <自定义源名> <源的url>nrm add xxxx http://xxxxxxxxxx2、私服 npm 包发布步骤仅在发布私服 npm 包时需要登录,正常安装/使用私服 npm 包无需登录。切换 _npm login
文章浏览阅读6.6k次。缘起本期讨论一个好东西,dapr——distributed application runtime。微软开源的CNCF项目,一个帮助我们更好开发分布式应用的框架。本系列一直在谈谈谈,就是没..._边缘设备开发
文章浏览阅读375次。资源名:唯一名称,默认请求路径针对来源:Sentinel可以针对调用者进行限流,填写微服务名,默认default (不区分来源)阅值类型单机阅值:QPS(每秒钟的请求数量):当调用该api的QPS达到值的时候,进行限流线程数:当调用该api的线程数达到阔值的时候,进行限流是否集群:不需要集群流控模式:直接:api达到限流条件时,直接限流关联:当关联的资源达到闻值时,就限流自己链路:只记录指定链路上的流量_sentinel 集群不能配统计时长
文章浏览阅读4.6k次,点赞4次,收藏25次。分析函数是什么? 分析函数是Oracle专门用于解决复杂报表统计需求的功能强大的函数,它可以在数据中进行分组然后计算基于组的某种统计值,并且每一组的每一行都可以返回一个统计值。分析函数和聚合函数的不同之处是什么? 普通的聚合函数用group by分组,每个分组返回一个统计值,而分析函数采用partition by分组,并且每组每行都可以返回一个统计值。分析函数的形式 分析函数带有一个开窗函数o_sum over partition by
文章浏览阅读1.1k次。实现一键布署:1.一键安装全部环境,上一篇文章说了基本的操作 《keras模型的布署在Tensorflow serving — 基础记录》,那么我们是否要手把手自己去安装呢?如果对方使用你的东西,但是不是很熟悉我们是否可以辅助安装,是肯定的,我们可以写一个sh文件,也就是.sh脚本文件参考链接1参考链接22.docker-compse 一键部署:tensorflow-serving和we..._tensorflow-serving:1.14 grpc protobuf
文章浏览阅读221次。点击上方“码农突围”,马上关注这里是码农充电第一站,回复“666”,获取一份专属大礼包真爱,请设置“星标”或点个“在看”来源:blog.csdn.net/lu930124/article..._list.stream().filter().map
文章浏览阅读3w次,点赞6次,收藏60次。Samba网络文件共享服务一、samba简介Samba是一个能让Linux系统应用Microsoft网络通讯协议的软件,而SMB是Server Message Block的缩写,即为服务器消息块,SMB主要是作为Microsoft的网络通讯协议,后来Samba将SMB通信协议应用到了Linux系统上,就形成了现在的Samba软件。后来微软又把 SMB 改名为 CIFS(Common Internet File System),即公共 Internet 文件系统,并且加入了许多新的功能,这样一来,使得Samb
文章浏览阅读941次。动态链接库(dll)的好处就不多说了,这里就把如何在VB中调用VC写的dll函数的方法介绍一下,供有需要的朋友们参考,同时也免得自己以后忘了。初次接触DLL的用户经常会遇到一个问题:在VC环境下创建的DLL,在VC里运行的好好的,可在VB应用程序中调用时却老是出现"调用约定错误"、"找不到入口点"之类的错误。这主要是由以下疏漏造成的。首先,要注意DLL中的函数和VB中的函数声明在名称、返回类型、参..._vb调用vcdll文件 数组类型乱码
文章浏览阅读1.1k次。Linux物理内存管理:page、zone、node_内存 page
文章浏览阅读1.7k次。两种方案1、使用Page,点击每个page进行切换2、直接把控件写在窗体中,然后隐藏,需要显示的时候在显示出来。_wpf多页面切换
文章浏览阅读9.9k次,点赞10次,收藏196次。趁着国庆假期,玩了一下深度学习(主要是LSTM这个网络),顺便做了一个中文多分类的情感分析。中文情感分析相对英文来说,难度太大,所以最后分析的结果,准确度也不是太高,但基本还是没啥问题的。数据我的数据是来自github的一个项目:ChineseNlpCorpus 里面收集了蛮多用于自然语言处理的中文数据集/语料。下载地址: 百度网盘数据概览: 36 万多条,带情感标注 新浪微博,包含..._基于lstm的中文文本情感分类