diff options
author | Seshagiri Holi <sholi@nvidia.com> | 2013-11-04 17:27:43 +0530 |
---|---|---|
committer | Bharat Nihalani <bnihalani@nvidia.com> | 2014-01-06 00:58:25 -0800 |
commit | a8eef868e27eb50f394365b84191eb4dd807b611 (patch) | |
tree | 1b58ba995579af6cbad0957498b5a9b4b8bee5f4 /drivers/mmc/card | |
parent | 6f10f486f8411f0860681ceae79e361c4e49d62f (diff) |
driver: mmc: Add new ioctl to send combo commands
Add ioctl for sending a sequence of commands to the device, atomically.
This combo command (MMC_COMBO_IOC_CMD) is a array of mmc_ioc_cmd.
The user space application can send an array
(mmc_ioc_cmd) of MMC commands.
To ensure atomic operations of all commands
in array, a single lock is acquired.
This is required for user-space tools to issue refresh
and check device's health and status.
bug 1304972
Change-Id: I340b9a95e6471ee578e404a7b01eec3db02662d0
Reviewed-on: http://git-master/r/281699
(cherry picked from commit 8945e061c4c5b26bb17c6e2d7ab610a056e7011f)
Reviewed-on: http://git-master/r/302096
Signed-off-by: Seshagiri Holi <sholi@nvidia.com>
Reviewed-on: http://git-master/r/337181
Reviewed-by: Pavan Kunapuli <pkunapuli@nvidia.com>
Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
Diffstat (limited to 'drivers/mmc/card')
-rw-r--r-- | drivers/mmc/card/block.c | 225 |
1 files changed, 185 insertions, 40 deletions
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 93b9e202bce1..af4c62f99a12 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -3,6 +3,7 @@ * * Copyright 2002 Hewlett-Packard Company * Copyright 2005-2008 Pierre Ossman + * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. * * Use consistent with the GNU GPL is permitted, * provided that this copyright notice is @@ -375,6 +376,30 @@ out: return ERR_PTR(err); } +static int mmc_blk_ioctl_copy_to_user( + struct mmc_ioc_cmd __user *ic_ptr, + struct mmc_blk_ioc_data *idata) +{ + int err = 0; + + if (copy_to_user(&(ic_ptr->response), idata->ic.response, + sizeof(idata->ic.response))) { + err = -EFAULT; + goto out; + } + if (!idata->ic.write_flag) { + if (copy_to_user((void __user *) + (unsigned long) idata->ic.data_ptr, + idata->buf, idata->buf_bytes)) { + err = -EFAULT; + goto out; + } + } +out: + return err; + +} + static int ioctl_rpmb_card_status_poll(struct mmc_card *card, u32 *status, u32 retries_max) { @@ -407,11 +432,9 @@ static int ioctl_rpmb_card_status_poll(struct mmc_card *card, u32 *status, return err; } -static int mmc_blk_ioctl_cmd(struct block_device *bdev, - struct mmc_ioc_cmd __user *ic_ptr) +static int _mmc_blk_ioctl_cmd_locked(struct mmc_blk_data *md, + struct mmc_blk_ioc_data *idata) { - struct mmc_blk_ioc_data *idata; - struct mmc_blk_data *md; struct mmc_card *card; struct mmc_command cmd = {0}; struct mmc_data data = {0}; @@ -421,19 +444,6 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev, int is_rpmb = false; u32 status = 0; - /* - * The caller must have CAP_SYS_RAWIO, and must be calling this on the - * whole block device, not on a partition. This prevents overspray - * between sibling partitions. - */ - if ((!capable(CAP_SYS_RAWIO)) || (bdev != bdev->bd_contains)) - return -EPERM; - - idata = mmc_blk_ioctl_copy_from_user(ic_ptr); - if (IS_ERR(idata)) - return PTR_ERR(idata); - - md = mmc_blk_get(bdev->bd_disk); if (!md) { err = -EINVAL; goto cmd_err; @@ -445,7 +455,7 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev, card = md->queue.card; if (IS_ERR(card)) { err = PTR_ERR(card); - goto cmd_done; + goto cmd_err; } cmd.opcode = idata->ic.opcode; @@ -490,23 +500,21 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev, mrq.cmd = &cmd; - mmc_claim_host(card->host); - err = mmc_blk_part_switch(card, md); if (err) - goto cmd_rel_host; + goto cmd_err; if (idata->ic.is_acmd) { err = mmc_app_cmd(card->host, card); if (err) - goto cmd_rel_host; + goto cmd_err; } if (is_rpmb) { err = mmc_set_blockcount(card, data.blocks, idata->ic.write_flag & (1 << 31)); if (err) - goto cmd_rel_host; + goto cmd_err; } mmc_wait_for_req(card->host, &mrq); @@ -515,13 +523,13 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev, dev_err(mmc_dev(card->host), "%s: cmd error %d\n", __func__, cmd.error); err = cmd.error; - goto cmd_rel_host; + goto cmd_err; } if (data.error) { dev_err(mmc_dev(card->host), "%s: data error %d\n", __func__, data.error); err = data.error; - goto cmd_rel_host; + goto cmd_err; } /* @@ -531,18 +539,7 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev, if (idata->ic.postsleep_min_us) usleep_range(idata->ic.postsleep_min_us, idata->ic.postsleep_max_us); - if (copy_to_user(&(ic_ptr->response), cmd.resp, sizeof(cmd.resp))) { - err = -EFAULT; - goto cmd_rel_host; - } - - if (!idata->ic.write_flag) { - if (copy_to_user((void __user *)(unsigned long) idata->ic.data_ptr, - idata->buf, idata->buf_bytes)) { - err = -EFAULT; - goto cmd_rel_host; - } - } + memcpy(&(idata->ic.response), cmd.resp, sizeof(cmd.resp)); if (is_rpmb) { /* @@ -555,10 +552,50 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev, "%s: Card Status=0x%08X, error %d\n", __func__, status, err); } +cmd_err: + return err; +} + +static int mmc_blk_ioctl_cmd(struct block_device *bdev, + struct mmc_ioc_cmd __user *ic_ptr) +{ + struct mmc_blk_ioc_data *idata; + struct mmc_blk_data *md; + struct mmc_card *card; + int err; + + /* + * The caller must have CAP_SYS_RAWIO, and must be calling this on the + * whole block device, not on a partition. This prevents overspray + * between sibling partitions. + */ + if ((!capable(CAP_SYS_RAWIO)) || (bdev != bdev->bd_contains)) + return -EPERM; + + idata = mmc_blk_ioctl_copy_from_user(ic_ptr); + if (IS_ERR(idata)) + return PTR_ERR(idata); + + md = mmc_blk_get(bdev->bd_disk); + if (!md) { + err = -EINVAL; + goto cmd_err; + } + + card = md->queue.card; + if (IS_ERR(card)) { + err = PTR_ERR(card); + goto cmd_done; + } + + mmc_claim_host(card->host); + + err = _mmc_blk_ioctl_cmd_locked(md, idata); -cmd_rel_host: mmc_release_host(card->host); + mmc_blk_ioctl_copy_to_user(ic_ptr, idata); + cmd_done: mmc_blk_put(md); cmd_err: @@ -567,12 +604,120 @@ cmd_err: return err; } +static int mmc_blk_ioctl_combo_cmd(struct block_device *bdev, + struct mmc_combo_cmd_info __user *user) +{ + int err, i; + struct mmc_combo_cmd_info mcci = {0}; + u8 num_cmd; + struct mmc_blk_ioc_data **ioc_data = NULL; + u32 usr_ptr; + struct mmc_card *card; + struct mmc_blk_data *md; + + /* + * The caller must have CAP_SYS_RAWIO, and must be calling this on the + * whole block device, not on a partition. This prevents overspray + * between sibling partitions. + */ + if ((!capable(CAP_SYS_RAWIO)) || (bdev != bdev->bd_contains)) + return -EPERM; + + md = mmc_blk_get(bdev->bd_disk); + if (!md) { + err = -EINVAL; + goto out; + } + + card = md->queue.card; + if (IS_ERR(card)) { + err = PTR_ERR(card); + goto cmd_done; + } + + if (copy_from_user(&mcci, user, sizeof(struct mmc_combo_cmd_info))) { + err = -EFAULT; + goto cmd_done; + } + + num_cmd = mcci.num_of_combo_cmds; + if (num_cmd < 1) { + err = -EINVAL; + goto cmd_done; + } + + ioc_data = devm_kzalloc(mmc_dev(card->host), + sizeof(struct mmc_blk_ioc_data) * num_cmd, + GFP_KERNEL); + if (!ioc_data) { + err = -ENOMEM; + goto cmd_done; + } + + for (i = 0; i < num_cmd; i++) { + usr_ptr = (u32)mcci.mmc_ioc_cmd_list + + (i * sizeof(struct mmc_ioc_cmd)); + ioc_data[i] = mmc_blk_ioctl_copy_from_user( + (struct mmc_ioc_cmd __user *)usr_ptr); + if (IS_ERR(ioc_data[i])) { + err = PTR_ERR(ioc_data[i]); + goto free_all; + } + } + + mmc_claim_host(card->host); + for (i = 0; i < num_cmd; i++) { + err = _mmc_blk_ioctl_cmd_locked(md, + ioc_data[i]); + if (err) { + err = -EFAULT; + mmc_release_host(card->host); + goto free_all; + } + } + mmc_release_host(card->host); + + /* copy to user if data and response */ + for (i = 0; i < num_cmd; i++) { + usr_ptr = (u32)mcci.mmc_ioc_cmd_list + + (i * sizeof(struct mmc_ioc_cmd)); + err = mmc_blk_ioctl_copy_to_user( + (struct mmc_ioc_cmd __user *)usr_ptr, + ioc_data[i]); + if (err) + break; + } + +free_all: + for (i = 0; i < num_cmd; i++) { + if (IS_ERR(ioc_data[i])) + break; + kfree(ioc_data[i]->buf); + kfree(ioc_data[i]); + } + devm_kfree(mmc_dev(card->host), ioc_data); +cmd_done: + mmc_blk_put(md); +out: + return err; +} + static int mmc_blk_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg) { int ret = -EINVAL; - if (cmd == MMC_IOC_CMD) - ret = mmc_blk_ioctl_cmd(bdev, (struct mmc_ioc_cmd __user *)arg); + switch (cmd) { + case MMC_COMBO_IOC_CMD: { + ret = mmc_blk_ioctl_combo_cmd(bdev, + (struct mmc_combo_cmd_info __user *)arg); + break; + } + case MMC_IOC_CMD: { + ret = mmc_blk_ioctl_cmd(bdev, + (struct mmc_ioc_cmd __user *)arg); + break; + } + } return ret; } |