sdio_cis.c 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. /*
  2. * linux/drivers/mmc/core/sdio_cis.c
  3. *
  4. * Author: Nicolas Pitre
  5. * Created: June 11, 2007
  6. * Copyright: MontaVista Software Inc.
  7. *
  8. * Copyright 2007 Pierre Ossman
  9. *
  10. * This program is free software; you can redistribute it and/or modify
  11. * it under the terms of the GNU General Public License as published by
  12. * the Free Software Foundation; either version 2 of the License, or (at
  13. * your option) any later version.
  14. */
  15. #include <linux/kernel.h>
  16. #include <linux/mmc/host.h>
  17. #include <linux/mmc/sdio.h>
  18. #include <linux/mmc/sdio_func.h>
  19. #include "sdio_cis.h"
  20. #include "sdio_ops.h"
  21. static int cistpl_manfid(struct sdio_func *func,
  22. const unsigned char *buf,
  23. unsigned size)
  24. {
  25. /* TPLMID_MANF */
  26. func->vendor = buf[0] | (buf[1] << 8);
  27. /* TPLMID_CARD */
  28. func->device = buf[2] | (buf[3] << 8);
  29. return 0;
  30. }
  31. struct cis_tpl {
  32. unsigned char code;
  33. unsigned char min_size;
  34. int (*parse)(struct sdio_func *, const unsigned char *buf, unsigned size);
  35. };
  36. static const struct cis_tpl cis_tpl_list[] = {
  37. { 0x15, 3, /* cistpl_vers_1 */ },
  38. { 0x20, 4, cistpl_manfid },
  39. { 0x21, 2, /* cistpl_funcid */ },
  40. { 0x22, 0, /* cistpl_funce */ },
  41. };
  42. int sdio_read_cis(struct sdio_func *func)
  43. {
  44. int ret;
  45. struct sdio_func_tuple *this, **prev;
  46. unsigned i, ptr = 0;
  47. for (i = 0; i < 3; i++) {
  48. unsigned char x;
  49. ret = mmc_io_rw_direct(func->card, 0, 0,
  50. func->num * 0x100 + SDIO_FBR_CIS + i, 0, &x);
  51. if (ret)
  52. return ret;
  53. ptr |= x << (i * 8);
  54. }
  55. /* find the list tail */
  56. for (prev = &func->tuples; *prev; prev = &(*prev)->next);
  57. do {
  58. unsigned char tpl_code, tpl_link;
  59. ret = mmc_io_rw_direct(func->card, 0, 0, ptr++, 0, &tpl_code);
  60. if (ret)
  61. break;
  62. /* 0xff means we're done */
  63. if (tpl_code == 0xff)
  64. break;
  65. ret = mmc_io_rw_direct(func->card, 0, 0, ptr++, 0, &tpl_link);
  66. if (ret)
  67. break;
  68. this = kmalloc(sizeof(*this) + tpl_link, GFP_KERNEL);
  69. if (!this)
  70. return -ENOMEM;
  71. for (i = 0; i < tpl_link; i++) {
  72. ret = mmc_io_rw_direct(func->card, 0, 0,
  73. ptr + i, 0, &this->data[i]);
  74. if (ret)
  75. break;
  76. }
  77. if (ret) {
  78. kfree(this);
  79. break;
  80. }
  81. for (i = 0; i < ARRAY_SIZE(cis_tpl_list); i++)
  82. if (cis_tpl_list[i].code == tpl_code)
  83. break;
  84. if (i >= ARRAY_SIZE(cis_tpl_list)) {
  85. /* this tuple is unknown to the core */
  86. this->next = NULL;
  87. this->code = tpl_code;
  88. this->size = tpl_link;
  89. *prev = this;
  90. prev = &this->next;
  91. printk(KERN_DEBUG
  92. "%s: queuing CIS tuple 0x%02x length %u\n",
  93. sdio_func_id(func), tpl_code, tpl_link);
  94. } else {
  95. const struct cis_tpl *tpl = cis_tpl_list + i;
  96. if (tpl_link < tpl->min_size) {
  97. printk(KERN_ERR
  98. "%s: bad CIS tuple 0x%02x (length = %u, expected >= %u)\n",
  99. sdio_func_id(func), tpl_code, tpl_link, tpl->min_size);
  100. ret = -EINVAL;
  101. } else if (tpl->parse)
  102. ret = tpl->parse(func, this->data, tpl_link);
  103. kfree(this);
  104. }
  105. ptr += tpl_link;
  106. } while (!ret);
  107. return ret;
  108. }
  109. void sdio_free_cis(struct sdio_func *func)
  110. {
  111. struct sdio_func_tuple *tuple, *victim;
  112. tuple = func->tuples;
  113. while (tuple) {
  114. victim = tuple;
  115. tuple = tuple->next;
  116. kfree(victim);
  117. }
  118. func->tuples = NULL;
  119. }