diff options
-rw-r--r-- | Documentation/ioctl/ioctl-number.txt | 1 | ||||
-rw-r--r-- | drivers/mmc/card/block.c | 201 | ||||
-rw-r--r-- | drivers/mmc/core/sd_ops.c | 3 | ||||
-rw-r--r-- | include/linux/Kbuild | 1 | ||||
-rw-r--r-- | include/linux/mmc/Kbuild | 1 | ||||
-rw-r--r-- | include/linux/mmc/core.h | 1 | ||||
-rw-r--r-- | include/linux/mmc/ioctl.h | 54 |
7 files changed, 261 insertions, 1 deletions
diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt index a0a5d82b6b0..2a34d822e6d 100644 --- a/Documentation/ioctl/ioctl-number.txt +++ b/Documentation/ioctl/ioctl-number.txt | |||
@@ -304,6 +304,7 @@ Code Seq#(hex) Include File Comments | |||
304 | 0xB0 all RATIO devices in development: | 304 | 0xB0 all RATIO devices in development: |
305 | <mailto:vgo@ratio.de> | 305 | <mailto:vgo@ratio.de> |
306 | 0xB1 00-1F PPPoX <mailto:mostrows@styx.uwaterloo.ca> | 306 | 0xB1 00-1F PPPoX <mailto:mostrows@styx.uwaterloo.ca> |
307 | 0xB3 00 linux/mmc/ioctl.h | ||
307 | 0xC0 00-0F linux/usb/iowarrior.h | 308 | 0xC0 00-0F linux/usb/iowarrior.h |
308 | 0xCB 00-1F CBM serial IEC bus in development: | 309 | 0xCB 00-1F CBM serial IEC bus in development: |
309 | <mailto:michael.klein@puffin.lb.shuttle.de> | 310 | <mailto:michael.klein@puffin.lb.shuttle.de> |
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index e7efd6faeaf..407836d5571 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c | |||
@@ -31,7 +31,11 @@ | |||
31 | #include <linux/mutex.h> | 31 | #include <linux/mutex.h> |
32 | #include <linux/scatterlist.h> | 32 | #include <linux/scatterlist.h> |
33 | #include <linux/string_helpers.h> | 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 | #include <linux/mmc/card.h> | 39 | #include <linux/mmc/card.h> |
36 | #include <linux/mmc/host.h> | 40 | #include <linux/mmc/host.h> |
37 | #include <linux/mmc/mmc.h> | 41 | #include <linux/mmc/mmc.h> |
@@ -218,11 +222,208 @@ mmc_blk_getgeo(struct block_device *bdev, struct hd_geometry *geo) | |||
218 | return 0; | 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 | static const struct block_device_operations mmc_bdops = { | 418 | static const struct block_device_operations mmc_bdops = { |
222 | .open = mmc_blk_open, | 419 | .open = mmc_blk_open, |
223 | .release = mmc_blk_release, | 420 | .release = mmc_blk_release, |
224 | .getgeo = mmc_blk_getgeo, | 421 | .getgeo = mmc_blk_getgeo, |
225 | .owner = THIS_MODULE, | 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 | struct mmc_blk_request { | 429 | struct mmc_blk_request { |
diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c index a206aea5360..021fed15380 100644 --- a/drivers/mmc/core/sd_ops.c +++ b/drivers/mmc/core/sd_ops.c | |||
@@ -21,7 +21,7 @@ | |||
21 | #include "core.h" | 21 | #include "core.h" |
22 | #include "sd_ops.h" | 22 | #include "sd_ops.h" |
23 | 23 | ||
24 | static int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card) | 24 | int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card) |
25 | { | 25 | { |
26 | int err; | 26 | int err; |
27 | struct mmc_command cmd = {0}; | 27 | struct mmc_command cmd = {0}; |
@@ -49,6 +49,7 @@ static int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card) | |||
49 | 49 | ||
50 | return 0; | 50 | return 0; |
51 | } | 51 | } |
52 | EXPORT_SYMBOL_GPL(mmc_app_cmd); | ||
52 | 53 | ||
53 | /** | 54 | /** |
54 | * mmc_wait_for_app_cmd - start an application command and wait for | 55 | * mmc_wait_for_app_cmd - start an application command and wait for |
diff --git a/include/linux/Kbuild b/include/linux/Kbuild index 75cf611641e..ed38527599e 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild | |||
@@ -4,6 +4,7 @@ header-y += caif/ | |||
4 | header-y += dvb/ | 4 | header-y += dvb/ |
5 | header-y += hdlc/ | 5 | header-y += hdlc/ |
6 | header-y += isdn/ | 6 | header-y += isdn/ |
7 | header-y += mmc/ | ||
7 | header-y += nfsd/ | 8 | header-y += nfsd/ |
8 | header-y += raid/ | 9 | header-y += raid/ |
9 | header-y += spi/ | 10 | header-y += spi/ |
diff --git a/include/linux/mmc/Kbuild b/include/linux/mmc/Kbuild new file mode 100644 index 00000000000..1fb26448faa --- /dev/null +++ b/include/linux/mmc/Kbuild | |||
@@ -0,0 +1 @@ | |||
header-y += ioctl.h | |||
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index f8e4bcbd284..cbe8d55f64c 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h | |||
@@ -133,6 +133,7 @@ struct mmc_card; | |||
133 | 133 | ||
134 | extern void mmc_wait_for_req(struct mmc_host *, struct mmc_request *); | 134 | extern void mmc_wait_for_req(struct mmc_host *, struct mmc_request *); |
135 | extern int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int); | 135 | extern int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int); |
136 | extern int mmc_app_cmd(struct mmc_host *, struct mmc_card *); | ||
136 | extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *, | 137 | extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *, |
137 | struct mmc_command *, int); | 138 | struct mmc_command *, int); |
138 | extern int mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int); | 139 | extern int mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int); |
diff --git a/include/linux/mmc/ioctl.h b/include/linux/mmc/ioctl.h new file mode 100644 index 00000000000..5baf2983a12 --- /dev/null +++ b/include/linux/mmc/ioctl.h | |||
@@ -0,0 +1,54 @@ | |||
1 | #ifndef LINUX_MMC_IOCTL_H | ||
2 | #define LINUX_MMC_IOCTL_H | ||
3 | struct mmc_ioc_cmd { | ||
4 | /* Implies direction of data. true = write, false = read */ | ||
5 | int write_flag; | ||
6 | |||
7 | /* Application-specific command. true = precede with CMD55 */ | ||
8 | int is_acmd; | ||
9 | |||
10 | __u32 opcode; | ||
11 | __u32 arg; | ||
12 | __u32 response[4]; /* CMD response */ | ||
13 | unsigned int flags; | ||
14 | unsigned int blksz; | ||
15 | unsigned int blocks; | ||
16 | |||
17 | /* | ||
18 | * Sleep at least postsleep_min_us useconds, and at most | ||
19 | * postsleep_max_us useconds *after* issuing command. Needed for | ||
20 | * some read commands for which cards have no other way of indicating | ||
21 | * they're ready for the next command (i.e. there is no equivalent of | ||
22 | * a "busy" indicator for read operations). | ||
23 | */ | ||
24 | unsigned int postsleep_min_us; | ||
25 | unsigned int postsleep_max_us; | ||
26 | |||
27 | /* | ||
28 | * Override driver-computed timeouts. Note the difference in units! | ||
29 | */ | ||
30 | unsigned int data_timeout_ns; | ||
31 | unsigned int cmd_timeout_ms; | ||
32 | |||
33 | /* | ||
34 | * For 64-bit machines, the next member, ``__u64 data_ptr``, wants to | ||
35 | * be 8-byte aligned. Make sure this struct is the same size when | ||
36 | * built for 32-bit. | ||
37 | */ | ||
38 | __u32 __pad; | ||
39 | |||
40 | /* DAT buffer */ | ||
41 | __u64 data_ptr; | ||
42 | }; | ||
43 | #define mmc_ioc_cmd_set_data(ic, ptr) ic.data_ptr = (__u64)(unsigned long) ptr | ||
44 | |||
45 | #define MMC_IOC_CMD _IOWR(MMC_BLOCK_MAJOR, 0, struct mmc_ioc_cmd) | ||
46 | |||
47 | /* | ||
48 | * Since this ioctl is only meant to enhance (and not replace) normal access | ||
49 | * to the mmc bus device, an upper data transfer limit of MMC_IOC_MAX_BYTES | ||
50 | * is enforced per ioctl call. For larger data transfers, use the normal | ||
51 | * block device operations. | ||
52 | */ | ||
53 | #define MMC_IOC_MAX_BYTES (512L * 256) | ||
54 | #endif /* LINUX_MMC_IOCTL_H */ | ||