|
31 | 31 | #include <linux/mutex.h> |
32 | 32 | #include <linux/scatterlist.h> |
33 | 33 | #include <linux/string_helpers.h> |
| 34 | +#include <linux/delay.h> |
| 35 | +#include <linux/capability.h> |
| 36 | +#include <linux/compat.h> |
34 | 37 |
|
| 38 | +#include <linux/mmc/ioctl.h> |
35 | 39 | #include <linux/mmc/card.h> |
36 | 40 | #include <linux/mmc/host.h> |
37 | 41 | #include <linux/mmc/mmc.h> |
@@ -218,11 +222,208 @@ mmc_blk_getgeo(struct block_device *bdev, struct hd_geometry *geo) |
218 | 222 | return 0; |
219 | 223 | } |
220 | 224 |
|
| 225 | +struct mmc_blk_ioc_data { |
| 226 | + struct mmc_ioc_cmd ic; |
| 227 | + unsigned char *buf; |
| 228 | + u64 buf_bytes; |
| 229 | +}; |
| 230 | + |
| 231 | +static struct mmc_blk_ioc_data *mmc_blk_ioctl_copy_from_user( |
| 232 | + struct mmc_ioc_cmd __user *user) |
| 233 | +{ |
| 234 | + struct mmc_blk_ioc_data *idata; |
| 235 | + int err; |
| 236 | + |
| 237 | + idata = kzalloc(sizeof(*idata), GFP_KERNEL); |
| 238 | + if (!idata) { |
| 239 | + err = -ENOMEM; |
| 240 | + goto copy_err; |
| 241 | + } |
| 242 | + |
| 243 | + if (copy_from_user(&idata->ic, user, sizeof(idata->ic))) { |
| 244 | + err = -EFAULT; |
| 245 | + goto copy_err; |
| 246 | + } |
| 247 | + |
| 248 | + idata->buf_bytes = (u64) idata->ic.blksz * idata->ic.blocks; |
| 249 | + if (idata->buf_bytes > MMC_IOC_MAX_BYTES) { |
| 250 | + err = -EOVERFLOW; |
| 251 | + goto copy_err; |
| 252 | + } |
| 253 | + |
| 254 | + idata->buf = kzalloc(idata->buf_bytes, GFP_KERNEL); |
| 255 | + if (!idata->buf) { |
| 256 | + err = -ENOMEM; |
| 257 | + goto copy_err; |
| 258 | + } |
| 259 | + |
| 260 | + if (copy_from_user(idata->buf, (void __user *)(unsigned long) |
| 261 | + idata->ic.data_ptr, idata->buf_bytes)) { |
| 262 | + err = -EFAULT; |
| 263 | + goto copy_err; |
| 264 | + } |
| 265 | + |
| 266 | + return idata; |
| 267 | + |
| 268 | +copy_err: |
| 269 | + kfree(idata->buf); |
| 270 | + kfree(idata); |
| 271 | + return ERR_PTR(err); |
| 272 | + |
| 273 | +} |
| 274 | + |
| 275 | +static int mmc_blk_ioctl_cmd(struct block_device *bdev, |
| 276 | + struct mmc_ioc_cmd __user *ic_ptr) |
| 277 | +{ |
| 278 | + struct mmc_blk_ioc_data *idata; |
| 279 | + struct mmc_blk_data *md; |
| 280 | + struct mmc_card *card; |
| 281 | + struct mmc_command cmd = {0}; |
| 282 | + struct mmc_data data = {0}; |
| 283 | + struct mmc_request mrq = {0}; |
| 284 | + struct scatterlist sg; |
| 285 | + int err; |
| 286 | + |
| 287 | + /* |
| 288 | + * The caller must have CAP_SYS_RAWIO, and must be calling this on the |
| 289 | + * whole block device, not on a partition. This prevents overspray |
| 290 | + * between sibling partitions. |
| 291 | + */ |
| 292 | + if ((!capable(CAP_SYS_RAWIO)) || (bdev != bdev->bd_contains)) |
| 293 | + return -EPERM; |
| 294 | + |
| 295 | + idata = mmc_blk_ioctl_copy_from_user(ic_ptr); |
| 296 | + if (IS_ERR(idata)) |
| 297 | + return PTR_ERR(idata); |
| 298 | + |
| 299 | + cmd.opcode = idata->ic.opcode; |
| 300 | + cmd.arg = idata->ic.arg; |
| 301 | + cmd.flags = idata->ic.flags; |
| 302 | + |
| 303 | + data.sg = &sg; |
| 304 | + data.sg_len = 1; |
| 305 | + data.blksz = idata->ic.blksz; |
| 306 | + data.blocks = idata->ic.blocks; |
| 307 | + |
| 308 | + sg_init_one(data.sg, idata->buf, idata->buf_bytes); |
| 309 | + |
| 310 | + if (idata->ic.write_flag) |
| 311 | + data.flags = MMC_DATA_WRITE; |
| 312 | + else |
| 313 | + data.flags = MMC_DATA_READ; |
| 314 | + |
| 315 | + mrq.cmd = &cmd; |
| 316 | + mrq.data = &data; |
| 317 | + |
| 318 | + md = mmc_blk_get(bdev->bd_disk); |
| 319 | + if (!md) { |
| 320 | + err = -EINVAL; |
| 321 | + goto cmd_done; |
| 322 | + } |
| 323 | + |
| 324 | + card = md->queue.card; |
| 325 | + if (IS_ERR(card)) { |
| 326 | + err = PTR_ERR(card); |
| 327 | + goto cmd_done; |
| 328 | + } |
| 329 | + |
| 330 | + mmc_claim_host(card->host); |
| 331 | + |
| 332 | + if (idata->ic.is_acmd) { |
| 333 | + err = mmc_app_cmd(card->host, card); |
| 334 | + if (err) |
| 335 | + goto cmd_rel_host; |
| 336 | + } |
| 337 | + |
| 338 | + /* data.flags must already be set before doing this. */ |
| 339 | + mmc_set_data_timeout(&data, card); |
| 340 | + /* Allow overriding the timeout_ns for empirical tuning. */ |
| 341 | + if (idata->ic.data_timeout_ns) |
| 342 | + data.timeout_ns = idata->ic.data_timeout_ns; |
| 343 | + |
| 344 | + if ((cmd.flags & MMC_RSP_R1B) == MMC_RSP_R1B) { |
| 345 | + /* |
| 346 | + * Pretend this is a data transfer and rely on the host driver |
| 347 | + * to compute timeout. When all host drivers support |
| 348 | + * cmd.cmd_timeout for R1B, this can be changed to: |
| 349 | + * |
| 350 | + * mrq.data = NULL; |
| 351 | + * cmd.cmd_timeout = idata->ic.cmd_timeout_ms; |
| 352 | + */ |
| 353 | + data.timeout_ns = idata->ic.cmd_timeout_ms * 1000000; |
| 354 | + } |
| 355 | + |
| 356 | + mmc_wait_for_req(card->host, &mrq); |
| 357 | + |
| 358 | + if (cmd.error) { |
| 359 | + dev_err(mmc_dev(card->host), "%s: cmd error %d\n", |
| 360 | + __func__, cmd.error); |
| 361 | + err = cmd.error; |
| 362 | + goto cmd_rel_host; |
| 363 | + } |
| 364 | + if (data.error) { |
| 365 | + dev_err(mmc_dev(card->host), "%s: data error %d\n", |
| 366 | + __func__, data.error); |
| 367 | + err = data.error; |
| 368 | + goto cmd_rel_host; |
| 369 | + } |
| 370 | + |
| 371 | + /* |
| 372 | + * According to the SD specs, some commands require a delay after |
| 373 | + * issuing the command. |
| 374 | + */ |
| 375 | + if (idata->ic.postsleep_min_us) |
| 376 | + usleep_range(idata->ic.postsleep_min_us, idata->ic.postsleep_max_us); |
| 377 | + |
| 378 | + if (copy_to_user(&(ic_ptr->response), cmd.resp, sizeof(cmd.resp))) { |
| 379 | + err = -EFAULT; |
| 380 | + goto cmd_rel_host; |
| 381 | + } |
| 382 | + |
| 383 | + if (!idata->ic.write_flag) { |
| 384 | + if (copy_to_user((void __user *)(unsigned long) idata->ic.data_ptr, |
| 385 | + idata->buf, idata->buf_bytes)) { |
| 386 | + err = -EFAULT; |
| 387 | + goto cmd_rel_host; |
| 388 | + } |
| 389 | + } |
| 390 | + |
| 391 | +cmd_rel_host: |
| 392 | + mmc_release_host(card->host); |
| 393 | + |
| 394 | +cmd_done: |
| 395 | + mmc_blk_put(md); |
| 396 | + kfree(idata->buf); |
| 397 | + kfree(idata); |
| 398 | + return err; |
| 399 | +} |
| 400 | + |
| 401 | +static int mmc_blk_ioctl(struct block_device *bdev, fmode_t mode, |
| 402 | + unsigned int cmd, unsigned long arg) |
| 403 | +{ |
| 404 | + int ret = -EINVAL; |
| 405 | + if (cmd == MMC_IOC_CMD) |
| 406 | + ret = mmc_blk_ioctl_cmd(bdev, (struct mmc_ioc_cmd __user *)arg); |
| 407 | + return ret; |
| 408 | +} |
| 409 | + |
| 410 | +#ifdef CONFIG_COMPAT |
| 411 | +static int mmc_blk_compat_ioctl(struct block_device *bdev, fmode_t mode, |
| 412 | + unsigned int cmd, unsigned long arg) |
| 413 | +{ |
| 414 | + return mmc_blk_ioctl(bdev, mode, cmd, (unsigned long) compat_ptr(arg)); |
| 415 | +} |
| 416 | +#endif |
| 417 | + |
221 | 418 | static const struct block_device_operations mmc_bdops = { |
222 | 419 | .open = mmc_blk_open, |
223 | 420 | .release = mmc_blk_release, |
224 | 421 | .getgeo = mmc_blk_getgeo, |
225 | 422 | .owner = THIS_MODULE, |
| 423 | + .ioctl = mmc_blk_ioctl, |
| 424 | +#ifdef CONFIG_COMPAT |
| 425 | + .compat_ioctl = mmc_blk_compat_ioctl, |
| 426 | +#endif |
226 | 427 | }; |
227 | 428 |
|
228 | 429 | struct mmc_blk_request { |
|
0 commit comments