diff options
author | Pierre Ossman <drzeus@drzeus.cx> | 2007-08-12 08:23:50 -0400 |
---|---|---|
committer | Pierre Ossman <drzeus@drzeus.cx> | 2008-05-14 15:02:16 -0400 |
commit | 88ae600d58a8d3160144af480133a988404b8d59 (patch) | |
tree | 5a14d90bb7f97243ae806d1c3df03cc2bfce334d /drivers/mmc | |
parent | df48dd028766ce2fc05d1f1d9da9bf89855d5282 (diff) |
mmc: mmc host test driver
A dummy driver that performs a series of requests that are often mis-
handled by host drivers.
Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
Diffstat (limited to 'drivers/mmc')
-rw-r--r-- | drivers/mmc/card/Kconfig | 12 | ||||
-rw-r--r-- | drivers/mmc/card/Makefile | 1 | ||||
-rw-r--r-- | drivers/mmc/card/mmc_test.c | 892 |
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 | ||
42 | config 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 | ||
9 | obj-$(CONFIG_MMC_BLOCK) += mmc_block.o | 9 | obj-$(CONFIG_MMC_BLOCK) += mmc_block.o |
10 | mmc_block-objs := block.o queue.o | 10 | mmc_block-objs := block.o queue.o |
11 | obj-$(CONFIG_MMC_TEST) += mmc_test.o | ||
11 | 12 | ||
12 | obj-$(CONFIG_SDIO_UART) += sdio_uart.o | 13 | obj-$(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 | |||
26 | struct mmc_test_card { | ||
27 | struct mmc_card *card; | ||
28 | |||
29 | u8 *buffer; | ||
30 | }; | ||
31 | |||
32 | /*******************************************************************/ | ||
33 | /* Helper functions */ | ||
34 | /*******************************************************************/ | ||
35 | |||
36 | static 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 | |||
51 | static 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 | |||
175 | static 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 | |||
182 | static 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 | |||
207 | static int mmc_test_prepare_verify_write(struct mmc_test_card *test) | ||
208 | { | ||
209 | return mmc_test_prepare_verify(test, 1); | ||
210 | } | ||
211 | |||
212 | static int mmc_test_prepare_verify_read(struct mmc_test_card *test) | ||
213 | { | ||
214 | return mmc_test_prepare_verify(test, 0); | ||
215 | } | ||
216 | |||
217 | static 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 | |||
282 | static 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 | |||
306 | struct 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 | |||
314 | static 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 | |||
329 | static 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 | |||
344 | static 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 | |||
355 | static 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 | |||
366 | static 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 | |||
390 | static 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 | |||
414 | static 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 | |||
431 | static 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 | |||
448 | static 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 | |||
465 | static 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 | |||
482 | static 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 | |||
496 | static 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 | |||
510 | static 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 | |||
536 | static 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 | |||
562 | static 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 | |||
577 | static 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 | |||
592 | static 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 | |||
610 | static 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 | |||
628 | static 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 | |||
744 | static struct mutex mmc_test_lock; | ||
745 | |||
746 | static 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 | |||
813 | static 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 | |||
822 | static 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 | |||
849 | static DEVICE_ATTR(test, S_IWUSR | S_IRUGO, mmc_test_show, mmc_test_store); | ||
850 | |||
851 | static 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 | |||
864 | static void mmc_test_remove(struct mmc_card *card) | ||
865 | { | ||
866 | device_remove_file(&card->dev, &dev_attr_test); | ||
867 | } | ||
868 | |||
869 | static struct mmc_driver mmc_driver = { | ||
870 | .drv = { | ||
871 | .name = "mmc_test", | ||
872 | }, | ||
873 | .probe = mmc_test_probe, | ||
874 | .remove = mmc_test_remove, | ||
875 | }; | ||
876 | |||
877 | static int __init mmc_test_init(void) | ||
878 | { | ||
879 | return mmc_register_driver(&mmc_driver); | ||
880 | } | ||
881 | |||
882 | static void __exit mmc_test_exit(void) | ||
883 | { | ||
884 | mmc_unregister_driver(&mmc_driver); | ||
885 | } | ||
886 | |||
887 | module_init(mmc_test_init); | ||
888 | module_exit(mmc_test_exit); | ||
889 | |||
890 | MODULE_LICENSE("GPL"); | ||
891 | MODULE_DESCRIPTION("Multimedia Card (MMC) host test driver"); | ||
892 | MODULE_AUTHOR("Pierre Ossman"); | ||