我们分析一个驱动模块,最重要的,也是最先的,就是找到它的probe函数,然后再根据probe中的逻辑去逐个分析其他函数源码。
通过查找,我们能在fbtft的源码中发现和probe有关的函数。
fbtft_device.c中
static int __init fbtft_device_init(void)
{
struct spi_board_info *spi = NULL;
struct fbtft_platform_data *pdata;
bool found = false;
int i = 0;
int ret = 0;
if (!name) { name是在模块载入时可设定的参数,指显示屏的名字
#ifdef MODULE
pr_err("missing module parameter: 'name'\n");
return -EINVAL;
#else
return 0;
#endif
}
if (init_num > FBTFT_MAX_INIT_SEQUENCE) { init_num初始化命令长度
pr_err("init parameter: exceeded max array size: %d\n",
FBTFT_MAX_INIT_SEQUENCE);
return -EINVAL;
}
if (verbose > 2) { 打印所有支持显示屏的名字
pr_spi_devices(); /* print list of registered SPI devices */
pr_p_devices(); /* print list of 'fb' platform devices */
}
pr_debug("name='%s', busnum=%d, cs=%d\n", name, busnum, cs);
if (rotate > 0 && rotate < 4) {
rotate = (4 - rotate) * 90;
pr_warn("argument 'rotate' should be an angle. Values 1-3 is deprecated. Setting it to %d.\n",
rotate);
}
if (rotate != 0 && rotate != 90 && rotate != 180 && rotate != 270) {
pr_warn("argument 'rotate' illegal value: %d. Setting it to 0.\n",
rotate);
rotate = 0;
}
/* name=list lists all supported displays */
if (strcmp(name, "list") == 0) {
pr_info("Supported displays:\n");
for (i = 0; i < ARRAY_SIZE(displays); i++)
pr_info("%s\n", displays[i].name);
return -ECANCELED;
}
if (custom) { 利用custom可以添加设备
i = ARRAY_SIZE(displays) - 1;
displays[i].name = name;
if (speed == 0) {
displays[i].pdev->name = name;
displays[i].spi = NULL;
} else {
size_t len;
len = strlcpy(displays[i].spi->modalias, name,
SPI_NAME_SIZE);
if (len >= SPI_NAME_SIZE)
pr_warn("modalias (name) truncated to: %s\n",
displays[i].spi->modalias);
displays[i].pdev = NULL;
}
}
for (i = 0; i < ARRAY_SIZE(displays); i++) {
if (strncmp(name, displays[i].name, SPI_NAME_SIZE) == 0) {
if (displays[i].spi) {
spi = displays[i].spi;
spi->chip_select = cs;
spi->bus_num = busnum;
if (speed)
spi->max_speed_hz = speed;
if (mode != -1)
spi->mode = mode;
pdata = (void *)spi->platform_data;
} else if (displays[i].pdev) {
p_device = displays[i].pdev;
pdata = p_device->dev.platform_data;
} else {
pr_err("broken displays array\n");
return -EINVAL;
}
pdata->rotate = rotate;
if (bgr == 0)
pdata->bgr = false;
else if (bgr == 1)
pdata->bgr = true;
if (startbyte)
pdata->startbyte = startbyte;
if (gamma)
pdata->gamma = gamma;
pdata->display.debug = debug;
if (fps)
pdata->fps = fps;
if (txbuflen)
pdata->txbuflen = txbuflen;
if (init_num)
pdata->display.init_sequence = init;
if (custom) {
pdata->display.width = width;
pdata->display.height = height;
pdata->display.buswidth = buswidth;
pdata->display.backlight = 1;
}
if (displays[i].spi) {
ret = fbtft_device_spi_device_register(spi);
if (ret) {
pr_err("failed to register SPI device\n");
return ret;
}
} else {
ret = platform_device_register(p_device);
if (ret < 0) {
pr_err("platform_device_register() returned %d\n",
ret);
return ret;
}
}
found = true;
break;
}
}
if (!found) {
pr_err("display not supported: '%s'\n", name);
return -EINVAL;
}
if (spi_device && (verbose > 1))
pr_spi_devices();
if (p_device && (verbose > 1))
pr_p_devices();
return 0;
}
arch_initcall(fbtft_device_init);
总之,在fbtft_device_init
中会读取模块载入时用户设置的变量,存进platform_data
供下一步使用,并称调用fbtft_device_spi_device_register
向SPI子系统申请SPI设备。
然后我们可以发现,这里用的是arch_initcall(fbtft_device_init);
,一般都是module_init
#define arch_initcall(fn) __define_initcall(fn, 3)
#define module_init(x) __initcall(x);
#define __initcall(fn) device_initcall(fn)
#define device_initcall(fn) __define_initcall(fn, 6)
都是调用arch_initcall,但是数字越小优先级越高,启动的时候就会优先启动
继续寻找
int fbtft_probe_common(struct fbtft_display *display,
struct spi_device *sdev,
struct platform_device *pdev)
{
struct device *dev;
struct fb_info *info;
struct fbtft_par *par;
struct fbtft_platform_data *pdata;
int ret;
if (sdev)
dev = &sdev->dev;
else
dev = &pdev->dev;
if (unlikely(display->debug & DEBUG_DRIVER_INIT_FUNCTIONS))
dev_info(dev, "%s()\n", __func__);
pdata = dev->platform_data; 获取fbtft_device设定的platform_data
if (!pdata) { 如果没有就从设备树获取
pdata = fbtft_probe_dt(dev);
if (IS_ERR(pdata))
return PTR_ERR(pdata);
}
info = fbtft_framebuffer_alloc(display, dev, pdata); 重点函数,提取信息设置到info里
if (!info)
return -ENOMEM;
par = info->par;
par->spi = sdev;
par->pdev = pdev;
if (display->buswidth == 0) {
dev_err(dev, "buswidth is not set\n");
return -EINVAL;
}
从这开始,设置一些默认的操作函数,例如:SPI写入8 Bits
/* write register functions */
if (display->regwidth == 8 && display->buswidth == 8)
par->fbtftops.write_register = fbtft_write_reg8_bus8;
else if (display->regwidth == 8 && display->buswidth == 9 && par->spi)
par->fbtftops.write_register = fbtft_write_reg8_bus9;
else if (display->regwidth == 16 && display->buswidth == 8)
par->fbtftops.write_register = fbtft_write_reg16_bus8;
else if (display->regwidth == 16 && display->buswidth == 16)
par->fbtftops.write_register = fbtft_write_reg16_bus16;
else
dev_warn(dev,
"no default functions for regwidth=%d and buswidth=%d\n",
display->regwidth, display->buswidth);
/* write_vmem() functions */
if (display->buswidth == 8)
par->fbtftops.write_vmem = fbtft_write_vmem16_bus8;
else if (display->buswidth == 9)
par->fbtftops.write_vmem = fbtft_write_vmem16_bus9;
else if (display->buswidth == 16)
par->fbtftops.write_vmem = fbtft_write_vmem16_bus16;
/* GPIO write() functions */
if (par->pdev) {
if (display->buswidth == 8)
par->fbtftops.write = fbtft_write_gpio8_wr;
else if (display->buswidth == 16)
par->fbtftops.write = fbtft_write_gpio16_wr;
}
/* 9-bit SPI setup */
if (par->spi && display->buswidth == 9) {
if (par->spi->master->bits_per_word_mask & SPI_BPW_MASK(9)) {
par->spi->bits_per_word = 9;
} else {
dev_warn(&par->spi->dev,
"9-bit SPI not available, emulating using 8-bit.\n");
/* allocate buffer with room for dc bits */
par->extra = devm_kzalloc(par->info->device,
par->txbuf.len +
(par->txbuf.len / 8) + 8,
GFP_KERNEL);
if (!par->extra) {
ret = -ENOMEM;
goto out_release;
}
par->fbtftops.write = fbtft_write_spi_emulate_9;
}
}
if (!par->fbtftops.verify_gpios)
par->fbtftops.verify_gpios = fbtft_verify_gpios;
/* make sure we still use the driver provided functions */
fbtft_merge_fbtftops(&par->fbtftops, &display->fbtftops);
从各显示屏设备文件中获取并覆盖原有的操作函数。
/* use init_sequence if provided */
if (par->init_sequence)
par->fbtftops.init_display = fbtft_init_display;
/* use platform_data provided functions above all */
fbtft_merge_fbtftops(&par->fbtftops, &pdata->display.fbtftops);
从fbtft_device.c中获取并覆盖原有的操作函数。(实际上没有定义,这句无用)
ret = fbtft_register_framebuffer(info);注册SPI设备,注册到用户空间,并且开始初始化,
if (ret < 0)
goto out_release;
return 0;
out_release:
fbtft_framebuffer_release(info);
return ret;
}
EXPORT_SYMBOL(fbtft_probe_common);
这函数作用是,提取并储存之前fbtft_device.c
里设置的变量,然后定义默认的操作函数,通过fbtft_merge_fbtftops
覆盖原有的操作函数,最后在fbtft_register_framebuffer(info);
注册SPI设备,注册到用户空间,并且开始初始化。
接着再找找
#define FBTFT_REGISTER_DRIVER(_name, _compatible, _display) \
\
static int fbtft_driver_probe_spi(struct spi_device *spi) \
{ \
return fbtft_probe_common(_display, spi, NULL); \
} \
\
static int fbtft_driver_remove_spi(struct spi_device *spi) \
{ \
struct fb_info *info = spi_get_drvdata(spi); \
\
return fbtft_remove_common(&spi->dev, info); \
} \
\
static int fbtft_driver_probe_pdev(struct platform_device *pdev) \
{ \
return fbtft_probe_common(_display, NULL, pdev); \
} \
\
static int fbtft_driver_remove_pdev(struct platform_device *pdev) \
{ \
struct fb_info *info = platform_get_drvdata(pdev); \
\
return fbtft_remove_common(&pdev->dev, info); \
} \
\
static const struct of_device_id dt_ids[] = { \
{ .compatible = _compatible }, \
{}, \
}; \
\
MODULE_DEVICE_TABLE(of, dt_ids); \
\
\
static struct spi_driver fbtft_driver_spi_driver = { \
.driver = { \
.name = _name, \
.of_match_table = of_match_ptr(dt_ids), \
}, \
.probe = fbtft_driver_probe_spi, \
.remove = fbtft_driver_remove_spi, \
}; \
\
static struct platform_driver fbtft_driver_platform_driver = { \
.driver = { \
.name = _name, \
.owner = THIS_MODULE, \
.of_match_table = of_match_ptr(dt_ids), \
}, \
.probe = fbtft_driver_probe_pdev, \
.remove = fbtft_driver_remove_pdev, \
}; \
\
static int __init fbtft_driver_module_init(void) \
{ \
int ret; \
\
ret = spi_register_driver(&fbtft_driver_spi_driver); \
if (ret < 0) \
return ret; \
return platform_driver_register(&fbtft_driver_platform_driver); \
} \
\
static void __exit fbtft_driver_module_exit(void) \
{ \
spi_unregister_driver(&fbtft_driver_spi_driver); \
platform_driver_unregister(&fbtft_driver_platform_driver); \
} \
\
module_init(fbtft_driver_module_init); \
module_exit(fbtft_driver_module_exit);
这个就比较简单了,生成一系列模块加载的模板
总结一下,fbtft_device先会调用fbtft_device_init,解析fbtft_device.c中的fbtft_device_display displays[]中的参数,同时也接受模块加载时用户指定的参数
如:modprobe fbtft_device name=admatec_c-berry28 speed=32000000 debug=7
最重要的是,将这些数据存进platform_data
中。
然后在fbtft_probe_common()
中,取出这些数据,用于设置fb_info,然后注册fb_info,以及spi写入等函数。
总体上来看,fb_tft的源码简单,逻辑清晰。
这里没有进一步深入分析fb_tft模块内底层控制SPI的和FB的函数,因为展开实在是太多了。fb_tft模块的逻辑很清晰,有兴趣的可以期待我第二批FBTFT源码分析,我会在下一篇编写"FBTFT"最小单元,实现FB到SPI的映射,从底层分析FBTFT的FB和SPI的控制函数。