|
@@ -28,6 +28,7 @@
|
|
|
#include <linux/hdreg.h>
|
|
|
#include <linux/kdev_t.h>
|
|
|
#include <linux/blkdev.h>
|
|
|
+#include <linux/cdev.h>
|
|
|
#include <linux/mutex.h>
|
|
|
#include <linux/scatterlist.h>
|
|
|
#include <linux/string_helpers.h>
|
|
@@ -86,6 +87,7 @@ static int max_devices;
|
|
|
#define MAX_DEVICES 256
|
|
|
|
|
|
static DEFINE_IDA(mmc_blk_ida);
|
|
|
+static DEFINE_IDA(mmc_rpmb_ida);
|
|
|
|
|
|
/*
|
|
|
* There is one mmc_blk_data per slot.
|
|
@@ -96,6 +98,7 @@ struct mmc_blk_data {
|
|
|
struct gendisk *disk;
|
|
|
struct mmc_queue queue;
|
|
|
struct list_head part;
|
|
|
+ struct list_head rpmbs;
|
|
|
|
|
|
unsigned int flags;
|
|
|
#define MMC_BLK_CMD23 (1 << 0) /* Can do SET_BLOCK_COUNT for multiblock */
|
|
@@ -121,6 +124,32 @@ struct mmc_blk_data {
|
|
|
int area_type;
|
|
|
};
|
|
|
|
|
|
+/* Device type for RPMB character devices */
|
|
|
+static dev_t mmc_rpmb_devt;
|
|
|
+
|
|
|
+/* Bus type for RPMB character devices */
|
|
|
+static struct bus_type mmc_rpmb_bus_type = {
|
|
|
+ .name = "mmc_rpmb",
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * struct mmc_rpmb_data - special RPMB device type for these areas
|
|
|
+ * @dev: the device for the RPMB area
|
|
|
+ * @chrdev: character device for the RPMB area
|
|
|
+ * @id: unique device ID number
|
|
|
+ * @part_index: partition index (0 on first)
|
|
|
+ * @md: parent MMC block device
|
|
|
+ * @node: list item, so we can put this device on a list
|
|
|
+ */
|
|
|
+struct mmc_rpmb_data {
|
|
|
+ struct device dev;
|
|
|
+ struct cdev chrdev;
|
|
|
+ int id;
|
|
|
+ unsigned int part_index;
|
|
|
+ struct mmc_blk_data *md;
|
|
|
+ struct list_head node;
|
|
|
+};
|
|
|
+
|
|
|
static DEFINE_MUTEX(open_lock);
|
|
|
|
|
|
module_param(perdev_minors, int, 0444);
|
|
@@ -299,6 +328,7 @@ struct mmc_blk_ioc_data {
|
|
|
struct mmc_ioc_cmd ic;
|
|
|
unsigned char *buf;
|
|
|
u64 buf_bytes;
|
|
|
+ struct mmc_rpmb_data *rpmb;
|
|
|
};
|
|
|
|
|
|
static struct mmc_blk_ioc_data *mmc_blk_ioctl_copy_from_user(
|
|
@@ -437,14 +467,25 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
|
|
|
struct mmc_request mrq = {};
|
|
|
struct scatterlist sg;
|
|
|
int err;
|
|
|
- bool is_rpmb = false;
|
|
|
+ unsigned int target_part;
|
|
|
u32 status = 0;
|
|
|
|
|
|
if (!card || !md || !idata)
|
|
|
return -EINVAL;
|
|
|
|
|
|
- if (md->area_type & MMC_BLK_DATA_AREA_RPMB)
|
|
|
- is_rpmb = true;
|
|
|
+ /*
|
|
|
+ * The RPMB accesses comes in from the character device, so we
|
|
|
+ * need to target these explicitly. Else we just target the
|
|
|
+ * partition type for the block device the ioctl() was issued
|
|
|
+ * on.
|
|
|
+ */
|
|
|
+ if (idata->rpmb) {
|
|
|
+ /* Support multiple RPMB partitions */
|
|
|
+ target_part = idata->rpmb->part_index;
|
|
|
+ target_part |= EXT_CSD_PART_CONFIG_ACC_RPMB;
|
|
|
+ } else {
|
|
|
+ target_part = md->part_type;
|
|
|
+ }
|
|
|
|
|
|
cmd.opcode = idata->ic.opcode;
|
|
|
cmd.arg = idata->ic.arg;
|
|
@@ -488,7 +529,7 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
|
|
|
|
|
|
mrq.cmd = &cmd;
|
|
|
|
|
|
- err = mmc_blk_part_switch(card, md->part_type);
|
|
|
+ err = mmc_blk_part_switch(card, target_part);
|
|
|
if (err)
|
|
|
return err;
|
|
|
|
|
@@ -498,7 +539,7 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
- if (is_rpmb) {
|
|
|
+ if (idata->rpmb) {
|
|
|
err = mmc_set_blockcount(card, data.blocks,
|
|
|
idata->ic.write_flag & (1 << 31));
|
|
|
if (err)
|
|
@@ -538,7 +579,7 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
|
|
|
|
|
|
memcpy(&(idata->ic.response), cmd.resp, sizeof(cmd.resp));
|
|
|
|
|
|
- if (is_rpmb) {
|
|
|
+ if (idata->rpmb) {
|
|
|
/*
|
|
|
* Ensure RPMB command has completed by polling CMD13
|
|
|
* "Send Status".
|
|
@@ -554,7 +595,8 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
|
|
|
}
|
|
|
|
|
|
static int mmc_blk_ioctl_cmd(struct mmc_blk_data *md,
|
|
|
- struct mmc_ioc_cmd __user *ic_ptr)
|
|
|
+ struct mmc_ioc_cmd __user *ic_ptr,
|
|
|
+ struct mmc_rpmb_data *rpmb)
|
|
|
{
|
|
|
struct mmc_blk_ioc_data *idata;
|
|
|
struct mmc_blk_ioc_data *idatas[1];
|
|
@@ -566,6 +608,8 @@ static int mmc_blk_ioctl_cmd(struct mmc_blk_data *md,
|
|
|
idata = mmc_blk_ioctl_copy_from_user(ic_ptr);
|
|
|
if (IS_ERR(idata))
|
|
|
return PTR_ERR(idata);
|
|
|
+ /* This will be NULL on non-RPMB ioctl():s */
|
|
|
+ idata->rpmb = rpmb;
|
|
|
|
|
|
card = md->queue.card;
|
|
|
if (IS_ERR(card)) {
|
|
@@ -581,7 +625,8 @@ static int mmc_blk_ioctl_cmd(struct mmc_blk_data *md,
|
|
|
idata->ic.write_flag ? REQ_OP_DRV_OUT : REQ_OP_DRV_IN,
|
|
|
__GFP_RECLAIM);
|
|
|
idatas[0] = idata;
|
|
|
- req_to_mmc_queue_req(req)->drv_op = MMC_DRV_OP_IOCTL;
|
|
|
+ req_to_mmc_queue_req(req)->drv_op =
|
|
|
+ rpmb ? MMC_DRV_OP_IOCTL_RPMB : MMC_DRV_OP_IOCTL;
|
|
|
req_to_mmc_queue_req(req)->drv_op_data = idatas;
|
|
|
req_to_mmc_queue_req(req)->ioc_count = 1;
|
|
|
blk_execute_rq(mq->queue, NULL, req, 0);
|
|
@@ -596,7 +641,8 @@ cmd_done:
|
|
|
}
|
|
|
|
|
|
static int mmc_blk_ioctl_multi_cmd(struct mmc_blk_data *md,
|
|
|
- struct mmc_ioc_multi_cmd __user *user)
|
|
|
+ struct mmc_ioc_multi_cmd __user *user,
|
|
|
+ struct mmc_rpmb_data *rpmb)
|
|
|
{
|
|
|
struct mmc_blk_ioc_data **idata = NULL;
|
|
|
struct mmc_ioc_cmd __user *cmds = user->cmds;
|
|
@@ -627,6 +673,8 @@ static int mmc_blk_ioctl_multi_cmd(struct mmc_blk_data *md,
|
|
|
num_of_cmds = i;
|
|
|
goto cmd_err;
|
|
|
}
|
|
|
+ /* This will be NULL on non-RPMB ioctl():s */
|
|
|
+ idata[i]->rpmb = rpmb;
|
|
|
}
|
|
|
|
|
|
card = md->queue.card;
|
|
@@ -643,7 +691,8 @@ static int mmc_blk_ioctl_multi_cmd(struct mmc_blk_data *md,
|
|
|
req = blk_get_request(mq->queue,
|
|
|
idata[0]->ic.write_flag ? REQ_OP_DRV_OUT : REQ_OP_DRV_IN,
|
|
|
__GFP_RECLAIM);
|
|
|
- req_to_mmc_queue_req(req)->drv_op = MMC_DRV_OP_IOCTL;
|
|
|
+ req_to_mmc_queue_req(req)->drv_op =
|
|
|
+ rpmb ? MMC_DRV_OP_IOCTL_RPMB : MMC_DRV_OP_IOCTL;
|
|
|
req_to_mmc_queue_req(req)->drv_op_data = idata;
|
|
|
req_to_mmc_queue_req(req)->ioc_count = num_of_cmds;
|
|
|
blk_execute_rq(mq->queue, NULL, req, 0);
|
|
@@ -691,7 +740,8 @@ static int mmc_blk_ioctl(struct block_device *bdev, fmode_t mode,
|
|
|
if (!md)
|
|
|
return -EINVAL;
|
|
|
ret = mmc_blk_ioctl_cmd(md,
|
|
|
- (struct mmc_ioc_cmd __user *)arg);
|
|
|
+ (struct mmc_ioc_cmd __user *)arg,
|
|
|
+ NULL);
|
|
|
mmc_blk_put(md);
|
|
|
return ret;
|
|
|
case MMC_IOC_MULTI_CMD:
|
|
@@ -702,7 +752,8 @@ static int mmc_blk_ioctl(struct block_device *bdev, fmode_t mode,
|
|
|
if (!md)
|
|
|
return -EINVAL;
|
|
|
ret = mmc_blk_ioctl_multi_cmd(md,
|
|
|
- (struct mmc_ioc_multi_cmd __user *)arg);
|
|
|
+ (struct mmc_ioc_multi_cmd __user *)arg,
|
|
|
+ NULL);
|
|
|
mmc_blk_put(md);
|
|
|
return ret;
|
|
|
default:
|
|
@@ -1174,17 +1225,19 @@ static void mmc_blk_issue_drv_op(struct mmc_queue *mq, struct request *req)
|
|
|
struct mmc_queue_req *mq_rq;
|
|
|
struct mmc_card *card = mq->card;
|
|
|
struct mmc_blk_data *md = mq->blkdata;
|
|
|
- struct mmc_blk_data *main_md = dev_get_drvdata(&card->dev);
|
|
|
struct mmc_blk_ioc_data **idata;
|
|
|
+ bool rpmb_ioctl;
|
|
|
u8 **ext_csd;
|
|
|
u32 status;
|
|
|
int ret;
|
|
|
int i;
|
|
|
|
|
|
mq_rq = req_to_mmc_queue_req(req);
|
|
|
+ rpmb_ioctl = (mq_rq->drv_op == MMC_DRV_OP_IOCTL_RPMB);
|
|
|
|
|
|
switch (mq_rq->drv_op) {
|
|
|
case MMC_DRV_OP_IOCTL:
|
|
|
+ case MMC_DRV_OP_IOCTL_RPMB:
|
|
|
idata = mq_rq->drv_op_data;
|
|
|
for (i = 0, ret = 0; i < mq_rq->ioc_count; i++) {
|
|
|
ret = __mmc_blk_ioctl_cmd(card, md, idata[i]);
|
|
@@ -1192,8 +1245,8 @@ static void mmc_blk_issue_drv_op(struct mmc_queue *mq, struct request *req)
|
|
|
break;
|
|
|
}
|
|
|
/* Always switch back to main area after RPMB access */
|
|
|
- if (md->area_type & MMC_BLK_DATA_AREA_RPMB)
|
|
|
- mmc_blk_part_switch(card, main_md->part_type);
|
|
|
+ if (rpmb_ioctl)
|
|
|
+ mmc_blk_part_switch(card, 0);
|
|
|
break;
|
|
|
case MMC_DRV_OP_BOOT_WP:
|
|
|
ret = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BOOT_WP,
|
|
@@ -2068,6 +2121,7 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
|
|
|
|
|
|
spin_lock_init(&md->lock);
|
|
|
INIT_LIST_HEAD(&md->part);
|
|
|
+ INIT_LIST_HEAD(&md->rpmbs);
|
|
|
md->usage = 1;
|
|
|
|
|
|
ret = mmc_init_queue(&md->queue, card, &md->lock, subname);
|
|
@@ -2186,6 +2240,154 @@ static int mmc_blk_alloc_part(struct mmc_card *card,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * mmc_rpmb_ioctl() - ioctl handler for the RPMB chardev
|
|
|
+ * @filp: the character device file
|
|
|
+ * @cmd: the ioctl() command
|
|
|
+ * @arg: the argument from userspace
|
|
|
+ *
|
|
|
+ * This will essentially just redirect the ioctl()s coming in over to
|
|
|
+ * the main block device spawning the RPMB character device.
|
|
|
+ */
|
|
|
+static long mmc_rpmb_ioctl(struct file *filp, unsigned int cmd,
|
|
|
+ unsigned long arg)
|
|
|
+{
|
|
|
+ struct mmc_rpmb_data *rpmb = filp->private_data;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ switch (cmd) {
|
|
|
+ case MMC_IOC_CMD:
|
|
|
+ ret = mmc_blk_ioctl_cmd(rpmb->md,
|
|
|
+ (struct mmc_ioc_cmd __user *)arg,
|
|
|
+ rpmb);
|
|
|
+ break;
|
|
|
+ case MMC_IOC_MULTI_CMD:
|
|
|
+ ret = mmc_blk_ioctl_multi_cmd(rpmb->md,
|
|
|
+ (struct mmc_ioc_multi_cmd __user *)arg,
|
|
|
+ rpmb);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ ret = -EINVAL;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+#ifdef CONFIG_COMPAT
|
|
|
+static long mmc_rpmb_ioctl_compat(struct file *filp, unsigned int cmd,
|
|
|
+ unsigned long arg)
|
|
|
+{
|
|
|
+ return mmc_rpmb_ioctl(filp, cmd, (unsigned long)compat_ptr(arg));
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
+static int mmc_rpmb_chrdev_open(struct inode *inode, struct file *filp)
|
|
|
+{
|
|
|
+ struct mmc_rpmb_data *rpmb = container_of(inode->i_cdev,
|
|
|
+ struct mmc_rpmb_data, chrdev);
|
|
|
+
|
|
|
+ get_device(&rpmb->dev);
|
|
|
+ filp->private_data = rpmb;
|
|
|
+ mutex_lock(&open_lock);
|
|
|
+ rpmb->md->usage++;
|
|
|
+ mutex_unlock(&open_lock);
|
|
|
+
|
|
|
+ return nonseekable_open(inode, filp);
|
|
|
+}
|
|
|
+
|
|
|
+static int mmc_rpmb_chrdev_release(struct inode *inode, struct file *filp)
|
|
|
+{
|
|
|
+ struct mmc_rpmb_data *rpmb = container_of(inode->i_cdev,
|
|
|
+ struct mmc_rpmb_data, chrdev);
|
|
|
+
|
|
|
+ put_device(&rpmb->dev);
|
|
|
+ mutex_lock(&open_lock);
|
|
|
+ rpmb->md->usage--;
|
|
|
+ mutex_unlock(&open_lock);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static const struct file_operations mmc_rpmb_fileops = {
|
|
|
+ .release = mmc_rpmb_chrdev_release,
|
|
|
+ .open = mmc_rpmb_chrdev_open,
|
|
|
+ .owner = THIS_MODULE,
|
|
|
+ .llseek = no_llseek,
|
|
|
+ .unlocked_ioctl = mmc_rpmb_ioctl,
|
|
|
+#ifdef CONFIG_COMPAT
|
|
|
+ .compat_ioctl = mmc_rpmb_ioctl_compat,
|
|
|
+#endif
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+static int mmc_blk_alloc_rpmb_part(struct mmc_card *card,
|
|
|
+ struct mmc_blk_data *md,
|
|
|
+ unsigned int part_index,
|
|
|
+ sector_t size,
|
|
|
+ const char *subname)
|
|
|
+{
|
|
|
+ int devidx, ret;
|
|
|
+ char rpmb_name[DISK_NAME_LEN];
|
|
|
+ char cap_str[10];
|
|
|
+ struct mmc_rpmb_data *rpmb;
|
|
|
+
|
|
|
+ /* This creates the minor number for the RPMB char device */
|
|
|
+ devidx = ida_simple_get(&mmc_rpmb_ida, 0, max_devices, GFP_KERNEL);
|
|
|
+ if (devidx < 0)
|
|
|
+ return devidx;
|
|
|
+
|
|
|
+ rpmb = kzalloc(sizeof(*rpmb), GFP_KERNEL);
|
|
|
+ if (!rpmb)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ snprintf(rpmb_name, sizeof(rpmb_name),
|
|
|
+ "mmcblk%u%s", card->host->index, subname ? subname : "");
|
|
|
+
|
|
|
+ rpmb->id = devidx;
|
|
|
+ rpmb->part_index = part_index;
|
|
|
+ rpmb->dev.init_name = rpmb_name;
|
|
|
+ rpmb->dev.bus = &mmc_rpmb_bus_type;
|
|
|
+ rpmb->dev.devt = MKDEV(MAJOR(mmc_rpmb_devt), rpmb->id);
|
|
|
+ rpmb->dev.parent = &card->dev;
|
|
|
+ device_initialize(&rpmb->dev);
|
|
|
+ dev_set_drvdata(&rpmb->dev, rpmb);
|
|
|
+ rpmb->md = md;
|
|
|
+
|
|
|
+ cdev_init(&rpmb->chrdev, &mmc_rpmb_fileops);
|
|
|
+ rpmb->chrdev.owner = THIS_MODULE;
|
|
|
+ ret = cdev_device_add(&rpmb->chrdev, &rpmb->dev);
|
|
|
+ if (ret) {
|
|
|
+ pr_err("%s: could not add character device\n", rpmb_name);
|
|
|
+ goto out_remove_ida;
|
|
|
+ }
|
|
|
+
|
|
|
+ list_add(&rpmb->node, &md->rpmbs);
|
|
|
+
|
|
|
+ string_get_size((u64)size, 512, STRING_UNITS_2,
|
|
|
+ cap_str, sizeof(cap_str));
|
|
|
+
|
|
|
+ pr_info("%s: %s %s partition %u %s, chardev (%d:%d)\n",
|
|
|
+ rpmb_name, mmc_card_id(card),
|
|
|
+ mmc_card_name(card), EXT_CSD_PART_CONFIG_ACC_RPMB, cap_str,
|
|
|
+ MAJOR(mmc_rpmb_devt), rpmb->id);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+
|
|
|
+out_remove_ida:
|
|
|
+ ida_simple_remove(&mmc_rpmb_ida, rpmb->id);
|
|
|
+ kfree(rpmb);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static void mmc_blk_remove_rpmb_part(struct mmc_rpmb_data *rpmb)
|
|
|
+{
|
|
|
+ cdev_device_del(&rpmb->chrdev, &rpmb->dev);
|
|
|
+ device_del(&rpmb->dev);
|
|
|
+ ida_simple_remove(&mmc_rpmb_ida, rpmb->id);
|
|
|
+ kfree(rpmb);
|
|
|
+}
|
|
|
+
|
|
|
/* MMC Physical partitions consist of two boot partitions and
|
|
|
* up to four general purpose partitions.
|
|
|
* For each partition enabled in EXT_CSD a block device will be allocatedi
|
|
@@ -2194,13 +2396,26 @@ static int mmc_blk_alloc_part(struct mmc_card *card,
|
|
|
|
|
|
static int mmc_blk_alloc_parts(struct mmc_card *card, struct mmc_blk_data *md)
|
|
|
{
|
|
|
- int idx, ret = 0;
|
|
|
+ int idx, ret;
|
|
|
|
|
|
if (!mmc_card_mmc(card))
|
|
|
return 0;
|
|
|
|
|
|
for (idx = 0; idx < card->nr_parts; idx++) {
|
|
|
- if (card->part[idx].size) {
|
|
|
+ if (card->part[idx].area_type & MMC_BLK_DATA_AREA_RPMB) {
|
|
|
+ /*
|
|
|
+ * RPMB partitions does not provide block access, they
|
|
|
+ * are only accessed using ioctl():s. Thus create
|
|
|
+ * special RPMB block devices that do not have a
|
|
|
+ * backing block queue for these.
|
|
|
+ */
|
|
|
+ ret = mmc_blk_alloc_rpmb_part(card, md,
|
|
|
+ card->part[idx].part_cfg,
|
|
|
+ card->part[idx].size >> 9,
|
|
|
+ card->part[idx].name);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+ } else if (card->part[idx].size) {
|
|
|
ret = mmc_blk_alloc_part(card, md,
|
|
|
card->part[idx].part_cfg,
|
|
|
card->part[idx].size >> 9,
|
|
@@ -2212,7 +2427,7 @@ static int mmc_blk_alloc_parts(struct mmc_card *card, struct mmc_blk_data *md)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- return ret;
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
static void mmc_blk_remove_req(struct mmc_blk_data *md)
|
|
@@ -2249,7 +2464,15 @@ static void mmc_blk_remove_parts(struct mmc_card *card,
|
|
|
{
|
|
|
struct list_head *pos, *q;
|
|
|
struct mmc_blk_data *part_md;
|
|
|
+ struct mmc_rpmb_data *rpmb;
|
|
|
|
|
|
+ /* Remove RPMB partitions */
|
|
|
+ list_for_each_safe(pos, q, &md->rpmbs) {
|
|
|
+ rpmb = list_entry(pos, struct mmc_rpmb_data, node);
|
|
|
+ list_del(pos);
|
|
|
+ mmc_blk_remove_rpmb_part(rpmb);
|
|
|
+ }
|
|
|
+ /* Remove block partitions */
|
|
|
list_for_each_safe(pos, q, &md->part) {
|
|
|
part_md = list_entry(pos, struct mmc_blk_data, part);
|
|
|
list_del(pos);
|
|
@@ -2568,6 +2791,17 @@ static int __init mmc_blk_init(void)
|
|
|
{
|
|
|
int res;
|
|
|
|
|
|
+ res = bus_register(&mmc_rpmb_bus_type);
|
|
|
+ if (res < 0) {
|
|
|
+ pr_err("mmcblk: could not register RPMB bus type\n");
|
|
|
+ return res;
|
|
|
+ }
|
|
|
+ res = alloc_chrdev_region(&mmc_rpmb_devt, 0, MAX_DEVICES, "rpmb");
|
|
|
+ if (res < 0) {
|
|
|
+ pr_err("mmcblk: failed to allocate rpmb chrdev region\n");
|
|
|
+ goto out_bus_unreg;
|
|
|
+ }
|
|
|
+
|
|
|
if (perdev_minors != CONFIG_MMC_BLOCK_MINORS)
|
|
|
pr_info("mmcblk: using %d minors per device\n", perdev_minors);
|
|
|
|
|
@@ -2575,16 +2809,20 @@ static int __init mmc_blk_init(void)
|
|
|
|
|
|
res = register_blkdev(MMC_BLOCK_MAJOR, "mmc");
|
|
|
if (res)
|
|
|
- goto out;
|
|
|
+ goto out_chrdev_unreg;
|
|
|
|
|
|
res = mmc_register_driver(&mmc_driver);
|
|
|
if (res)
|
|
|
- goto out2;
|
|
|
+ goto out_blkdev_unreg;
|
|
|
|
|
|
return 0;
|
|
|
- out2:
|
|
|
+
|
|
|
+out_blkdev_unreg:
|
|
|
unregister_blkdev(MMC_BLOCK_MAJOR, "mmc");
|
|
|
- out:
|
|
|
+out_chrdev_unreg:
|
|
|
+ unregister_chrdev_region(mmc_rpmb_devt, MAX_DEVICES);
|
|
|
+out_bus_unreg:
|
|
|
+ bus_unregister(&mmc_rpmb_bus_type);
|
|
|
return res;
|
|
|
}
|
|
|
|
|
@@ -2592,6 +2830,7 @@ static void __exit mmc_blk_exit(void)
|
|
|
{
|
|
|
mmc_unregister_driver(&mmc_driver);
|
|
|
unregister_blkdev(MMC_BLOCK_MAJOR, "mmc");
|
|
|
+ unregister_chrdev_region(mmc_rpmb_devt, MAX_DEVICES);
|
|
|
}
|
|
|
|
|
|
module_init(mmc_blk_init);
|