#include #include #include #include #include #if defined(CONFIG_TFFS) #include #endif #include #include #include #include #include #include #include /* * Container-Aufbau: * (mtd-nand "ubi" (ubivol "avm_filesys_0" (mtd-gluebi "filesystem"), ... )) * * Ablauf: * * (0. mtdram1= ist gesetzt und prepare_ram_resource bereitet * avm_mtd_ram_resource vor.) * 1. init registriert notification handler und ggf. ein neues platram device * 2. mtd_add_handler wird über neue devices in RAM oder NAND informiert. * a) abhängig von mtd->name wird der root= parameter gesetzt, wobei RAM * priorisiert wird * b) "nand-tffs" wird für TFFS3 registriert * c) für "ubi" wird der ubi= parameter gesetz * d) "urlader" wird als urlader_mtd gemerkt (z.B. für prom_getenv) * 3. gluebi stellt mtdblock devices auf ubi bereit und triggert * mtd_add_handler, was mtd->name ändert und wieder nach Punkt 2 vorgeht. * * Wenn UBI_BLOCK vorhanden ist, wird alternativ zu 3. der ubi_add_notifier * benutzt. */ static unsigned int next_mtd_slot; static unsigned int next_filesystem_slot = MAX_AVM_ADDITIONAL_MTDS; struct avm_mtdram_data { char mapname[MAX_AVM_MTD_NAME_LENGTH]; struct resource resource; struct platdata_mtd_ram data; struct platform_device dev; }; static struct avm_mtdram_data avm_mtdram_data[NUM_AVM_MTDRAMS]; struct mtd_entry { char *urlader_name; char *runtime_name_0; char *runtime_name_1; }; struct mtd_entry mtd_names_ubi[] = { { "avm_filesys_0", "filesystem", "reserved-filesystem" }, { "avm_filesys_1", "reserved-filesystem", "filesystem" }, { "avm_config", "config", "config" }, { "avm_userdata", "nand-filesystem", "nand-filesystem" }, { NULL, NULL, NULL }, }; static int prioritize_ram = 0; extern int tffs_mtd[2]; extern int __init root_dev_setup(char *line); extern int __init ubi_mtd_param_parse(const char *val, struct kernel_param *kp); /* set_avm_mtdram_info */ int avm_mtd_set_mtdram_info(char *name, enum avm_mtd_group_selector group_selector, resource_size_t start, resource_size_t end, unsigned int flags) { size_t size; unsigned int dev_id; char numstr[10]; struct avm_mtdram_data *pmtdram_data; struct platform_device *pdev; struct platdata_mtd_ram *pdata; struct resource *res; dev_id = (group_selector == GROUP_FILESYSTEMS) ? (next_filesystem_slot++) : (next_mtd_slot++); if ((group_selector == GROUP_FILESYSTEMS) ? (dev_id >= NUM_AVM_MTDRAMS) : (dev_id >= MAX_AVM_ADDITIONAL_MTDS)) { return -ENOSPC; } pmtdram_data = &(avm_mtdram_data[dev_id]); pdev = &pmtdram_data->dev; pdata = &pmtdram_data->data; res = &pmtdram_data->resource; size = strlcpy(pmtdram_data->mapname, name, MAX_AVM_MTD_NAME_LENGTH); if (size > MAX_AVM_MTD_NAME_LENGTH) { return -EINVAL; } if (group_selector == GROUP_MTDS) { snprintf(numstr, sizeof(numstr), ".%x", dev_id); if (strlcat(pmtdram_data->mapname, numstr, MAX_AVM_MTD_NAME_LENGTH) > MAX_AVM_MTD_NAME_LENGTH) { return -EINVAL; } } pr_info("[%s] ram device name \"%s\"\n", __func__, pmtdram_data->mapname); res->start = start; res->end = end - 1; res->flags = flags; pr_info("[%s] registering ram device %d now\n", __func__, dev_id); pdata->mapname = pmtdram_data->mapname; pdata->bankwidth = 4; pdev->name = "mtd-ram"; pdev->id = dev_id; pdev->dev.platform_data = pdata; pdev->num_resources = 1; pdev->resource = res; return 0; } /* remove and reserve mem areas */ void avm_mtd_remove_and_reserve_mem_area(void) { int i; for (i = MAX_AVM_ADDITIONAL_MTDS; i < NUM_AVM_MTDRAMS; i++) { struct resource *res = &(avm_mtdram_data[i].resource); resource_size_t start = res->start; resource_size_t size = (res->end + 1) - start; if (res->end <= 0) { break; } pr_debug("[%s] mem_area start: 0x%x, size: 0x%x\n", __func__, start, size); memblock_remove(start, size); memblock_reserve(start, size); } } /* check for the partition name set by avmpart parser */ static int mtd_is_ram(struct mtd_info *mtd) { return (mtd && !strcmp(mtd->name, "rootfs_ram")); } /* identifies root device candidates by name */ static int mtd_is_rootfs(struct mtd_info *mtd) { const char *rootfs_names[] = { "filesystem", "rootfs_ram" }; int i; for(i = 0; i < (int)ARRAY_SIZE(rootfs_names); i++) { pr_debug("[%s] checking name \"%s\"\n", __func__, rootfs_names[i]); if (!strcmp(mtd->name, rootfs_names[i])) { return true; } } return false; } /* mount the root filesystem as if the root= kernel paramater were set */ static void announce_root(struct mtd_info *mtd) { char root_dev[64]; unsigned long ret; ret = snprintf(root_dev, sizeof(root_dev), "/dev/mtdblock%d", mtd->index); if (ret >= sizeof(root_dev)) { pr_emerg("[%s] Unable to generate root device name!\n", mtd->name); return; } pr_info("[%s] %s (%s) will be used as root device\n", __func__, mtd->name, root_dev); root_dev_setup(root_dev); } /* * search a name table for runtime MTD name. Returns original name if no * substitution is found */ static char *get_name(struct mtd_entry *nametable, const char *name) { unsigned int i; unsigned long linux_fs_start; char *p, *new_name; int res; linux_fs_start = 0; new_name = (char *)name; p = prom_getenv("linux_fs_start"); if (p != NULL) { res = kstrtoul(p, 0, &linux_fs_start); if (res == 0) { switch(linux_fs_start) { case 0: case 1: break; default: linux_fs_start = 0; } } } i = 0; while (nametable[i].urlader_name != NULL) { if (!strcmp(name, nametable[i].urlader_name)) { new_name = (linux_fs_start == 0) ? nametable[i].runtime_name_0 : nametable[i].runtime_name_1; break; } ++i; } return new_name; } void rename_ubi(struct mtd_info *mtd) { char *mtdname, *oldname; pr_debug("[%s] entering\n", __func__); if (!mtd->name) { pr_info("[%s] Called for unnamed mtd", __func__); return; } if (mtd->type != MTD_UBIVOLUME) { pr_debug("[%s] skipping mtd %s. Not an UBI volume\n", __func__, mtd->name); return; } mtdname = get_name(&mtd_names_ubi[0], mtd->name); if (mtdname != mtd->name) { // gluebi allocates name strings dynamiccaly and frees them on // volume removal oldname = (char *)mtd->name; mtd->name = kstrdup(mtdname, GFP_KERNEL); if (mtd->name != NULL) { pr_debug("[%s] renamed mtd %s -> %s\n", __func__, oldname, mtd->name); // FIXME: is it safe to free the string or should we just drop it? kfree(oldname); } else { mtd->name = oldname; pr_warning("[%s] Unable to rename mtd %s.\n", __func__, mtd->name); } } } /* called by the mtd driver whenever a new mtd device is being added */ void __init mtd_add_handler(struct mtd_info *mtd) { unsigned char *recover = NULL, *p = prom_getenv("firmware_info"); pr_debug("[%s] entered\n", __func__); /*--------------------------------------------------------------------------------* \ * beim recover soll das UBI gelöscht und neu angelegt werden, also wird bei * gesetzten recover nicht das UBI gemountet \*--------------------------------------------------------------------------------*/ if (p) recover = strstr(p, "recovered=2"); if (mtd->type == MTD_UBIVOLUME) rename_ubi(mtd); if (mtd_is_rootfs(mtd) && !prioritize_ram) { prioritize_ram = mtd_is_ram(mtd); announce_root(mtd); #if defined(CONFIG_TFFS) } else if (!strcmp(mtd->name, "nand-tffs")) { TFFS3_Register_NAND(mtd); pr_debug("[%s] tffs3 on MTD %s\n", __func__, mtd->name); #endif } else if (!strcmp(mtd->name, "ubi")) { if (!recover) { ubi_mtd_param_parse(mtd->name, NULL); pr_debug("[%s] UBI on MTD %s\n", __func__, mtd->name); } else { pr_debug(KERN_ERR "{%s} box recovered - %s\n", __func__, recover); } } else { pr_debug("[%s] mtd \"%s\" is not handled by me.\n", __func__, mtd->name); } } void mtd_rm_handler(struct mtd_info *mtd) { pr_debug("[%s] mtd \"%s\" passing by.\n", __func__, mtd->name); } static struct mtd_notifier mtd_notifier_ops = { .add = mtd_add_handler, .remove = mtd_rm_handler }; static struct mtd_notifier mtd_postinit_notifier_ops = { .add = rename_ubi, .remove = mtd_rm_handler }; static int register_ram_device(unsigned int dev_id, struct avm_mtdram_data *pmtdram_data) { int result = 0; struct platform_device *pdev = &pmtdram_data->dev; pr_info("[%s] registering ram device %d now\n", __func__, dev_id); result = platform_device_register(pdev); if (result) { pr_err("[%s] failed to register plat_mem device\n", __func__); } return result; } #if defined(CONFIG_MTD_UBI_BLOCK) /*------------------------------------------------------------------------------------------*\ * UBI volume notifier * Check volume name against runtime name and if it contains the current * root filesysten, register the associated ubiblock-dev as kernel root-dev \*------------------------------------------------------------------------------------------*/ void __init ubi_add_notifier(struct ubi_device_info *di __attribute__((unused)), struct ubi_volume_info *vi) { unsigned long ret; char root_dev[64]; char *name; // skip check if a RAM MTD has already been registered if (prioritize_ram) { return; } if (!vi->name) { pr_info("[%s] Called for unnamed volume", __func__); return; } // just a sanity check. There should not be a volume called "filesystem" if (!strcmp(vi->name, "filesystem")) { pr_warn("[%s] found UBI volume named \"filesystem\". Potential " "problems ahead!\n", __func__); return; } // register ubiblock-dev as kernel root-dev if this volume's runtime // name will be "filesystem" name = get_name(&mtd_names_ubi[0], vi->name); if (!strcmp(name, "filesystem")) { ret = snprintf(root_dev, sizeof(root_dev), "/dev/ubiblock%d_%d", vi->ubi_num, vi->vol_id); if (ret >= sizeof(root_dev)) { pr_emerg( "[%s] Unable to generate root device name /dev/ubiblock%d_%d!\n", name, vi->ubi_num, vi->vol_id); return; } pr_info("[%s] %s(%s) will be used as root device\n", __func__, root_dev, vi->name); root_dev_setup(root_dev); } else { pr_err("skip %s\n", vi->name); } } static int __init ubi_notify(struct notifier_block *nb __attribute__((unused)), unsigned long l, void *ns_ptr) { struct ubi_notification *nt = ns_ptr; switch(l) { case UBI_VOLUME_ADDED: pr_debug("[%s] new volume %s added\n", __func__, nt->vi.name); ubi_add_notifier(&nt->di, &nt->vi); break; case UBI_VOLUME_REMOVED: case UBI_VOLUME_RESIZED: case UBI_VOLUME_UPDATED: break; default: break; } return NOTIFY_OK; } static struct notifier_block ubi_notifier __initdata = { .notifier_call = ubi_notify, }; #endif static int __init avm_mtd_init(void) { unsigned int i; int result = 0; pr_debug("[%s] entered\n", __func__); register_mtd_user(&mtd_notifier_ops); #if defined(CONFIG_MTD_UBI_BLOCK) ubi_register_volume_notifier(&ubi_notifier, 1); #endif for (i = 0 ; i < NUM_AVM_MTDRAMS; i++) { struct resource *res = &(avm_mtdram_data[i].resource); if (res->end > 0) { pr_info("[%s] register_ram_device #%d from %x to %x\n", __func__, i, res->start, res->end); if (register_ram_device(i, &(avm_mtdram_data[i]))) { result = -ENODEV; pr_err("[%s] error while registering ram_device #%d\n", __func__, i); } } else { pr_info("[%s] skipping ram_device #%d\n", __func__, i); } } return result; } /* this is called from free_initmem as late_initcall is still too early */ void avm_mtd_cleanup(void) { pr_debug("[%s] entered\n", __func__); register_mtd_user(&mtd_postinit_notifier_ops); #if defined(CONFIG_MTD_UBI_BLOCK) ubi_unregister_volume_notifier(&ubi_notifier); #endif unregister_mtd_user(&mtd_notifier_ops); } subsys_initcall(avm_mtd_init);