aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mmc/card
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc/card')
-rw-r--r--drivers/mmc/card/Kconfig12
-rw-r--r--drivers/mmc/card/Makefile1
-rw-r--r--drivers/mmc/card/mmc_test.c892
3 files changed, 905 insertions, 0 deletions
diff --git a/drivers/mmc/card/Kconfig b/drivers/mmc/card/Kconfig
index aa8a4e461942..dd0f398ee2f5 100644
--- a/drivers/mmc/card/Kconfig
+++ b/drivers/mmc/card/Kconfig
@@ -39,3 +39,15 @@ config SDIO_UART
39 SDIO function driver for SDIO cards that implements the UART 39 SDIO function driver for SDIO cards that implements the UART
40 class, as well as the GPS class which appears like a UART. 40 class, as well as the GPS class which appears like a UART.
41 41
42config MMC_TEST
43 tristate "MMC host test driver"
44 default n
45 help
46 Development driver that performs a series of reads and writes
47 to a memory card in order to expose certain well known bugs
48 in host controllers. The tests are executed by writing to the
49 "test" file in sysfs under each card. Note that whatever is
50 on your card will be overwritten by these tests.
51
52 This driver is only of interest to those developing or
53 testing a host driver. Most people should say N here.
diff --git a/drivers/mmc/card/Makefile b/drivers/mmc/card/Makefile
index fc5a784cfa1a..0d407514f67d 100644
--- a/drivers/mmc/card/Makefile
+++ b/drivers/mmc/card/Makefile
@@ -8,6 +8,7 @@ endif
8 8
9obj-$(CONFIG_MMC_BLOCK) += mmc_block.o 9obj-$(CONFIG_MMC_BLOCK) += mmc_block.o
10mmc_block-objs := block.o queue.o 10mmc_block-objs := block.o queue.o
11obj-$(CONFIG_MMC_TEST) += mmc_test.o
11 12
12obj-$(CONFIG_SDIO_UART) += sdio_uart.o 13obj-$(CONFIG_SDIO_UART) += sdio_uart.o
13 14
diff --git a/drivers/mmc/card/mmc_test.c b/drivers/mmc/card/mmc_test.c
new file mode 100644
index 000000000000..ffadee549a41
--- /dev/null
+++ b/drivers/mmc/card/mmc_test.c
@@ -0,0 +1,892 @@
1/*
2 * linux/drivers/mmc/card/mmc_test.c
3 *
4 * Copyright 2007 Pierre Ossman
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or (at
9 * your option) any later version.
10 */
11
12#include <linux/mmc/core.h>
13#include <linux/mmc/card.h>
14#include <linux/mmc/host.h>
15#include <linux/mmc/mmc.h>
16
17#include <linux/scatterlist.h>
18
19#define RESULT_OK 0
20#define RESULT_FAIL 1
21#define RESULT_UNSUP_HOST 2
22#define RESULT_UNSUP_CARD 3
23
24#define BUFFER_SIZE (PAGE_SIZE * 4)
25
26struct mmc_test_card {
27 struct mmc_card *card;
28
29 u8 *buffer;
30};
31
32/*******************************************************************/
33/* Helper functions */
34/*******************************************************************/
35
36static int mmc_test_set_blksize(struct mmc_test_card *test, unsigned size)
37{
38 struct mmc_command cmd;
39 int ret;
40
41 cmd.opcode = MMC_SET_BLOCKLEN;
42 cmd.arg = size;
43 cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
44 ret = mmc_wait_for_cmd(test->card->host, &cmd, 0);
45 if (ret)
46 return ret;
47
48 return 0;
49}
50
51static int __mmc_test_transfer(struct mmc_test_card *test, int write,
52 unsigned broken_xfer, u8 *buffer, unsigned addr,
53 unsigned blocks, unsigned blksz)
54{
55 int ret, busy;
56
57 struct mmc_request mrq;
58 struct mmc_command cmd;
59 struct mmc_command stop;
60 struct mmc_data data;
61
62 struct scatterlist sg;
63
64 memset(&mrq, 0, sizeof(struct mmc_request));
65
66 mrq.cmd = &cmd;
67 mrq.data = &data;
68
69 memset(&cmd, 0, sizeof(struct mmc_command));
70
71 if (broken_xfer) {
72 if (blocks > 1) {
73 cmd.opcode = write ?
74 MMC_WRITE_BLOCK : MMC_READ_SINGLE_BLOCK;
75 } else {
76 cmd.opcode = MMC_SEND_STATUS;
77 }
78 } else {
79 if (blocks > 1) {
80 cmd.opcode = write ?
81 MMC_WRITE_MULTIPLE_BLOCK : MMC_READ_MULTIPLE_BLOCK;
82 } else {
83 cmd.opcode = write ?
84 MMC_WRITE_BLOCK : MMC_READ_SINGLE_BLOCK;
85 }
86 }
87
88 if (broken_xfer && blocks == 1)
89 cmd.arg = test->card->rca << 16;
90 else
91 cmd.arg = addr;
92 cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
93
94 memset(&stop, 0, sizeof(struct mmc_command));
95
96 if (!broken_xfer && (blocks > 1)) {
97 stop.opcode = MMC_STOP_TRANSMISSION;
98 stop.arg = 0;
99 stop.flags = MMC_RSP_R1B | MMC_CMD_AC;
100
101 mrq.stop = &stop;
102 }
103
104 memset(&data, 0, sizeof(struct mmc_data));
105
106 data.blksz = blksz;
107 data.blocks = blocks;
108 data.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ;
109 data.sg = &sg;
110 data.sg_len = 1;
111
112 sg_init_one(&sg, buffer, blocks * blksz);
113
114 mmc_set_data_timeout(&data, test->card);
115
116 mmc_wait_for_req(test->card->host, &mrq);
117
118 ret = 0;
119
120 if (broken_xfer) {
121 if (!ret && cmd.error)
122 ret = cmd.error;
123 if (!ret && data.error == 0)
124 ret = RESULT_FAIL;
125 if (!ret && data.error != -ETIMEDOUT)
126 ret = data.error;
127 if (!ret && stop.error)
128 ret = stop.error;
129 if (blocks > 1) {
130 if (!ret && data.bytes_xfered > blksz)
131 ret = RESULT_FAIL;
132 } else {
133 if (!ret && data.bytes_xfered > 0)
134 ret = RESULT_FAIL;
135 }
136 } else {
137 if (!ret && cmd.error)
138 ret = cmd.error;
139 if (!ret && data.error)
140 ret = data.error;
141 if (!ret && stop.error)
142 ret = stop.error;
143 if (!ret && data.bytes_xfered != blocks * blksz)
144 ret = RESULT_FAIL;
145 }
146
147 if (ret == -EINVAL)
148 ret = RESULT_UNSUP_HOST;
149
150 busy = 0;
151 do {
152 int ret2;
153
154 memset(&cmd, 0, sizeof(struct mmc_command));
155
156 cmd.opcode = MMC_SEND_STATUS;
157 cmd.arg = test->card->rca << 16;
158 cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
159
160 ret2 = mmc_wait_for_cmd(test->card->host, &cmd, 0);
161 if (ret2)
162 break;
163
164 if (!busy && !(cmd.resp[0] & R1_READY_FOR_DATA)) {
165 busy = 1;
166 printk(KERN_INFO "%s: Warning: Host did not "
167 "wait for busy state to end.\n",
168 mmc_hostname(test->card->host));
169 }
170 } while (!(cmd.resp[0] & R1_READY_FOR_DATA));
171
172 return ret;
173}
174
175static int mmc_test_transfer(struct mmc_test_card *test, int write,
176 u8 *buffer, unsigned addr, unsigned blocks, unsigned blksz)
177{
178 return __mmc_test_transfer(test, write, 0, buffer,
179 addr, blocks, blksz);
180}
181
182static int mmc_test_prepare_verify(struct mmc_test_card *test, int write)
183{
184 int ret, i;
185
186 ret = mmc_test_set_blksize(test, 512);
187 if (ret)
188 return ret;
189
190 if (write)
191 memset(test->buffer, 0xDF, BUFFER_SIZE);
192 else {
193 for (i = 0;i < BUFFER_SIZE;i++)
194 test->buffer[i] = i;
195 }
196
197 for (i = 0;i < BUFFER_SIZE / 512;i++) {
198 ret = mmc_test_transfer(test, 1, test->buffer + i * 512,
199 i * 512, 1, 512);
200 if (ret)
201 return ret;
202 }
203
204 return 0;
205}
206
207static int mmc_test_prepare_verify_write(struct mmc_test_card *test)
208{
209 return mmc_test_prepare_verify(test, 1);
210}
211
212static int mmc_test_prepare_verify_read(struct mmc_test_card *test)
213{
214 return mmc_test_prepare_verify(test, 0);
215}
216
217static int mmc_test_verified_transfer(struct mmc_test_card *test, int write,
218 u8 *buffer, unsigned addr, unsigned blocks, unsigned blksz)
219{
220 int ret, i, sectors;
221
222 /*
223 * It is assumed that the above preparation has been done.
224 */
225
226 memset(test->buffer, 0, BUFFER_SIZE);
227
228 if (write) {
229 for (i = 0;i < blocks * blksz;i++)
230 buffer[i] = i;
231 }
232
233 ret = mmc_test_set_blksize(test, blksz);
234 if (ret)
235 return ret;
236
237 ret = mmc_test_transfer(test, write, buffer, addr, blocks, blksz);
238 if (ret)
239 return ret;
240
241 if (write) {
242 ret = mmc_test_set_blksize(test, 512);
243 if (ret)
244 return ret;
245
246 sectors = (blocks * blksz + 511) / 512;
247 if ((sectors * 512) == (blocks * blksz))
248 sectors++;
249
250 if ((sectors * 512) > BUFFER_SIZE)
251 return -EINVAL;
252
253 memset(test->buffer, 0, sectors * 512);
254
255 for (i = 0;i < sectors;i++) {
256 ret = mmc_test_transfer(test, 0,
257 test->buffer + i * 512,
258 addr + i * 512, 1, 512);
259 if (ret)
260 return ret;
261 }
262
263 for (i = 0;i < blocks * blksz;i++) {
264 if (test->buffer[i] != (u8)i)
265 return RESULT_FAIL;
266 }
267
268 for (;i < sectors * 512;i++) {
269 if (test->buffer[i] != 0xDF)
270 return RESULT_FAIL;
271 }
272 } else {
273 for (i = 0;i < blocks * blksz;i++) {
274 if (buffer[i] != (u8)i)
275 return RESULT_FAIL;
276 }
277 }
278
279 return 0;
280}
281
282static int mmc_test_cleanup_verify(struct mmc_test_card *test)
283{
284 int ret, i;
285
286 ret = mmc_test_set_blksize(test, 512);
287 if (ret)
288 return ret;
289
290 memset(test->buffer, 0, BUFFER_SIZE);
291
292 for (i = 0;i < BUFFER_SIZE / 512;i++) {
293 ret = mmc_test_transfer(test, 1, test->buffer + i * 512,
294 i * 512, 1, 512);
295 if (ret)
296 return ret;
297 }
298
299 return 0;
300}
301
302/*******************************************************************/
303/* Tests */
304/*******************************************************************/
305
306struct mmc_test_case {
307 const char *name;
308
309 int (*prepare)(struct mmc_test_card *);
310 int (*run)(struct mmc_test_card *);
311 int (*cleanup)(struct mmc_test_card *);
312};
313
314static int mmc_test_basic_write(struct mmc_test_card *test)
315{
316 int ret;
317
318 ret = mmc_test_set_blksize(test, 512);
319 if (ret)
320 return ret;
321
322 ret = mmc_test_transfer(test, 1, test->buffer, 0, 1, 512);
323 if (ret)
324 return ret;
325
326 return 0;
327}
328
329static int mmc_test_basic_read(struct mmc_test_card *test)
330{
331 int ret;
332
333 ret = mmc_test_set_blksize(test, 512);
334 if (ret)
335 return ret;
336
337 ret = mmc_test_transfer(test, 0, test->buffer, 0, 1, 512);
338 if (ret)
339 return ret;
340
341 return 0;
342}
343
344static int mmc_test_verify_write(struct mmc_test_card *test)
345{
346 int ret;
347
348 ret = mmc_test_verified_transfer(test, 1, test->buffer, 0, 1, 512);
349 if (ret)
350 return ret;
351
352 return 0;
353}
354
355static int mmc_test_verify_read(struct mmc_test_card *test)
356{
357 int ret;
358
359 ret = mmc_test_verified_transfer(test, 0, test->buffer, 0, 1, 512);
360 if (ret)
361 return ret;
362
363 return 0;
364}
365
366static int mmc_test_multi_write(struct mmc_test_card *test)
367{
368 int ret;
369 unsigned int size;
370
371 if (test->card->host->max_blk_count == 1)
372 return RESULT_UNSUP_HOST;
373
374 size = PAGE_SIZE * 2;
375 size = min(size, test->card->host->max_req_size);
376 size = min(size, test->card->host->max_seg_size);
377 size = min(size, test->card->host->max_blk_count * 512);
378
379 if (size < 1024)
380 return RESULT_UNSUP_HOST;
381
382 ret = mmc_test_verified_transfer(test, 1, test->buffer, 0,
383 size / 512, 512);
384 if (ret)
385 return ret;
386
387 return 0;
388}
389
390static int mmc_test_multi_read(struct mmc_test_card *test)
391{
392 int ret;
393 unsigned int size;
394
395 if (test->card->host->max_blk_count == 1)
396 return RESULT_UNSUP_HOST;
397
398 size = PAGE_SIZE * 2;
399 size = min(size, test->card->host->max_req_size);
400 size = min(size, test->card->host->max_seg_size);
401 size = min(size, test->card->host->max_blk_count * 512);
402
403 if (size < 1024)
404 return RESULT_UNSUP_HOST;
405
406 ret = mmc_test_verified_transfer(test, 0, test->buffer, 0,
407 size / 512, 512);
408 if (ret)
409 return ret;
410
411 return 0;
412}
413
414static int mmc_test_pow2_write(struct mmc_test_card *test)
415{
416 int ret, i;
417
418 if (!test->card->csd.write_partial)
419 return RESULT_UNSUP_CARD;
420
421 for (i = 1; i < 512;i <<= 1) {
422 ret = mmc_test_verified_transfer(test, 1,
423 test->buffer, 0, 1, i);
424 if (ret)
425 return ret;
426 }
427
428 return 0;
429}
430
431static int mmc_test_pow2_read(struct mmc_test_card *test)
432{
433 int ret, i;
434
435 if (!test->card->csd.read_partial)
436 return RESULT_UNSUP_CARD;
437
438 for (i = 1; i < 512;i <<= 1) {
439 ret = mmc_test_verified_transfer(test, 0,
440 test->buffer, 0, 1, i);
441 if (ret)
442 return ret;
443 }
444
445 return 0;
446}
447
448static int mmc_test_weird_write(struct mmc_test_card *test)
449{
450 int ret, i;
451
452 if (!test->card->csd.write_partial)
453 return RESULT_UNSUP_CARD;
454
455 for (i = 3; i < 512;i += 7) {
456 ret = mmc_test_verified_transfer(test, 1,
457 test->buffer, 0, 1, i);
458 if (ret)
459 return ret;
460 }
461
462 return 0;
463}
464
465static int mmc_test_weird_read(struct mmc_test_card *test)
466{
467 int ret, i;
468
469 if (!test->card->csd.read_partial)
470 return RESULT_UNSUP_CARD;
471
472 for (i = 3; i < 512;i += 7) {
473 ret = mmc_test_verified_transfer(test, 0,
474 test->buffer, 0, 1, i);
475 if (ret)
476 return ret;
477 }
478
479 return 0;
480}
481
482static int mmc_test_align_write(struct mmc_test_card *test)
483{
484 int ret, i;
485
486 for (i = 1;i < 4;i++) {
487 ret = mmc_test_verified_transfer(test, 1, test->buffer + i,
488 0, 1, 512);
489 if (ret)
490 return ret;
491 }
492
493 return 0;
494}
495
496static int mmc_test_align_read(struct mmc_test_card *test)
497{
498 int ret, i;
499
500 for (i = 1;i < 4;i++) {
501 ret = mmc_test_verified_transfer(test, 0, test->buffer + i,
502 0, 1, 512);
503 if (ret)
504 return ret;
505 }
506
507 return 0;
508}
509
510static int mmc_test_align_multi_write(struct mmc_test_card *test)
511{
512 int ret, i;
513 unsigned int size;
514
515 if (test->card->host->max_blk_count == 1)
516 return RESULT_UNSUP_HOST;
517
518 size = PAGE_SIZE * 2;
519 size = min(size, test->card->host->max_req_size);
520 size = min(size, test->card->host->max_seg_size);
521 size = min(size, test->card->host->max_blk_count * 512);
522
523 if (size < 1024)
524 return RESULT_UNSUP_HOST;
525
526 for (i = 1;i < 4;i++) {
527 ret = mmc_test_verified_transfer(test, 1, test->buffer + i,
528 0, size / 512, 512);
529 if (ret)
530 return ret;
531 }
532
533 return 0;
534}
535
536static int mmc_test_align_multi_read(struct mmc_test_card *test)
537{
538 int ret, i;
539 unsigned int size;
540
541 if (test->card->host->max_blk_count == 1)
542 return RESULT_UNSUP_HOST;
543
544 size = PAGE_SIZE * 2;
545 size = min(size, test->card->host->max_req_size);
546 size = min(size, test->card->host->max_seg_size);
547 size = min(size, test->card->host->max_blk_count * 512);
548
549 if (size < 1024)
550 return RESULT_UNSUP_HOST;
551
552 for (i = 1;i < 4;i++) {
553 ret = mmc_test_verified_transfer(test, 0, test->buffer + i,
554 0, size / 512, 512);
555 if (ret)
556 return ret;
557 }
558
559 return 0;
560}
561
562static int mmc_test_xfersize_write(struct mmc_test_card *test)
563{
564 int ret;
565
566 ret = mmc_test_set_blksize(test, 512);
567 if (ret)
568 return ret;
569
570 ret = __mmc_test_transfer(test, 1, 1, test->buffer, 0, 1, 512);
571 if (ret)
572 return ret;
573
574 return 0;
575}
576
577static int mmc_test_xfersize_read(struct mmc_test_card *test)
578{
579 int ret;
580
581 ret = mmc_test_set_blksize(test, 512);
582 if (ret)
583 return ret;
584
585 ret = __mmc_test_transfer(test, 0, 1, test->buffer, 0, 1, 512);
586 if (ret)
587 return ret;
588
589 return 0;
590}
591
592static int mmc_test_multi_xfersize_write(struct mmc_test_card *test)
593{
594 int ret;
595
596 if (test->card->host->max_blk_count == 1)
597 return RESULT_UNSUP_HOST;
598
599 ret = mmc_test_set_blksize(test, 512);
600 if (ret)
601 return ret;
602
603 ret = __mmc_test_transfer(test, 1, 1, test->buffer, 0, 2, 512);
604 if (ret)
605 return ret;
606
607 return 0;
608}
609
610static int mmc_test_multi_xfersize_read(struct mmc_test_card *test)
611{
612 int ret;
613
614 if (test->card->host->max_blk_count == 1)
615 return RESULT_UNSUP_HOST;
616
617 ret = mmc_test_set_blksize(test, 512);
618 if (ret)
619 return ret;
620
621 ret = __mmc_test_transfer(test, 0, 1, test->buffer, 0, 2, 512);
622 if (ret)
623 return ret;
624
625 return 0;
626}
627
628static const struct mmc_test_case mmc_test_cases[] = {
629 {
630 .name = "Basic write (no data verification)",
631 .run = mmc_test_basic_write,
632 },
633
634 {
635 .name = "Basic read (no data verification)",
636 .run = mmc_test_basic_read,
637 },
638
639 {
640 .name = "Basic write (with data verification)",
641 .prepare = mmc_test_prepare_verify_write,
642 .run = mmc_test_verify_write,
643 .cleanup = mmc_test_cleanup_verify,
644 },
645
646 {
647 .name = "Basic read (with data verification)",
648 .prepare = mmc_test_prepare_verify_read,
649 .run = mmc_test_verify_read,
650 .cleanup = mmc_test_cleanup_verify,
651 },
652
653 {
654 .name = "Multi-block write",
655 .prepare = mmc_test_prepare_verify_write,
656 .run = mmc_test_multi_write,
657 .cleanup = mmc_test_cleanup_verify,
658 },
659
660 {
661 .name = "Multi-block read",
662 .prepare = mmc_test_prepare_verify_read,
663 .run = mmc_test_multi_read,
664 .cleanup = mmc_test_cleanup_verify,
665 },
666
667 {
668 .name = "Power of two block writes",
669 .prepare = mmc_test_prepare_verify_write,
670 .run = mmc_test_pow2_write,
671 .cleanup = mmc_test_cleanup_verify,
672 },
673
674 {
675 .name = "Power of two block reads",
676 .prepare = mmc_test_prepare_verify_read,
677 .run = mmc_test_pow2_read,
678 .cleanup = mmc_test_cleanup_verify,
679 },
680
681 {
682 .name = "Weird sized block writes",
683 .prepare = mmc_test_prepare_verify_write,
684 .run = mmc_test_weird_write,
685 .cleanup = mmc_test_cleanup_verify,
686 },
687
688 {
689 .name = "Weird sized block reads",
690 .prepare = mmc_test_prepare_verify_read,
691 .run = mmc_test_weird_read,
692 .cleanup = mmc_test_cleanup_verify,
693 },
694
695 {
696 .name = "Badly aligned write",
697 .prepare = mmc_test_prepare_verify_write,
698 .run = mmc_test_align_write,
699 .cleanup = mmc_test_cleanup_verify,
700 },
701
702 {
703 .name = "Badly aligned read",
704 .prepare = mmc_test_prepare_verify_read,
705 .run = mmc_test_align_read,
706 .cleanup = mmc_test_cleanup_verify,
707 },
708
709 {
710 .name = "Badly aligned multi-block write",
711 .prepare = mmc_test_prepare_verify_write,
712 .run = mmc_test_align_multi_write,
713 .cleanup = mmc_test_cleanup_verify,
714 },
715
716 {
717 .name = "Badly aligned multi-block read",
718 .prepare = mmc_test_prepare_verify_read,
719 .run = mmc_test_align_multi_read,
720 .cleanup = mmc_test_cleanup_verify,
721 },
722
723 {
724 .name = "Correct xfer_size at write (start failure)",
725 .run = mmc_test_xfersize_write,
726 },
727
728 {
729 .name = "Correct xfer_size at read (start failure)",
730 .run = mmc_test_xfersize_read,
731 },
732
733 {
734 .name = "Correct xfer_size at write (midway failure)",
735 .run = mmc_test_multi_xfersize_write,
736 },
737
738 {
739 .name = "Correct xfer_size at read (midway failure)",
740 .run = mmc_test_multi_xfersize_read,
741 },
742};
743
744static struct mutex mmc_test_lock;
745
746static void mmc_test_run(struct mmc_test_card *test)
747{
748 int i, ret;
749
750 printk(KERN_INFO "%s: Starting tests of card %s...\n",
751 mmc_hostname(test->card->host), mmc_card_id(test->card));
752
753 mmc_claim_host(test->card->host);
754
755 for (i = 0;i < ARRAY_SIZE(mmc_test_cases);i++) {
756 printk(KERN_INFO "%s: Test case %d. %s...\n",
757 mmc_hostname(test->card->host), i + 1,
758 mmc_test_cases[i].name);
759
760 if (mmc_test_cases[i].prepare) {
761 ret = mmc_test_cases[i].prepare(test);
762 if (ret) {
763 printk(KERN_INFO "%s: Result: Prepare "
764 "stage failed! (%d)\n",
765 mmc_hostname(test->card->host),
766 ret);
767 continue;
768 }
769 }
770
771 ret = mmc_test_cases[i].run(test);
772 switch (ret) {
773 case RESULT_OK:
774 printk(KERN_INFO "%s: Result: OK\n",
775 mmc_hostname(test->card->host));
776 break;
777 case RESULT_FAIL:
778 printk(KERN_INFO "%s: Result: FAILED\n",
779 mmc_hostname(test->card->host));
780 break;
781 case RESULT_UNSUP_HOST:
782 printk(KERN_INFO "%s: Result: UNSUPPORTED "
783 "(by host)\n",
784 mmc_hostname(test->card->host));
785 break;
786 case RESULT_UNSUP_CARD:
787 printk(KERN_INFO "%s: Result: UNSUPPORTED "
788 "(by card)\n",
789 mmc_hostname(test->card->host));
790 break;
791 default:
792 printk(KERN_INFO "%s: Result: ERROR (%d)\n",
793 mmc_hostname(test->card->host), ret);
794 }
795
796 if (mmc_test_cases[i].cleanup) {
797 ret = mmc_test_cases[i].cleanup(test);
798 if (ret) {
799 printk(KERN_INFO "%s: Warning: Cleanup "
800 "stage failed! (%d)\n",
801 mmc_hostname(test->card->host),
802 ret);
803 }
804 }
805 }
806
807 mmc_release_host(test->card->host);
808
809 printk(KERN_INFO "%s: Tests completed.\n",
810 mmc_hostname(test->card->host));
811}
812
813static ssize_t mmc_test_show(struct device *dev,
814 struct device_attribute *attr, char *buf)
815{
816 mutex_lock(&mmc_test_lock);
817 mutex_unlock(&mmc_test_lock);
818
819 return 0;
820}
821
822static ssize_t mmc_test_store(struct device *dev,
823 struct device_attribute *attr, const char *buf, size_t count)
824{
825 struct mmc_card *card;
826 struct mmc_test_card *test;
827
828 card = container_of(dev, struct mmc_card, dev);
829
830 test = kzalloc(sizeof(struct mmc_test_card), GFP_KERNEL);
831 if (!test)
832 return -ENOMEM;
833
834 test->card = card;
835
836 test->buffer = kzalloc(BUFFER_SIZE, GFP_KERNEL);
837 if (test->buffer) {
838 mutex_lock(&mmc_test_lock);
839 mmc_test_run(test);
840 mutex_unlock(&mmc_test_lock);
841 }
842
843 kfree(test->buffer);
844 kfree(test);
845
846 return count;
847}
848
849static DEVICE_ATTR(test, S_IWUSR | S_IRUGO, mmc_test_show, mmc_test_store);
850
851static int mmc_test_probe(struct mmc_card *card)
852{
853 int ret;
854
855 mutex_init(&mmc_test_lock);
856
857 ret = device_create_file(&card->dev, &dev_attr_test);
858 if (ret)
859 return ret;
860
861 return 0;
862}
863
864static void mmc_test_remove(struct mmc_card *card)
865{
866 device_remove_file(&card->dev, &dev_attr_test);
867}
868
869static struct mmc_driver mmc_driver = {
870 .drv = {
871 .name = "mmc_test",
872 },
873 .probe = mmc_test_probe,
874 .remove = mmc_test_remove,
875};
876
877static int __init mmc_test_init(void)
878{
879 return mmc_register_driver(&mmc_driver);
880}
881
882static void __exit mmc_test_exit(void)
883{
884 mmc_unregister_driver(&mmc_driver);
885}
886
887module_init(mmc_test_init);
888module_exit(mmc_test_exit);
889
890MODULE_LICENSE("GPL");
891MODULE_DESCRIPTION("Multimedia Card (MMC) host test driver");
892MODULE_AUTHOR("Pierre Ossman");