/drivers/ieee1394/cmp.c

https://bitbucket.org/abioy/linux · C · 303 lines · 216 code · 55 blank · 32 comment · 47 complexity · 8e1b78802128dfb322c85cc853b2639d MD5 · raw file

  1. /* -*- c-basic-offset: 8 -*-
  2. *
  3. * cmp.c - Connection Management Procedures
  4. * Copyright (C) 2001 Kristian Høgsberg
  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
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  19. */
  20. /* TODO
  21. * ----
  22. *
  23. * - Implement IEC61883-1 output plugs and connection management.
  24. * This should probably be part of the general subsystem, as it could
  25. * be shared with dv1394.
  26. *
  27. * - Add IEC61883 unit directory when loading this module. This
  28. * requires a run-time changeable config rom.
  29. */
  30. #include <linux/module.h>
  31. #include <linux/list.h>
  32. #include <linux/sched.h>
  33. #include <linux/types.h>
  34. #include <linux/wait.h>
  35. #include <linux/interrupt.h>
  36. #include "hosts.h"
  37. #include "highlevel.h"
  38. #include "ieee1394.h"
  39. #include "ieee1394_core.h"
  40. #include "cmp.h"
  41. struct plug {
  42. union {
  43. struct cmp_pcr pcr;
  44. quadlet_t quadlet;
  45. } u;
  46. void (*update)(struct cmp_pcr *plug, void *data);
  47. void *data;
  48. };
  49. struct cmp_host {
  50. struct hpsb_host *host;
  51. union {
  52. struct cmp_mpr ompr;
  53. quadlet_t ompr_quadlet;
  54. } u;
  55. struct plug opcr[2];
  56. union {
  57. struct cmp_mpr impr;
  58. quadlet_t impr_quadlet;
  59. } v;
  60. struct plug ipcr[2];
  61. };
  62. enum {
  63. CMP_P2P_CONNECTION,
  64. CMP_BC_CONNECTION
  65. };
  66. #define CSR_PCR_MAP 0x900
  67. #define CSR_PCR_MAP_END 0x9fc
  68. static struct hpsb_highlevel cmp_highlevel;
  69. struct cmp_pcr *
  70. cmp_register_opcr(struct hpsb_host *host, int opcr_number, int payload,
  71. void (*update)(struct cmp_pcr *pcr, void *data),
  72. void *data)
  73. {
  74. struct cmp_host *ch;
  75. struct plug *plug;
  76. ch = hpsb_get_hostinfo(&cmp_highlevel, host);
  77. if (opcr_number >= ch->u.ompr.nplugs ||
  78. ch->opcr[opcr_number].update != NULL)
  79. return NULL;
  80. plug = &ch->opcr[opcr_number];
  81. plug->u.pcr.online = 1;
  82. plug->u.pcr.bcast_count = 0;
  83. plug->u.pcr.p2p_count = 0;
  84. plug->u.pcr.overhead = 0;
  85. plug->u.pcr.payload = payload;
  86. plug->update = update;
  87. plug->data = data;
  88. return &plug->u.pcr;
  89. }
  90. void cmp_unregister_opcr(struct hpsb_host *host, struct cmp_pcr *opcr)
  91. {
  92. struct cmp_host *ch;
  93. struct plug *plug;
  94. ch = hpsb_get_hostinfo(&cmp_highlevel, host);
  95. plug = (struct plug *)opcr;
  96. if (plug - ch->opcr >= ch->u.ompr.nplugs) BUG();
  97. plug->u.pcr.online = 0;
  98. plug->update = NULL;
  99. }
  100. static void reset_plugs(struct cmp_host *ch)
  101. {
  102. int i;
  103. ch->u.ompr.non_persistent_ext = 0xff;
  104. for (i = 0; i < ch->u.ompr.nplugs; i++) {
  105. ch->opcr[i].u.pcr.bcast_count = 0;
  106. ch->opcr[i].u.pcr.p2p_count = 0;
  107. ch->opcr[i].u.pcr.overhead = 0;
  108. }
  109. }
  110. static void cmp_add_host(struct hpsb_host *host)
  111. {
  112. struct cmp_host *ch = hpsb_create_hostinfo(&cmp_highlevel, host, sizeof (*ch));
  113. if (ch == NULL) {
  114. HPSB_ERR("Failed to allocate cmp_host");
  115. return;
  116. }
  117. ch->host = host;
  118. ch->u.ompr.rate = IEEE1394_SPEED_100;
  119. ch->u.ompr.bcast_channel_base = 63;
  120. ch->u.ompr.nplugs = 2;
  121. reset_plugs(ch);
  122. }
  123. static void cmp_host_reset(struct hpsb_host *host)
  124. {
  125. struct cmp_host *ch;
  126. ch = hpsb_get_hostinfo(&cmp_highlevel, host);
  127. if (ch == NULL) {
  128. HPSB_ERR("cmp: Tried to reset unknown host");
  129. return;
  130. }
  131. reset_plugs(ch);
  132. }
  133. static int pcr_read(struct hpsb_host *host, int nodeid, quadlet_t *buf,
  134. u64 addr, size_t length, u16 flags)
  135. {
  136. int csraddr = addr - CSR_REGISTER_BASE;
  137. int plug;
  138. struct cmp_host *ch;
  139. if (length != 4)
  140. return RCODE_TYPE_ERROR;
  141. ch = hpsb_get_hostinfo(&cmp_highlevel, host);
  142. if (csraddr == 0x900) {
  143. *buf = cpu_to_be32(ch->u.ompr_quadlet);
  144. return RCODE_COMPLETE;
  145. }
  146. else if (csraddr < 0x904 + ch->u.ompr.nplugs * 4) {
  147. plug = (csraddr - 0x904) / 4;
  148. *buf = cpu_to_be32(ch->opcr[plug].u.quadlet);
  149. return RCODE_COMPLETE;
  150. }
  151. else if (csraddr < 0x980) {
  152. return RCODE_ADDRESS_ERROR;
  153. }
  154. else if (csraddr == 0x980) {
  155. *buf = cpu_to_be32(ch->v.impr_quadlet);
  156. return RCODE_COMPLETE;
  157. }
  158. else if (csraddr < 0x984 + ch->v.impr.nplugs * 4) {
  159. plug = (csraddr - 0x984) / 4;
  160. *buf = cpu_to_be32(ch->ipcr[plug].u.quadlet);
  161. return RCODE_COMPLETE;
  162. }
  163. else
  164. return RCODE_ADDRESS_ERROR;
  165. }
  166. static int pcr_lock(struct hpsb_host *host, int nodeid, quadlet_t *store,
  167. u64 addr, quadlet_t data, quadlet_t arg, int extcode, u16 flags)
  168. {
  169. int csraddr = addr - CSR_REGISTER_BASE;
  170. int plug;
  171. struct cmp_host *ch;
  172. ch = hpsb_get_hostinfo(&cmp_highlevel, host);
  173. if (extcode != EXTCODE_COMPARE_SWAP)
  174. return RCODE_TYPE_ERROR;
  175. if (csraddr == 0x900) {
  176. /* FIXME: Ignore writes to bits 30-31 and 0-7 */
  177. *store = cpu_to_be32(ch->u.ompr_quadlet);
  178. if (arg == cpu_to_be32(ch->u.ompr_quadlet))
  179. ch->u.ompr_quadlet = be32_to_cpu(data);
  180. return RCODE_COMPLETE;
  181. }
  182. if (csraddr < 0x904 + ch->u.ompr.nplugs * 4) {
  183. plug = (csraddr - 0x904) / 4;
  184. *store = cpu_to_be32(ch->opcr[plug].u.quadlet);
  185. if (arg == *store)
  186. ch->opcr[plug].u.quadlet = be32_to_cpu(data);
  187. if (be32_to_cpu(*store) != ch->opcr[plug].u.quadlet &&
  188. ch->opcr[plug].update != NULL)
  189. ch->opcr[plug].update(&ch->opcr[plug].u.pcr,
  190. ch->opcr[plug].data);
  191. return RCODE_COMPLETE;
  192. }
  193. else if (csraddr < 0x980) {
  194. return RCODE_ADDRESS_ERROR;
  195. }
  196. else if (csraddr == 0x980) {
  197. /* FIXME: Ignore writes to bits 24-31 and 0-7 */
  198. *store = cpu_to_be32(ch->u.ompr_quadlet);
  199. if (arg == cpu_to_be32(ch->u.ompr_quadlet))
  200. ch->u.ompr_quadlet = be32_to_cpu(data);
  201. return RCODE_COMPLETE;
  202. }
  203. else if (csraddr < 0x984 + ch->v.impr.nplugs * 4) {
  204. plug = (csraddr - 0x984) / 4;
  205. *store = cpu_to_be32(ch->ipcr[plug].u.quadlet);
  206. if (arg == *store)
  207. ch->ipcr[plug].u.quadlet = be32_to_cpu(data);
  208. if (be32_to_cpu(*store) != ch->ipcr[plug].u.quadlet &&
  209. ch->ipcr[plug].update != NULL)
  210. ch->ipcr[plug].update(&ch->ipcr[plug].u.pcr,
  211. ch->ipcr[plug].data);
  212. return RCODE_COMPLETE;
  213. }
  214. else
  215. return RCODE_ADDRESS_ERROR;
  216. }
  217. static struct hpsb_highlevel cmp_highlevel = {
  218. .name = "cmp",
  219. .add_host = cmp_add_host,
  220. .host_reset = cmp_host_reset,
  221. };
  222. static struct hpsb_address_ops pcr_ops = {
  223. .read = pcr_read,
  224. .lock = pcr_lock,
  225. };
  226. /* Module interface */
  227. MODULE_AUTHOR("Kristian Hogsberg <hogsberg@users.sf.net>");
  228. MODULE_DESCRIPTION("Connection Management Procedures (CMP)");
  229. MODULE_SUPPORTED_DEVICE("cmp");
  230. MODULE_LICENSE("GPL");
  231. EXPORT_SYMBOL(cmp_register_opcr);
  232. EXPORT_SYMBOL(cmp_unregister_opcr);
  233. static int __init cmp_init_module (void)
  234. {
  235. hpsb_register_highlevel (&cmp_highlevel);
  236. hpsb_register_addrspace(&cmp_highlevel, &pcr_ops,
  237. CSR_REGISTER_BASE + CSR_PCR_MAP,
  238. CSR_REGISTER_BASE + CSR_PCR_MAP_END);
  239. HPSB_INFO("Loaded CMP driver");
  240. return 0;
  241. }
  242. static void __exit cmp_exit_module (void)
  243. {
  244. hpsb_unregister_highlevel(&cmp_highlevel);
  245. HPSB_INFO("Unloaded CMP driver");
  246. }
  247. module_init(cmp_init_module);
  248. module_exit(cmp_exit_module);