📄 build.c
字号:
/* * Copyright (c) International Business Machines Corp., 2006 * Copyright (c) Nokia Corporation, 2007 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Artem Bityutskiy (Битюцкий Артём), * Frank Haverkamp *//* * This file includes UBI initialization and building of UBI devices. * * When UBI is initialized, it attaches all the MTD devices specified as the * module load parameters or the kernel boot parameters. If MTD devices were * specified, UBI does not attach any MTD device, but it is possible to do * later using the "UBI control device". * * At the moment we only attach UBI devices by scanning, which will become a * bottleneck when flashes reach certain large size. Then one may improve UBI * and add other methods, although it does not seem to be easy to do. */#include <linux/err.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/stringify.h>#include <linux/stat.h>#include <linux/miscdevice.h>#include <linux/log2.h>#include <linux/kthread.h>#include "ubi.h"/* Maximum length of the 'mtd=' parameter */#define MTD_PARAM_LEN_MAX 64/** * struct mtd_dev_param - MTD device parameter description data structure. * @name: MTD device name or number string * @vid_hdr_offs: VID header offset */struct mtd_dev_param { char name[MTD_PARAM_LEN_MAX]; int vid_hdr_offs;};/* Numbers of elements set in the @mtd_dev_param array */static int mtd_devs;/* MTD devices specification parameters */static struct mtd_dev_param mtd_dev_param[UBI_MAX_DEVICES];/* Root UBI "class" object (corresponds to '/<sysfs>/class/ubi/') */struct class *ubi_class;/* Slab cache for wear-leveling entries */struct kmem_cache *ubi_wl_entry_slab;/* UBI control character device */static struct miscdevice ubi_ctrl_cdev = { .minor = MISC_DYNAMIC_MINOR, .name = "ubi_ctrl", .fops = &ubi_ctrl_cdev_operations,};/* All UBI devices in system */static struct ubi_device *ubi_devices[UBI_MAX_DEVICES];/* Serializes UBI devices creations and removals */DEFINE_MUTEX(ubi_devices_mutex);/* Protects @ubi_devices and @ubi->ref_count */static DEFINE_SPINLOCK(ubi_devices_lock);/* "Show" method for files in '/<sysfs>/class/ubi/' */static ssize_t ubi_version_show(struct class *class, char *buf){ return sprintf(buf, "%d\n", UBI_VERSION);}/* UBI version attribute ('/<sysfs>/class/ubi/version') */static struct class_attribute ubi_version = __ATTR(version, S_IRUGO, ubi_version_show, NULL);static ssize_t dev_attribute_show(struct device *dev, struct device_attribute *attr, char *buf);/* UBI device attributes (correspond to files in '/<sysfs>/class/ubi/ubiX') */static struct device_attribute dev_eraseblock_size = __ATTR(eraseblock_size, S_IRUGO, dev_attribute_show, NULL);static struct device_attribute dev_avail_eraseblocks = __ATTR(avail_eraseblocks, S_IRUGO, dev_attribute_show, NULL);static struct device_attribute dev_total_eraseblocks = __ATTR(total_eraseblocks, S_IRUGO, dev_attribute_show, NULL);static struct device_attribute dev_volumes_count = __ATTR(volumes_count, S_IRUGO, dev_attribute_show, NULL);static struct device_attribute dev_max_ec = __ATTR(max_ec, S_IRUGO, dev_attribute_show, NULL);static struct device_attribute dev_reserved_for_bad = __ATTR(reserved_for_bad, S_IRUGO, dev_attribute_show, NULL);static struct device_attribute dev_bad_peb_count = __ATTR(bad_peb_count, S_IRUGO, dev_attribute_show, NULL);static struct device_attribute dev_max_vol_count = __ATTR(max_vol_count, S_IRUGO, dev_attribute_show, NULL);static struct device_attribute dev_min_io_size = __ATTR(min_io_size, S_IRUGO, dev_attribute_show, NULL);static struct device_attribute dev_bgt_enabled = __ATTR(bgt_enabled, S_IRUGO, dev_attribute_show, NULL);static struct device_attribute dev_mtd_num = __ATTR(mtd_num, S_IRUGO, dev_attribute_show, NULL);/** * ubi_get_device - get UBI device. * @ubi_num: UBI device number * * This function returns UBI device description object for UBI device number * @ubi_num, or %NULL if the device does not exist. This function increases the * device reference count to prevent removal of the device. In other words, the * device cannot be removed if its reference count is not zero. */struct ubi_device *ubi_get_device(int ubi_num){ struct ubi_device *ubi; spin_lock(&ubi_devices_lock); ubi = ubi_devices[ubi_num]; if (ubi) { ubi_assert(ubi->ref_count >= 0); ubi->ref_count += 1; get_device(&ubi->dev); } spin_unlock(&ubi_devices_lock); return ubi;}/** * ubi_put_device - drop an UBI device reference. * @ubi: UBI device description object */void ubi_put_device(struct ubi_device *ubi){ spin_lock(&ubi_devices_lock); ubi->ref_count -= 1; put_device(&ubi->dev); spin_unlock(&ubi_devices_lock);}/** * ubi_get_by_major - get UBI device by character device major number. * @major: major number * * This function is similar to 'ubi_get_device()', but it searches the device * by its major number. */struct ubi_device *ubi_get_by_major(int major){ int i; struct ubi_device *ubi; spin_lock(&ubi_devices_lock); for (i = 0; i < UBI_MAX_DEVICES; i++) { ubi = ubi_devices[i]; if (ubi && MAJOR(ubi->cdev.dev) == major) { ubi_assert(ubi->ref_count >= 0); ubi->ref_count += 1; get_device(&ubi->dev); spin_unlock(&ubi_devices_lock); return ubi; } } spin_unlock(&ubi_devices_lock); return NULL;}/** * ubi_major2num - get UBI device number by character device major number. * @major: major number * * This function searches UBI device number object by its major number. If UBI * device was not found, this function returns -ENODEV, otherwise the UBI device * number is returned. */int ubi_major2num(int major){ int i, ubi_num = -ENODEV; spin_lock(&ubi_devices_lock); for (i = 0; i < UBI_MAX_DEVICES; i++) { struct ubi_device *ubi = ubi_devices[i]; if (ubi && MAJOR(ubi->cdev.dev) == major) { ubi_num = ubi->ubi_num; break; } } spin_unlock(&ubi_devices_lock); return ubi_num;}/* "Show" method for files in '/<sysfs>/class/ubi/ubiX/' */static ssize_t dev_attribute_show(struct device *dev, struct device_attribute *attr, char *buf){ ssize_t ret; struct ubi_device *ubi; /* * The below code looks weird, but it actually makes sense. We get the * UBI device reference from the contained 'struct ubi_device'. But it * is unclear if the device was removed or not yet. Indeed, if the * device was removed before we increased its reference count, * 'ubi_get_device()' will return -ENODEV and we fail. * * Remember, 'struct ubi_device' is freed in the release function, so * we still can use 'ubi->ubi_num'. */ ubi = container_of(dev, struct ubi_device, dev); ubi = ubi_get_device(ubi->ubi_num); if (!ubi) return -ENODEV; if (attr == &dev_eraseblock_size) ret = sprintf(buf, "%d\n", ubi->leb_size); else if (attr == &dev_avail_eraseblocks) ret = sprintf(buf, "%d\n", ubi->avail_pebs); else if (attr == &dev_total_eraseblocks) ret = sprintf(buf, "%d\n", ubi->good_peb_count); else if (attr == &dev_volumes_count) ret = sprintf(buf, "%d\n", ubi->vol_count - UBI_INT_VOL_COUNT); else if (attr == &dev_max_ec) ret = sprintf(buf, "%d\n", ubi->max_ec); else if (attr == &dev_reserved_for_bad) ret = sprintf(buf, "%d\n", ubi->beb_rsvd_pebs); else if (attr == &dev_bad_peb_count) ret = sprintf(buf, "%d\n", ubi->bad_peb_count); else if (attr == &dev_max_vol_count) ret = sprintf(buf, "%d\n", ubi->vtbl_slots); else if (attr == &dev_min_io_size) ret = sprintf(buf, "%d\n", ubi->min_io_size); else if (attr == &dev_bgt_enabled) ret = sprintf(buf, "%d\n", ubi->thread_enabled); else if (attr == &dev_mtd_num) ret = sprintf(buf, "%d\n", ubi->mtd->index); else ret = -EINVAL; ubi_put_device(ubi); return ret;}/* Fake "release" method for UBI devices */static void dev_release(struct device *dev) { }/** * ubi_sysfs_init - initialize sysfs for an UBI device. * @ubi: UBI device description object * * This function returns zero in case of success and a negative error code in * case of failure. */static int ubi_sysfs_init(struct ubi_device *ubi){ int err; ubi->dev.release = dev_release; ubi->dev.devt = ubi->cdev.dev; ubi->dev.class = ubi_class; sprintf(&ubi->dev.bus_id[0], UBI_NAME_STR"%d", ubi->ubi_num); err = device_register(&ubi->dev); if (err) return err; err = device_create_file(&ubi->dev, &dev_eraseblock_size); if (err) return err; err = device_create_file(&ubi->dev, &dev_avail_eraseblocks); if (err) return err; err = device_create_file(&ubi->dev, &dev_total_eraseblocks); if (err) return err; err = device_create_file(&ubi->dev, &dev_volumes_count); if (err) return err; err = device_create_file(&ubi->dev, &dev_max_ec); if (err) return err; err = device_create_file(&ubi->dev, &dev_reserved_for_bad); if (err) return err; err = device_create_file(&ubi->dev, &dev_bad_peb_count); if (err) return err; err = device_create_file(&ubi->dev, &dev_max_vol_count); if (err) return err; err = device_create_file(&ubi->dev, &dev_min_io_size); if (err) return err; err = device_create_file(&ubi->dev, &dev_bgt_enabled); if (err) return err; err = device_create_file(&ubi->dev, &dev_mtd_num); return err;}/** * ubi_sysfs_close - close sysfs for an UBI device. * @ubi: UBI device description object */static void ubi_sysfs_close(struct ubi_device *ubi){ device_remove_file(&ubi->dev, &dev_mtd_num); device_remove_file(&ubi->dev, &dev_bgt_enabled); device_remove_file(&ubi->dev, &dev_min_io_size); device_remove_file(&ubi->dev, &dev_max_vol_count); device_remove_file(&ubi->dev, &dev_bad_peb_count); device_remove_file(&ubi->dev, &dev_reserved_for_bad); device_remove_file(&ubi->dev, &dev_max_ec); device_remove_file(&ubi->dev, &dev_volumes_count); device_remove_file(&ubi->dev, &dev_total_eraseblocks); device_remove_file(&ubi->dev, &dev_avail_eraseblocks); device_remove_file(&ubi->dev, &dev_eraseblock_size); device_unregister(&ubi->dev);}/** * kill_volumes - destroy all volumes. * @ubi: UBI device description object */static void kill_volumes(struct ubi_device *ubi){ int i; for (i = 0; i < ubi->vtbl_slots; i++) if (ubi->volumes[i]) ubi_free_volume(ubi, ubi->volumes[i]);}/** * free_user_volumes - free all user volumes. * @ubi: UBI device description object * * Normally the volumes are freed at the release function of the volume device * objects. However, on error paths the volumes have to be freed before the * device objects have been initialized. */static void free_user_volumes(struct ubi_device *ubi){ int i; for (i = 0; i < ubi->vtbl_slots; i++) if (ubi->volumes[i]) { kfree(ubi->volumes[i]->eba_tbl); kfree(ubi->volumes[i]); }}/** * uif_init - initialize user interfaces for an UBI device. * @ubi: UBI device description object * * This function returns zero in case of success and a negative error code in * case of failure. Note, this function destroys all volumes if it failes. */static int uif_init(struct ubi_device *ubi){ int i, err, do_free = 0; dev_t dev; sprintf(ubi->ubi_name, UBI_NAME_STR "%d", ubi->ubi_num); /* * Major numbers for the UBI character devices are allocated * dynamically. Major numbers of volume character devices are * equivalent to ones of the corresponding UBI character device. Minor * numbers of UBI character devices are 0, while minor numbers of * volume character devices start from 1. Thus, we allocate one major * number and ubi->vtbl_slots + 1 minor numbers. */ err = alloc_chrdev_region(&dev, 0, ubi->vtbl_slots + 1, ubi->ubi_name); if (err) { ubi_err("cannot register UBI character devices"); return err; } ubi_assert(MINOR(dev) == 0); cdev_init(&ubi->cdev, &ubi_cdev_operations); dbg_gen("%s major is %u", ubi->ubi_name, MAJOR(dev)); ubi->cdev.owner = THIS_MODULE;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -