aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mmc/card/mmc_test.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc/card/mmc_test.c')
-rw-r--r--drivers/mmc/card/mmc_test.c892
1 files changed, 892 insertions, 0 deletions
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");