2 * linux/drivers/mmc/card/mmc_test.c
4 * Copyright 2007-2008 Pierre Ossman
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.
12 #include <linux/mmc/core.h>
13 #include <linux/mmc/card.h>
14 #include <linux/mmc/host.h>
15 #include <linux/mmc/mmc.h>
17 #include <linux/scatterlist.h>
21 #define RESULT_UNSUP_HOST 2
22 #define RESULT_UNSUP_CARD 3
24 #define BUFFER_SIZE (PAGE_SIZE * 4)
26 struct mmc_test_card {
27 struct mmc_card *card;
29 u8 scratch[BUFFER_SIZE];
33 /*******************************************************************/
34 /* General helper functions */
35 /*******************************************************************/
38 * Configure correct block size in card
40 static int mmc_test_set_blksize(struct mmc_test_card *test, unsigned size)
42 struct mmc_command cmd;
45 cmd.opcode = MMC_SET_BLOCKLEN;
47 cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
48 ret = mmc_wait_for_cmd(test->card->host, &cmd, 0);
56 * Fill in the mmc_request structure given a set of transfer parameters.
58 static void mmc_test_prepare_mrq(struct mmc_test_card *test,
59 struct mmc_request *mrq, struct scatterlist *sg, unsigned sg_len,
60 unsigned dev_addr, unsigned blocks, unsigned blksz, int write)
62 BUG_ON(!mrq || !mrq->cmd || !mrq->data || !mrq->stop);
65 mrq->cmd->opcode = write ?
66 MMC_WRITE_MULTIPLE_BLOCK : MMC_READ_MULTIPLE_BLOCK;
68 mrq->cmd->opcode = write ?
69 MMC_WRITE_BLOCK : MMC_READ_SINGLE_BLOCK;
72 mrq->cmd->arg = dev_addr;
73 mrq->cmd->flags = MMC_RSP_R1 | MMC_CMD_ADTC;
78 mrq->stop->opcode = MMC_STOP_TRANSMISSION;
80 mrq->stop->flags = MMC_RSP_R1B | MMC_CMD_AC;
83 mrq->data->blksz = blksz;
84 mrq->data->blocks = blocks;
85 mrq->data->flags = write ? MMC_DATA_WRITE : MMC_DATA_READ;
87 mrq->data->sg_len = sg_len;
89 mmc_set_data_timeout(mrq->data, test->card);
93 * Wait for the card to finish the busy state
95 static int mmc_test_wait_busy(struct mmc_test_card *test)
98 struct mmc_command cmd;
102 memset(&cmd, 0, sizeof(struct mmc_command));
104 cmd.opcode = MMC_SEND_STATUS;
105 cmd.arg = test->card->rca << 16;
106 cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
108 ret = mmc_wait_for_cmd(test->card->host, &cmd, 0);
112 if (!busy && !(cmd.resp[0] & R1_READY_FOR_DATA)) {
114 printk(KERN_INFO "%s: Warning: Host did not "
115 "wait for busy state to end.\n",
116 mmc_hostname(test->card->host));
118 } while (!(cmd.resp[0] & R1_READY_FOR_DATA));
124 * Transfer a single sector of kernel addressable data
126 static int mmc_test_buffer_transfer(struct mmc_test_card *test,
127 u8 *buffer, unsigned addr, unsigned blksz, int write)
131 struct mmc_request mrq;
132 struct mmc_command cmd;
133 struct mmc_command stop;
134 struct mmc_data data;
136 struct scatterlist sg;
138 memset(&mrq, 0, sizeof(struct mmc_request));
139 memset(&cmd, 0, sizeof(struct mmc_command));
140 memset(&data, 0, sizeof(struct mmc_data));
141 memset(&stop, 0, sizeof(struct mmc_command));
147 sg_init_one(&sg, buffer, blksz);
149 mmc_test_prepare_mrq(test, &mrq, &sg, 1, addr, 1, blksz, write);
151 mmc_wait_for_req(test->card->host, &mrq);
158 ret = mmc_test_wait_busy(test);
165 /*******************************************************************/
166 /* Test preparation and cleanup */
167 /*******************************************************************/
170 * Fill the first couple of sectors of the card with known data
171 * so that bad reads/writes can be detected
173 static int __mmc_test_prepare(struct mmc_test_card *test, int write)
177 ret = mmc_test_set_blksize(test, 512);
182 memset(test->buffer, 0xDF, 512);
184 for (i = 0;i < 512;i++)
188 for (i = 0;i < BUFFER_SIZE / 512;i++) {
189 ret = mmc_test_buffer_transfer(test, test->buffer, i * 512, 512, 1);
197 static int mmc_test_prepare_write(struct mmc_test_card *test)
199 return __mmc_test_prepare(test, 1);
202 static int mmc_test_prepare_read(struct mmc_test_card *test)
204 return __mmc_test_prepare(test, 0);
207 static int mmc_test_cleanup(struct mmc_test_card *test)
211 ret = mmc_test_set_blksize(test, 512);
215 memset(test->buffer, 0, 512);
217 for (i = 0;i < BUFFER_SIZE / 512;i++) {
218 ret = mmc_test_buffer_transfer(test, test->buffer, i * 512, 512, 1);
226 /*******************************************************************/
227 /* Test execution helpers */
228 /*******************************************************************/
231 * Modifies the mmc_request to perform the "short transfer" tests
233 static void mmc_test_prepare_broken_mrq(struct mmc_test_card *test,
234 struct mmc_request *mrq, int write)
236 BUG_ON(!mrq || !mrq->cmd || !mrq->data);
238 if (mrq->data->blocks > 1) {
239 mrq->cmd->opcode = write ?
240 MMC_WRITE_BLOCK : MMC_READ_SINGLE_BLOCK;
243 mrq->cmd->opcode = MMC_SEND_STATUS;
244 mrq->cmd->arg = test->card->rca << 16;
249 * Checks that a normal transfer didn't have any errors
251 static int mmc_test_check_result(struct mmc_test_card *test,
252 struct mmc_request *mrq)
256 BUG_ON(!mrq || !mrq->cmd || !mrq->data);
260 if (!ret && mrq->cmd->error)
261 ret = mrq->cmd->error;
262 if (!ret && mrq->data->error)
263 ret = mrq->data->error;
264 if (!ret && mrq->stop && mrq->stop->error)
265 ret = mrq->stop->error;
266 if (!ret && mrq->data->bytes_xfered !=
267 mrq->data->blocks * mrq->data->blksz)
271 ret = RESULT_UNSUP_HOST;
277 * Checks that a "short transfer" behaved as expected
279 static int mmc_test_check_broken_result(struct mmc_test_card *test,
280 struct mmc_request *mrq)
284 BUG_ON(!mrq || !mrq->cmd || !mrq->data);
288 if (!ret && mrq->cmd->error)
289 ret = mrq->cmd->error;
290 if (!ret && mrq->data->error == 0)
292 if (!ret && mrq->data->error != -ETIMEDOUT)
293 ret = mrq->data->error;
294 if (!ret && mrq->stop && mrq->stop->error)
295 ret = mrq->stop->error;
296 if (mrq->data->blocks > 1) {
297 if (!ret && mrq->data->bytes_xfered > mrq->data->blksz)
300 if (!ret && mrq->data->bytes_xfered > 0)
305 ret = RESULT_UNSUP_HOST;
311 * Tests a basic transfer with certain parameters
313 static int mmc_test_simple_transfer(struct mmc_test_card *test,
314 struct scatterlist *sg, unsigned sg_len, unsigned dev_addr,
315 unsigned blocks, unsigned blksz, int write)
317 struct mmc_request mrq;
318 struct mmc_command cmd;
319 struct mmc_command stop;
320 struct mmc_data data;
322 memset(&mrq, 0, sizeof(struct mmc_request));
323 memset(&cmd, 0, sizeof(struct mmc_command));
324 memset(&data, 0, sizeof(struct mmc_data));
325 memset(&stop, 0, sizeof(struct mmc_command));
331 mmc_test_prepare_mrq(test, &mrq, sg, sg_len, dev_addr,
332 blocks, blksz, write);
334 mmc_wait_for_req(test->card->host, &mrq);
336 mmc_test_wait_busy(test);
338 return mmc_test_check_result(test, &mrq);
342 * Tests a transfer where the card will fail completely or partly
344 static int mmc_test_broken_transfer(struct mmc_test_card *test,
345 unsigned blocks, unsigned blksz, int write)
347 struct mmc_request mrq;
348 struct mmc_command cmd;
349 struct mmc_command stop;
350 struct mmc_data data;
352 struct scatterlist sg;
354 memset(&mrq, 0, sizeof(struct mmc_request));
355 memset(&cmd, 0, sizeof(struct mmc_command));
356 memset(&data, 0, sizeof(struct mmc_data));
357 memset(&stop, 0, sizeof(struct mmc_command));
363 sg_init_one(&sg, test->buffer, blocks * blksz);
365 mmc_test_prepare_mrq(test, &mrq, &sg, 1, 0, blocks, blksz, write);
366 mmc_test_prepare_broken_mrq(test, &mrq, write);
368 mmc_wait_for_req(test->card->host, &mrq);
370 mmc_test_wait_busy(test);
372 return mmc_test_check_broken_result(test, &mrq);
376 * Does a complete transfer test where data is also validated
378 * Note: mmc_test_prepare() must have been done before this call
380 static int mmc_test_transfer(struct mmc_test_card *test,
381 struct scatterlist *sg, unsigned sg_len, unsigned dev_addr,
382 unsigned blocks, unsigned blksz, int write)
388 for (i = 0;i < blocks * blksz;i++)
389 test->scratch[i] = i;
391 memset(test->scratch, 0, BUFFER_SIZE);
393 local_irq_save(flags);
394 sg_copy_from_buffer(sg, sg_len, test->scratch, BUFFER_SIZE);
395 local_irq_restore(flags);
397 ret = mmc_test_set_blksize(test, blksz);
401 ret = mmc_test_simple_transfer(test, sg, sg_len, dev_addr,
402 blocks, blksz, write);
409 ret = mmc_test_set_blksize(test, 512);
413 sectors = (blocks * blksz + 511) / 512;
414 if ((sectors * 512) == (blocks * blksz))
417 if ((sectors * 512) > BUFFER_SIZE)
420 memset(test->buffer, 0, sectors * 512);
422 for (i = 0;i < sectors;i++) {
423 ret = mmc_test_buffer_transfer(test,
424 test->buffer + i * 512,
425 dev_addr + i * 512, 512, 0);
430 for (i = 0;i < blocks * blksz;i++) {
431 if (test->buffer[i] != (u8)i)
435 for (;i < sectors * 512;i++) {
436 if (test->buffer[i] != 0xDF)
440 local_irq_save(flags);
441 sg_copy_to_buffer(sg, sg_len, test->scratch, BUFFER_SIZE);
442 local_irq_restore(flags);
443 for (i = 0;i < blocks * blksz;i++) {
444 if (test->scratch[i] != (u8)i)
452 /*******************************************************************/
454 /*******************************************************************/
456 struct mmc_test_case {
459 int (*prepare)(struct mmc_test_card *);
460 int (*run)(struct mmc_test_card *);
461 int (*cleanup)(struct mmc_test_card *);
464 static int mmc_test_basic_write(struct mmc_test_card *test)
467 struct scatterlist sg;
469 ret = mmc_test_set_blksize(test, 512);
473 sg_init_one(&sg, test->buffer, 512);
475 ret = mmc_test_simple_transfer(test, &sg, 1, 0, 1, 512, 1);
482 static int mmc_test_basic_read(struct mmc_test_card *test)
485 struct scatterlist sg;
487 ret = mmc_test_set_blksize(test, 512);
491 sg_init_one(&sg, test->buffer, 512);
493 ret = mmc_test_simple_transfer(test, &sg, 1, 0, 1, 512, 1);
500 static int mmc_test_verify_write(struct mmc_test_card *test)
503 struct scatterlist sg;
505 sg_init_one(&sg, test->buffer, 512);
507 ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 1);
514 static int mmc_test_verify_read(struct mmc_test_card *test)
517 struct scatterlist sg;
519 sg_init_one(&sg, test->buffer, 512);
521 ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 0);
528 static int mmc_test_multi_write(struct mmc_test_card *test)
532 struct scatterlist sg;
534 if (test->card->host->max_blk_count == 1)
535 return RESULT_UNSUP_HOST;
537 size = PAGE_SIZE * 2;
538 size = min(size, test->card->host->max_req_size);
539 size = min(size, test->card->host->max_seg_size);
540 size = min(size, test->card->host->max_blk_count * 512);
543 return RESULT_UNSUP_HOST;
545 sg_init_one(&sg, test->buffer, size);
547 ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 1);
554 static int mmc_test_multi_read(struct mmc_test_card *test)
558 struct scatterlist sg;
560 if (test->card->host->max_blk_count == 1)
561 return RESULT_UNSUP_HOST;
563 size = PAGE_SIZE * 2;
564 size = min(size, test->card->host->max_req_size);
565 size = min(size, test->card->host->max_seg_size);
566 size = min(size, test->card->host->max_blk_count * 512);
569 return RESULT_UNSUP_HOST;
571 sg_init_one(&sg, test->buffer, size);
573 ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 0);
580 static int mmc_test_pow2_write(struct mmc_test_card *test)
583 struct scatterlist sg;
585 if (!test->card->csd.write_partial)
586 return RESULT_UNSUP_CARD;
588 for (i = 1; i < 512;i <<= 1) {
589 sg_init_one(&sg, test->buffer, i);
590 ret = mmc_test_transfer(test, &sg, 1, 0, 1, i, 1);
598 static int mmc_test_pow2_read(struct mmc_test_card *test)
601 struct scatterlist sg;
603 if (!test->card->csd.read_partial)
604 return RESULT_UNSUP_CARD;
606 for (i = 1; i < 512;i <<= 1) {
607 sg_init_one(&sg, test->buffer, i);
608 ret = mmc_test_transfer(test, &sg, 1, 0, 1, i, 0);
616 static int mmc_test_weird_write(struct mmc_test_card *test)
619 struct scatterlist sg;
621 if (!test->card->csd.write_partial)
622 return RESULT_UNSUP_CARD;
624 for (i = 3; i < 512;i += 7) {
625 sg_init_one(&sg, test->buffer, i);
626 ret = mmc_test_transfer(test, &sg, 1, 0, 1, i, 1);
634 static int mmc_test_weird_read(struct mmc_test_card *test)
637 struct scatterlist sg;
639 if (!test->card->csd.read_partial)
640 return RESULT_UNSUP_CARD;
642 for (i = 3; i < 512;i += 7) {
643 sg_init_one(&sg, test->buffer, i);
644 ret = mmc_test_transfer(test, &sg, 1, 0, 1, i, 0);
652 static int mmc_test_align_write(struct mmc_test_card *test)
655 struct scatterlist sg;
657 for (i = 1;i < 4;i++) {
658 sg_init_one(&sg, test->buffer + i, 512);
659 ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 1);
667 static int mmc_test_align_read(struct mmc_test_card *test)
670 struct scatterlist sg;
672 for (i = 1;i < 4;i++) {
673 sg_init_one(&sg, test->buffer + i, 512);
674 ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 0);
682 static int mmc_test_align_multi_write(struct mmc_test_card *test)
686 struct scatterlist sg;
688 if (test->card->host->max_blk_count == 1)
689 return RESULT_UNSUP_HOST;
691 size = PAGE_SIZE * 2;
692 size = min(size, test->card->host->max_req_size);
693 size = min(size, test->card->host->max_seg_size);
694 size = min(size, test->card->host->max_blk_count * 512);
697 return RESULT_UNSUP_HOST;
699 for (i = 1;i < 4;i++) {
700 sg_init_one(&sg, test->buffer + i, size);
701 ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 1);
709 static int mmc_test_align_multi_read(struct mmc_test_card *test)
713 struct scatterlist sg;
715 if (test->card->host->max_blk_count == 1)
716 return RESULT_UNSUP_HOST;
718 size = PAGE_SIZE * 2;
719 size = min(size, test->card->host->max_req_size);
720 size = min(size, test->card->host->max_seg_size);
721 size = min(size, test->card->host->max_blk_count * 512);
724 return RESULT_UNSUP_HOST;
726 for (i = 1;i < 4;i++) {
727 sg_init_one(&sg, test->buffer + i, size);
728 ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 0);
736 static int mmc_test_xfersize_write(struct mmc_test_card *test)
740 ret = mmc_test_set_blksize(test, 512);
744 ret = mmc_test_broken_transfer(test, 1, 512, 1);
751 static int mmc_test_xfersize_read(struct mmc_test_card *test)
755 ret = mmc_test_set_blksize(test, 512);
759 ret = mmc_test_broken_transfer(test, 1, 512, 0);
766 static int mmc_test_multi_xfersize_write(struct mmc_test_card *test)
770 if (test->card->host->max_blk_count == 1)
771 return RESULT_UNSUP_HOST;
773 ret = mmc_test_set_blksize(test, 512);
777 ret = mmc_test_broken_transfer(test, 2, 512, 1);
784 static int mmc_test_multi_xfersize_read(struct mmc_test_card *test)
788 if (test->card->host->max_blk_count == 1)
789 return RESULT_UNSUP_HOST;
791 ret = mmc_test_set_blksize(test, 512);
795 ret = mmc_test_broken_transfer(test, 2, 512, 0);
802 static const struct mmc_test_case mmc_test_cases[] = {
804 .name = "Basic write (no data verification)",
805 .run = mmc_test_basic_write,
809 .name = "Basic read (no data verification)",
810 .run = mmc_test_basic_read,
814 .name = "Basic write (with data verification)",
815 .prepare = mmc_test_prepare_write,
816 .run = mmc_test_verify_write,
817 .cleanup = mmc_test_cleanup,
821 .name = "Basic read (with data verification)",
822 .prepare = mmc_test_prepare_read,
823 .run = mmc_test_verify_read,
824 .cleanup = mmc_test_cleanup,
828 .name = "Multi-block write",
829 .prepare = mmc_test_prepare_write,
830 .run = mmc_test_multi_write,
831 .cleanup = mmc_test_cleanup,
835 .name = "Multi-block read",
836 .prepare = mmc_test_prepare_read,
837 .run = mmc_test_multi_read,
838 .cleanup = mmc_test_cleanup,
842 .name = "Power of two block writes",
843 .prepare = mmc_test_prepare_write,
844 .run = mmc_test_pow2_write,
845 .cleanup = mmc_test_cleanup,
849 .name = "Power of two block reads",
850 .prepare = mmc_test_prepare_read,
851 .run = mmc_test_pow2_read,
852 .cleanup = mmc_test_cleanup,
856 .name = "Weird sized block writes",
857 .prepare = mmc_test_prepare_write,
858 .run = mmc_test_weird_write,
859 .cleanup = mmc_test_cleanup,
863 .name = "Weird sized block reads",
864 .prepare = mmc_test_prepare_read,
865 .run = mmc_test_weird_read,
866 .cleanup = mmc_test_cleanup,
870 .name = "Badly aligned write",
871 .prepare = mmc_test_prepare_write,
872 .run = mmc_test_align_write,
873 .cleanup = mmc_test_cleanup,
877 .name = "Badly aligned read",
878 .prepare = mmc_test_prepare_read,
879 .run = mmc_test_align_read,
880 .cleanup = mmc_test_cleanup,
884 .name = "Badly aligned multi-block write",
885 .prepare = mmc_test_prepare_write,
886 .run = mmc_test_align_multi_write,
887 .cleanup = mmc_test_cleanup,
891 .name = "Badly aligned multi-block read",
892 .prepare = mmc_test_prepare_read,
893 .run = mmc_test_align_multi_read,
894 .cleanup = mmc_test_cleanup,
898 .name = "Correct xfer_size at write (start failure)",
899 .run = mmc_test_xfersize_write,
903 .name = "Correct xfer_size at read (start failure)",
904 .run = mmc_test_xfersize_read,
908 .name = "Correct xfer_size at write (midway failure)",
909 .run = mmc_test_multi_xfersize_write,
913 .name = "Correct xfer_size at read (midway failure)",
914 .run = mmc_test_multi_xfersize_read,
918 static struct mutex mmc_test_lock;
920 static void mmc_test_run(struct mmc_test_card *test, int testcase)
924 printk(KERN_INFO "%s: Starting tests of card %s...\n",
925 mmc_hostname(test->card->host), mmc_card_id(test->card));
927 mmc_claim_host(test->card->host);
929 for (i = 0;i < ARRAY_SIZE(mmc_test_cases);i++) {
930 if (testcase && ((i + 1) != testcase))
933 printk(KERN_INFO "%s: Test case %d. %s...\n",
934 mmc_hostname(test->card->host), i + 1,
935 mmc_test_cases[i].name);
937 if (mmc_test_cases[i].prepare) {
938 ret = mmc_test_cases[i].prepare(test);
940 printk(KERN_INFO "%s: Result: Prepare "
941 "stage failed! (%d)\n",
942 mmc_hostname(test->card->host),
948 ret = mmc_test_cases[i].run(test);
951 printk(KERN_INFO "%s: Result: OK\n",
952 mmc_hostname(test->card->host));
955 printk(KERN_INFO "%s: Result: FAILED\n",
956 mmc_hostname(test->card->host));
958 case RESULT_UNSUP_HOST:
959 printk(KERN_INFO "%s: Result: UNSUPPORTED "
961 mmc_hostname(test->card->host));
963 case RESULT_UNSUP_CARD:
964 printk(KERN_INFO "%s: Result: UNSUPPORTED "
966 mmc_hostname(test->card->host));
969 printk(KERN_INFO "%s: Result: ERROR (%d)\n",
970 mmc_hostname(test->card->host), ret);
973 if (mmc_test_cases[i].cleanup) {
974 ret = mmc_test_cases[i].cleanup(test);
976 printk(KERN_INFO "%s: Warning: Cleanup "
977 "stage failed! (%d)\n",
978 mmc_hostname(test->card->host),
984 mmc_release_host(test->card->host);
986 printk(KERN_INFO "%s: Tests completed.\n",
987 mmc_hostname(test->card->host));
990 static ssize_t mmc_test_show(struct device *dev,
991 struct device_attribute *attr, char *buf)
993 mutex_lock(&mmc_test_lock);
994 mutex_unlock(&mmc_test_lock);
999 static ssize_t mmc_test_store(struct device *dev,
1000 struct device_attribute *attr, const char *buf, size_t count)
1002 struct mmc_card *card;
1003 struct mmc_test_card *test;
1006 card = container_of(dev, struct mmc_card, dev);
1008 testcase = simple_strtol(buf, NULL, 10);
1010 test = kzalloc(sizeof(struct mmc_test_card), GFP_KERNEL);
1016 test->buffer = kzalloc(BUFFER_SIZE, GFP_KERNEL);
1018 mutex_lock(&mmc_test_lock);
1019 mmc_test_run(test, testcase);
1020 mutex_unlock(&mmc_test_lock);
1023 kfree(test->buffer);
1029 static DEVICE_ATTR(test, S_IWUSR | S_IRUGO, mmc_test_show, mmc_test_store);
1031 static int mmc_test_probe(struct mmc_card *card)
1035 if ((card->type != MMC_TYPE_MMC) && (card->type != MMC_TYPE_SD))
1038 mutex_init(&mmc_test_lock);
1040 ret = device_create_file(&card->dev, &dev_attr_test);
1047 static void mmc_test_remove(struct mmc_card *card)
1049 device_remove_file(&card->dev, &dev_attr_test);
1052 static struct mmc_driver mmc_driver = {
1056 .probe = mmc_test_probe,
1057 .remove = mmc_test_remove,
1060 static int __init mmc_test_init(void)
1062 return mmc_register_driver(&mmc_driver);
1065 static void __exit mmc_test_exit(void)
1067 mmc_unregister_driver(&mmc_driver);
1070 module_init(mmc_test_init);
1071 module_exit(mmc_test_exit);
1073 MODULE_LICENSE("GPL");
1074 MODULE_DESCRIPTION("Multimedia Card (MMC) host test driver");
1075 MODULE_AUTHOR("Pierre Ossman");