嵌入式开发的一个常见任务是调配新设备。 以前有很多任务,包括配置uboot、添加flash驱动程序支持、配置MTD分区和配置网络。 其中MTD分区信息往往以cmdline的形式传递给内核,有两种方法:通过uboot参数传递,以及内核配置CONFIG_CMDLINE参数。
在cmdline中,MTD分区信息具有以下固定格式:
mtdparts=mtddef[; MTD def ] MTD def :=MTD-id :部件def [, 部件def ]部件def :=size [ @ offset ] [ name ] [ ro ] [ lk ] MTD-id :=uniquenameusedinmappingdriver/设备(MTD size :=标准dardlinuxmemsizeor '-' todenoteallremainingspacesizeisautomaticallytruncatedatendofdeviceifspecior ) ped offset :=standardlinuxmemsizeifomittedthepartwillimmediatelyfollowthepreviouspartor0ifthefirstpartname :=' (' nanal MTD部件=SPI 32766.03360256 k (uboot )、2m (kernel )、6m (rootfs ).- ) (rootfs ) )。
其中mtd-id=spi32766.0,我接触的很多项目都采用这一个mtd-id,为什么是这个值? 32766是怎么得到的? 和flash芯片有关吗? 分析一下吧。
1,32766=0x7FFE,最初以为应该与闪存型号有关,但搜索闪存驱动器代码后未发现两者之间的关联。 另外,使用不同闪存芯片的器件采用了该值,表示与闪存没有关系。
2、那么分析代码。 首先,需要知道内核中的哪个变量表示mtd-id。 这里不是按照代码的执行顺序进行分析,而是打算按照我思考问题的方式进行分析。 既然mtdparts是cmdline参数,我们首先查看cmdline的分析函数。
staticintparse _ cmdline _ partitions (struct MTD _ info * master,struct mtd_partition **pparts,struct MTD _ part _ part _ s ) 部件; 部件=部件-下一步({ if (! mtd_id! strcmp (部件MTD_id,MTD_id ) ) break; (与cmdline的mtd-id匹配……) 12345678910111213上的代码显示内核中的mtd-id是在master-name中定义的,然后从该函数开始跟踪。 调用关系如下:
m25p _ probe-- MTD _ device _ parse _ register-- parse _ MTD _ partitions-- parse _ cmdline _ partitions 1234是
静态int M25 p _ probe (struct SPI _ device * SPI ({ . struct M25 p * flash; struct spi_nor *nor; flash=devm_kzalloc(SPI-dev,sizeof ) flash ),GFP_KERNEL ); nor-dev=spi-dev; //spi设备设备nor-MTD=flash-MTD; //master源. ret=SPI_nor_scan(nor,SPI_get_device_id ) SPI ),mode ); //在此为nor处理. return MTD _ device _ parse _ register (
&flash->mtd, NULL, &ppdata, data ? data->parts : NULL, data ? data->nr_parts : 0);} 1234567891011121314151617继续跟踪spi_nor_scan
int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id, enum read_mode mode){ ... struct device *dev = nor->dev; struct mtd_info *mtd = nor->mtd; ... mtd->name = dev_name(dev);//mtd-id是设备dev的name ...} 12345678910可以得出内核中mtd-id是spi设备名(spi->dev->name)
3、接下来我们去跟踪spi设备名是怎么得来的
查找spi设备驱动(platform_driver),一般是通过设备树dts信息去内核查找,跟踪platform_driver中相应的probe函数。这里的驱动函数就不列举了,不同的spi设备有不同的驱动,一般是由原厂商提供。驱动probe函数中一般的执行流程如下:
1) spi_alloc_master
struct spi_master *spi_alloc_master(struct device *dev, unsigned size){ struct spi_master *master; ... master = kzalloc(size + sizeof(*master), GFP_KERNEL); ... master->bus_num = -1;//注意这里初始化为-1 master->num_chipselect = 1; master->dev.class = &spi_master_class; master->dev.parent = get_device(dev); spi_master_set_devdata(master, &master[1]); return master;} 12345678910111213142)spi_register_master
int spi_register_master(struct spi_master *master){ static atomic_t dyn_bus_id = ATOMIC_INIT((1<<15) - 1); // 重点,dyn_bus_id初始化为0x7FFF struct device *dev = master->dev.parent; ... if (master->num_chipselect == 0) return -EINVAL; if ((master->bus_num < 0) && master->dev.of_node) master->bus_num = of_alias_get_id(master->dev.of_node, "spi"); /* convention: dynamically assigned bus IDs count down from the max */ if (master->bus_num < 0) { // 初始化时bus_num=-1 /* FIXME switch to an IDR based scheme, something like * I2C now uses, so we can't run out of "dynamic" IDs */ master->bus_num = atomic_dec_return(&dyn_bus_id); //计算后bus_num=0x7FFE=32766 dynamic = 1; } ... /* register the device, then userspace will see it. * registration fails if the bus ID is in use. */ dev_set_name(&master->dev, "spi%u", master->bus_num);// master->dev->name="spi32766" status = device_add(&master->dev); if (status < 0) goto done; dev_dbg(dev, "registered master %s%sn", dev_name(&master->dev), dynamic ? " (dynamic)" : ""); ... /* Register devices from the device tree and ACPI */ of_register_spi_devices(master); acpi_register_spi_devices(master);done: return status;} 1234567891011121314151617181920212223242526272829303132333435363738现在已经得到spi_master设备名为spi32766
3)of_register_spi_devices
4)spi_add_device
int spi_add_device(struct spi_device *spi){ ... spi_dev_set_name(spi);//确定spi->dev->name ... status = device_add(&spi->dev);//这是linux设备模型中一个很关键的函数,这里不详细讲,函数除了添加device到bus外,还会probe drivers for this device and attach device to driver. ...}static void spi_dev_set_name(struct spi_device *spi){ struct acpi_device *adev = ACPI_COMPANION(&spi->dev); if (adev) { dev_set_name(&spi->dev, "spi-%s", acpi_dev_name(adev)); return; } //最终确定spi设备名为"spi32766.0",dev_name(&spi->master->dev)为"spi32766", spi->chip_select为0 dev_set_name(&spi->dev, "%s.%u", dev_name(&spi->master->dev), spi->chip_select);} 12345678910111213141516171819202122至此,确定spi设备名spi32766.0,按代码流程分析,已经清楚为什么mtd-id是spi32766.0了。