https://blog.youkuaiyun.com/b7376811/article/details/86607529
今天继续分析NUC972的触摸屏驱动移植过程,上一节主要分析了触摸屏需要数据,今天来分析一下驱动部分,上一节我们已经了解了触摸屏一般有I2C接口和gpio接口,所以触摸屏既是一个I2C设备,也是一个input设备,linux中把触摸屏整体归为input设备,在input驱动中又包含了I2C驱动和gpio按键驱动,今天首先分析一下I2C驱动部分。
I2C驱动在linux内核中整的还是挺复杂的,按我的理解,作为一般的驱动开发者,需要抓住三部分:第一、I2C核心部分驱动;第二、具体的硬件平台的I2C适配器部分(其实一般都是集成到SOC上的),每个适配器驱动对应一个I2C硬件接口;第三、I2C客户端设备驱动,也就是具体和I2C接口连接的硬件设备的驱动。I2C核心部分驱动类似一个领导,负责协调I2C适配器驱动与I2C客户端驱动之间的数据交互,提供相应的接口;I2C适配器驱动就是SOC上的I2C接口的驱动;I2C客户端驱动就是连接的具体的I2C设备的具体操作方式。I2C核心部分驱动属于linux内核的框架制定者所写,作为一般的驱动开发人员是不需要修改的(当然,框架设计者除外,不过那么高级的开发人员,肯定是不屑于看我这个小白写的东西的,哈哈哈,开个玩笑),这一部分的驱动代码在kernel/i2c这个文件夹里面,主要包括i2c-core.c和i2c-dev.c。其实适配器驱动代码,一般也不需要板级驱动开发者开发的,因为一般的方案提供商或者是芯片生产商,在原版的linux内核的基础上,根据自己的平台开发好了,就比如我用的nuc972这个片子,上面自带两个I2C硬件接口,在nuc970系列的BSP中已经做好了两个硬件I2C适配器的驱动,在kernel/drivers/i2c/busses这个文件夹里面,i2c-nuc970-p0.c和i2c-nuc970-p0.c分别是I2C0与I2C1适配器的驱动代码。另外,在内核中包含了使用SOC的gpio接口模拟I2C时序的模拟I2C适配器的驱动代码,同样是在这个文件夹里面,i2c-gpio.c这个文件就是通用的使用gpio模拟I2C通信的适配器驱动代码。而第三部分的I2C客户端的驱动一般是需要板级驱动移植开发者着重分析和开发的。
通过以上的分析,可以看得出来,I2C驱动框架中其实是包含了两套driver+device的结构,分别是I2C适配器设备+I2C适配器驱动、I2C客户端设备+I2C客户端驱动。I2C适配器在内核中是被虚拟成platform_device,相对应的驱动是platform_driver,I2C客户端设备是一个标准的I2C设备。内核启动时,注册platform总线,从kernel/driver/base/init.c这个文件中可以看出来:
-
void __init driver_init(void) -
{ -
/* These are the core pieces */ -
devtmpfs_init(); -
devices_init(); -
buses_init(); -
classes_init(); -
firmware_init(); -
hypervisor_init(); -
/* These are also core pieces, but must come after the -
* core core pieces. -
*/ -
platform_bus_init(); -
cpu_dev_init(); -
memory_dev_init(); -
}
内核使用platform_bus_init()这个函数对platform总线进行了初始化。将这个函数追进去,可以看到对platform总线进行了注册,声明了总线的match方法,这段代码在kernel/drivers/base/platform.c中:
-
struct bus_type platform_bus_type = { -
.name = "platform", -
.dev_attrs = platform_dev_attrs, -
.match = platform_match, -
.uevent = platform_uevent, -
.pm = &platform_dev_pm_ops, -
}; -
EXPORT_SYMBOL_GPL(platform_bus_type); -
int __init platform_bus_init(void) -
{ -
int error; -
early_platform_cleanup(); -
error = device_register(&platform_bus); -
if (error) -
return error; -
error = bus_register(&platform_bus_type); -
if (error) -
device_unregister(&platform_bus); -
return error; -
}
从这段代码中可以看出来,在platform_bus_init这个函数中,注册了平台总线的match方法和uevent方法等,再往上追match方法对应的函数:
-
static int platform_match(struct device *dev, struct device_driver *drv) -
{ -
struct platform_device *pdev = to_platform_device(dev); -
struct platform_driver *pdrv = to_platform_driver(drv); -
/* Attempt an OF style match first */ -
if (of_driver_match_device(dev, drv)) -
return 1; -
/* Then try ACPI style match */ -
if (acpi_driver_match_device(dev, drv)) -
return 1; -
/* Then try to match against the id table */ -
if (pdrv->id_table) -
return platform_match_id(pdrv->id_table, pdev) != NULL; -
/* fall-back to driver name match */ -
return (strcmp(pdev->name, drv->name) == 0); -
}
从这段代码中可以看出来platform总线驱动和设备的具体的match方法,id_table的匹配规则优先级高于name的匹配规则。可能说到这里,各位看官迷糊了,现在是在说I2C,说这么多平台总线的事儿干啥,不要急,下面就来看I2C总线的相关内容,基于ncu970的BSP,nuc972的两个硬件I2C适配器驱动的代码包含在kernel/drivers/i2c/busses文件夹中,上面有提到,分别是i2c-nuc970-p0.c和i2c-nuc970-p1.c,这两个文件几乎相同,咱们这里就只看第一个文件吧,这个文件有几百行,具体的可以自己去看看,咱们这里只是了解一下框架,只贴最后几行吧:
-
static int nuc970_i2c0_probe(struct platform_device *pdev) -
{ -
struct nuc970_i2c *i2c; -
struct nuc970_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 nuc970_i2c), GFP_KERNEL); -
if (!i2c) { -
dev_err(&pdev->dev, "no memory for state\n"); -
return -ENOMEM; -
} -
strlcpy(i2c->adap.name, "nuc970-i2c0", sizeof(i2c->adap.name)); -
i2c->adap.owner = THIS_MODULE; -
i2c->adap.algo = &nuc970_i2c0_algorithm; -
i2c->adap.retries = 2; -
i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; -
spin_lock_init(&i2c->lock); -
init_waitqueue_head(&i2c->wait); -
/* find the clock and enable it */ -
i2c->dev = &pdev->dev; -
i2c->clk = clk_get(NULL, "i2c0"); -
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_prepare(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; -
i2c->adap.dev.parent = &pdev->dev; -
ret = clk_get_rate(i2c->clk)/(pdata->bus_freq * 5) - 1; -
writel(ret & 0xffff, i2c->regs + DIVIDER); -
/* 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); -
if (ret <= 0) { -
dev_err(&pdev->dev, "cannot find IRQ\n"); -
goto err_iomap; -
} -
ret = request_irq(i2c->irq, nuc970_i2c_irq, IRQF_SHARED, -
dev_name(&pdev->dev), i2c); -
if (ret != 0) { -
dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq); -
goto err_iomap; -
} -
/* 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; -
ret = i2c_add_numbered_adapter(&i2c->adap); -
if (ret < 0) { -
dev_err(&pdev->dev, "failed to add bus to i2c core\n"); -
goto err_irq; -
} -
platform_set_drvdata(pdev, i2c); -
dev_info(&pdev->dev, "%s: nuc970 I2C adapter\n", -
dev_name(&i2c->adap.dev)); -
return 0; -
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; -
} -
/* nuc970_i2c0_remove -
* -
* called when device is removed from the bus -
*/ -
static int nuc970_i2c0_remove(struct platform_device *pdev) -
{ -
struct nuc970_i2c *i2c = platform_get_drvdata(pdev); -
i2c_del_adapter(&i2c->adap); -
free_irq(i2c->irq, i2c); -
clk_disable(i2c->clk); -
clk_put(i2c->clk); -
iounmap(i2c->regs); -
release_resource(i2c->ioarea); -
kfree(i2c->ioarea); -
kfree(i2c); -
return 0; -
} -
static struct platform_driver nuc970_i2c0_driver = { -
.probe = nuc970_i2c0_probe, -
.remove = nuc970_i2c0_remove, -
.driver = { -
.name = "nuc970-i2c0", -
.owner = THIS_MODULE, -
}, -
}; -
module_platform_driver(nuc970_i2c0_driver); -
MODULE_DESCRIPTION("nuc970 I2C Bus driver"); -
MODULE_AUTHOR("Wan ZongShun, <mcuos.com-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>"); -
MODULE_LICENSE("GPL"); -
MODULE_ALIAS("platform:nuc970-i2c0");
从代码的最后几行可以看出来,nuc970系列的I2C0接口确实是被虚拟成了一个platform设备,具体的设备数据在kernel/arch/arm/mach-nuc970/dev.c这个文件里面,如下代码:
-
static struct resource nuc970_i2c0_resource[] = { -
[0] = { -
.start = NUC970_PA_I2C0, -
.end = NUC970_PA_I2C0 + NUC970_SZ_I2C0 - 1, -
.flags = IORESOURCE_MEM, -
}, -
[1] = { -
.start = IRQ_I2C0, -
.end = IRQ_I2C0, -
.flags = IORESOURCE_IRQ, -
} -
}; -
static struct nuc970_platform_i2c nuc970_i2c0_data = { -
.bus_num = 0, -
.bus_freq = 100000, -
}; -
struct platform_device nuc970_device_i2c0 = { -
.name = "nuc970-i2c0", -
.id = -1, -
.num_resources = ARRAY_SIZE(nuc970_i2c0_resource), -
.resource = nuc970_i2c0_resource, -
.dev = { -
.platform_data = &nuc970_i2c0_data, -
} -
};
在这里定义了nuc970系列的i2c0接口必须的硬件资源,可以明显的看出来是一个platform设备。而platform总线在匹配device与driver时,是根据name = "nuc970-i2c0"这个名字字段来匹配的。在总线上,只要driver与device同时出现这个名字,platform总线的match方法就能让他们匹配起来,干柴烈火,就燃烧起来了,正常情况下,后果就是启动了platform_driver中的probe方法,根据上面对应的probe方法对应的函数,可以很清楚的看出来,使用ret = i2c_add_numbered_adapter(&i2c->adap)这个函数向内核添加了一个I2C适配器。至此,I2C0这个硬件适配器已经被成功添加到内核中,并添加了一个名字为nuc970-i2c0的I2C总线。
对于GPIO模拟的I2C适配器驱动,同样是这个道理,驱动内具体的实现方法不太一样,这里就不详细分析了。
下面再看一下I2C总线的注册过程,在kernel/drivers/i2c/i2c-core.c这个文件中,包含了I2C总线初始化的代码:
-
static int __init i2c_init(void) -
{ -
int retval; -
retval = bus_register(&i2c_bus_type); -
if (retval) -
return retval; -
#ifdef CONFIG_I2C_COMPAT -
i2c_adapter_compat_class = class_compat_register("i2c-adapter"); -
if (!i2c_adapter_compat_class) { -
retval = -ENOMEM; -
goto bus_err; -
} -
#endif -
retval = i2c_add_driver(&dummy_driver); -
if (retval) -
goto class_err; -
return 0; -
class_err: -
#ifdef CONFIG_I2C_COMPAT -
class_compat_unregister(i2c_adapter_compat_class); -
bus_err: -
#endif -
bus_unregister(&i2c_bus_type); -
return retval; -
} -
static void __exit i2c_exit(void) -
{ -
i2c_del_driver(&dummy_driver); -
#ifdef CONFIG_I2C_COMPAT -
class_compat_unregister(i2c_adapter_compat_class); -
#endif -
bus_unregister(&i2c_bus_type); -
} -
/* We must initialize early, because some subsystems register i2c drivers -
* in subsys_initcall() code, but are linked (and initialized) before i2c. -
*/ -
postcore_initcall(i2c_init); -
module_exit(i2c_exit);
这段代码可以很清楚的看出来在内核中注册了i2c_bus,并注册了一个名字为dummy的I2C总线。根据注册的i2c_bus_type,网上追代码:
-
struct bus_type i2c_bus_type = { -
.name = "i2c", -
.match = i2c_device_match, -
.probe = i2c_device_probe, -
.remove = i2c_device_remove, -
.shutdown = i2c_device_shutdown, -
.pm = &i2c_device_pm_ops, -
};
可以看出来注册I2C总线时,分别配置了match、probe、remove等方法,网上追match方法实现的代码:
-
static int i2c_device_match(struct device *dev, struct device_driver *drv) -
{ -
struct i2c_client *client = i2c_verify_client(dev); -
struct i2c_driver *driver; -
if (!client) -
return 0; -
/* Attempt an OF style match */ -
if (of_driver_match_device(dev, drv)) -
return 1; -
/* Then ACPI style match */ -
if (acpi_driver_match_device(dev, drv)) -
return 1; -
driver = to_i2c_driver(drv); -
/* match on an id table if there is one */ -
if (driver->id_table) -
return i2c_match_id(driver->id_table, client) != NULL; -
return 0; -
}
从上面的代码可以很清楚的看出来,i2c的device与driver之间的匹配是根据id_table进行匹配的,其实将i2c_match_id这个函数追进去,可以看出来,也是根据名字进行匹配的。
说到这里,I2C适配器端的驱动与设备基本上已经分析完成了,下面继续分析I2C客户端的驱动与设备数据,即触摸屏芯片TSC2007的驱动(终于说到触摸屏芯片了)。首先看一下TSC2007驱动代码,在kernel/drivers/input/touchscreen文件夹里面,tsc2007.c这个文件,同样的,这里只看框架,所以只看一下最后几行代码:
-
static int tsc2007_probe(struct i2c_client *client, -
const struct i2c_device_id *id) -
{ -
struct tsc2007 *ts; -
struct tsc2007_platform_data *pdata = client->dev.platform_data; -
struct input_dev *input_dev; -
int err; -
if (!pdata) { -
dev_err(&client->dev, "platform data is required!\n"); -
return -EINVAL; -
} -
if (!i2c_check_functionality(client->adapter, -
I2C_FUNC_SMBUS_READ_WORD_DATA)) -
return -EIO; -
ts = kzalloc(sizeof(struct tsc2007), GFP_KERNEL); -
input_dev = input_allocate_device(); -
if (!ts || !input_dev) { -
err = -ENOMEM; -
goto err_free_mem; -
} -
/***********************************************************************/ -
#if 1 -
#warning hack for MDK972 -
if (gpio_is_valid(NUC970_PE15)) { -
err = gpio_request_one(NUC970_PE15, GPIOF_IN, "nuc970-ts2007"); -
if (err < 0) { -
dev_err(&client->dev, "Failed to request GPIO %d, error %d\n", -
NUC970_PE15, err); -
return err; -
} -
#if 1 -
if (1) { -
err = gpio_set_debounce(NUC970_PE15, -
1 * 1000); -
/* use timer if gpiolib doesn't provide debounce */ -
if (err < 0) -
dev_err(&client->dev, "Failed to set_debounce for GPIO %d, error %d\n", -
NUC970_PE15, err); -
} -
#endif -
client->irq = gpio_to_irq(NUC970_PE15); -
if (client->irq < 0) { -
err = client->irq; -
dev_err(&client->dev, -
"Unable to get irq number for GPIO %d, error %d\n", -
NUC970_PE15, err); -
goto err_free_mem; -
} -
} -
#endif -
/***********************************************************************/ -
ts->client = client; -
ts->irq = client->irq; -
ts->input = input_dev; -
init_waitqueue_head(&ts->wait); -
ts->model = pdata->model; -
ts->x_plate_ohms = pdata->x_plate_ohms; -
ts->max_rt = pdata->max_rt ? : MAX_12BIT; -
ts->poll_delay = pdata->poll_delay ? : 1; -
ts->poll_period = pdata->poll_period ? : 1; -
ts->get_pendown_state = pdata->get_pendown_state; -
ts->clear_penirq = pdata->clear_penirq; -
if (pdata->x_plate_ohms == 0) { -
dev_err(&client->dev, "x_plate_ohms is not set up in platform data"); -
err = -EINVAL; -
goto err_free_mem; -
} -
snprintf(ts->phys, sizeof(ts->phys), -
"%s/input0", dev_name(&client->dev)); -
input_dev->name = "TSC2007 Touchscreen"; -
input_dev->phys = ts->phys; -
input_dev->id.bustype = BUS_I2C; -
input_dev->open = tsc2007_open; -
input_dev->close = tsc2007_close; -
input_set_drvdata(input_dev, ts); -
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); -
input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); -
input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, pdata->fuzzx, 0); -
input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, pdata->fuzzy, 0); -
input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, -
pdata->fuzzz, 0); -
if (pdata->init_platform_hw) -
pdata->init_platform_hw(); -
err = request_threaded_irq(ts->irq, tsc2007_hard_irq, tsc2007_soft_irq, -
IRQF_ONESHOT, client->dev.driver->name, ts); -
if (err < 0) { -
dev_err(&client->dev, "irq %d busy?\n", ts->irq); -
goto err_free_mem; -
} -
tsc2007_stop(ts); -
err = input_register_device(input_dev); -
if (err) -
goto err_free_irq; -
i2c_set_clientdata(client, ts); -
return 0; -
err_free_irq: -
free_irq(ts->irq, ts); -
if (pdata->exit_platform_hw) -
pdata->exit_platform_hw(); -
err_free_mem: -
input_free_device(input_dev); -
kfree(ts); -
return err; -
} -
static int tsc2007_remove(struct i2c_client *client) -
{ -
struct tsc2007 *ts = i2c_get_clientdata(client); -
struct tsc2007_platform_data *pdata = client->dev.platform_data; -
free_irq(ts->irq, ts); -
if (pdata->exit_platform_hw) -
pdata->exit_platform_hw(); -
input_unregister_device(ts->input); -
kfree(ts); -
return 0; -
} -
static const struct i2c_device_id tsc2007_idtable[] = { -
{ "tsc2007", 0 }, -
{ } -
}; -
MODULE_DEVICE_TABLE(i2c, tsc2007_idtable); -
static struct i2c_driver tsc2007_driver = { -
.driver = { -
.owner = THIS_MODULE, -
.name = "tsc2007" -
}, -
.id_table = tsc2007_idtable, -
.probe = tsc2007_probe, -
.remove = tsc2007_remove, -
}; -
module_i2c_driver(tsc2007_driver); -
MODULE_AUTHOR("Kwangwoo Lee <kwlee@mtekvision.com>"); -
MODULE_DESCRIPTION("TSC2007 TouchScreen Driver"); -
MODULE_LICENSE("GPL");
tsc2007的驱动是向内核的添加了i2c的id_table,作为总线match时使用,并注册了一个名字为tsc2007的i2c_driver,里面包含probe方法、remove方法,这是驱动部分。下面再看一下i2c设备的device部分,这部分的数据在kernel/arch/arm/mach-nuc970文件夹里面的dev.c中:
-
static struct i2c_board_info __initdata nuc970_i2c_clients2[] = -
{ -
{ -
I2C_BOARD_INFO("tsc2007", 0x48), -
.platform_data = &tsc2007_info, -
/* irq number is run-time assigned */ -
}, -
#ifdef CONFIG_SENSOR_OV7725 -
{I2C_BOARD_INFO("ov7725", 0x21),}, -
#endif -
#ifdef CONFIG_SENSOR_OV5640 -
{I2C_BOARD_INFO("ov5640", 0x3c),}, -
#endif -
#ifdef CONFIG_SENSOR_NT99141 -
{I2C_BOARD_INFO("nt99141", 0x2a),}, -
#endif -
#ifdef CONFIG_SENSOR_NT99050 -
{I2C_BOARD_INFO("nt99050", 0x21),}, -
#endif -
{I2C_BOARD_INFO("lm75a", 0x4e),}, -
{I2C_BOARD_INFO("ds1307", 0x68),}, -
};
从上面的代码中可以看出来,这个总线上包含的不仅仅是触摸屏一个I2C设备,I2C_BOARD_INFO就是一个宏定义,填充结构体i2c_board_info的名字和地址字段。这个是就是tsc2007需要传入驱动的数据,下面看一下i2c device的注册:
i2c_register_board_info(2, nuc970_i2c_clients2, ARRAY_SIZE(nuc970_i2c_clients2));
其实就是这个函数,意思就是想I2C适配器号码为2的总线注册这些I2C客户端设备,将这个函数追进去,其实就是向相应i2c总线的id_table对应的链表中增加元素:
-
int __init -
i2c_register_board_info(int busnum, -
struct i2c_board_info const *info, unsigned len) -
{ -
int status; -
down_write(&__i2c_board_lock); -
/* dynamic bus numbers will be assigned after the last static one */ -
if (busnum >= __i2c_first_dynamic_bus_num) -
__i2c_first_dynamic_bus_num = busnum + 1; -
for (status = 0; len; len--, info++) { -
struct i2c_devinfo *devinfo; -
devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL); -
if (!devinfo) { -
pr_debug("i2c-core: can't register boardinfo!\n"); -
status = -ENOMEM; -
break; -
} -
devinfo->busnum = busnum; -
devinfo->board_info = *info; -
list_add_tail(&devinfo->list, &__i2c_board_list); -
} -
up_write(&__i2c_board_lock); -
return status; -
}
到此为止,i2c客户端的driver与device都已经注册完毕,和平台总线一样的流程,i2c总线通过match绑定的匹配方法对driver与device进行匹配,当driver与device中都出现“tsc2007”这个字符时,同样的情况,同样的干柴烈火,同样的启动驱动中的probe方法对应的函数,就可是使用i2c_device设备中的数据对i2c driver中的相应字段进行填充。
好了,到这里,触摸屏驱动中和I2C相关的内容基本上就分析完了,在这里总结一下,I2C的驱动分为两部分,第一部分是I2C适配器的驱动,也就是soc上的I2C接口的驱动,在内核中是一个platform设备,对应的也是platform驱动,当I2C适配器的设备与驱动匹配成功后,会向内核添加相对应的I2C总线,这个是实实在在的i2c总线,这样I2C适配器的设备与驱动就匹配完成了。第二部分是I2C客户端的设备与驱动,I2c客户端的驱动是i2c总线驱动,设备是i2c设备,通过设备数据向相应的设备总线注册i2c device,驱动端向内核中注册相应的驱动,i2c总线的match方法根据名字进行匹配,一旦匹配成功,I2C部分的初始化基本上就完成了。
好了,今天就说这么多吧。

本文深入解析NUC972平台下触摸屏驱动的移植过程,重点分析了I2C驱动的框架与实现,包括I2C适配器与客户端设备的驱动开发,以及TSC2007触摸屏芯片的驱动实现。
839

被折叠的 条评论
为什么被折叠?



