IPoIB: Fix memory leak of multicast group structures
[linux-2.6/linux-acpi-2.6/ibm-acpi-2.6.git] / drivers / ieee1394 / cmp.c
blob69aed26e83a1a97d1634225765396a4220540389
1 /* -*- c-basic-offset: 8 -*-
3 * cmp.c - Connection Management Procedures
4 * Copyright (C) 2001 Kristian Høgsberg
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.
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.
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.
21 /* TODO
22 * ----
24 * - Implement IEC61883-1 output plugs and connection management.
25 * This should probably be part of the general subsystem, as it could
26 * be shared with dv1394.
28 * - Add IEC61883 unit directory when loading this module. This
29 * requires a run-time changeable config rom.
32 #include <linux/module.h>
33 #include <linux/list.h>
34 #include <linux/sched.h>
35 #include <linux/types.h>
36 #include <linux/wait.h>
37 #include <linux/interrupt.h>
39 #include "hosts.h"
40 #include "highlevel.h"
41 #include "ieee1394.h"
42 #include "ieee1394_core.h"
43 #include "cmp.h"
45 struct plug {
46 union {
47 struct cmp_pcr pcr;
48 quadlet_t quadlet;
49 } u;
50 void (*update)(struct cmp_pcr *plug, void *data);
51 void *data;
54 struct cmp_host {
55 struct hpsb_host *host;
57 union {
58 struct cmp_mpr ompr;
59 quadlet_t ompr_quadlet;
60 } u;
61 struct plug opcr[2];
63 union {
64 struct cmp_mpr impr;
65 quadlet_t impr_quadlet;
66 } v;
67 struct plug ipcr[2];
70 enum {
71 CMP_P2P_CONNECTION,
72 CMP_BC_CONNECTION
75 #define CSR_PCR_MAP 0x900
76 #define CSR_PCR_MAP_END 0x9fc
78 static struct hpsb_highlevel cmp_highlevel;
80 static void cmp_add_host(struct hpsb_host *host);
81 static void cmp_host_reset(struct hpsb_host *host);
82 static int pcr_read(struct hpsb_host *host, int nodeid, quadlet_t *buf,
83 u64 addr, size_t length, u16 flags);
84 static int pcr_lock(struct hpsb_host *host, int nodeid, quadlet_t *store,
85 u64 addr, quadlet_t data, quadlet_t arg, int extcode, u16 flags);
87 static struct hpsb_highlevel cmp_highlevel = {
88 .name = "cmp",
89 .add_host = cmp_add_host,
90 .host_reset = cmp_host_reset,
93 static struct hpsb_address_ops pcr_ops = {
94 .read = pcr_read,
95 .lock = pcr_lock,
99 struct cmp_pcr *
100 cmp_register_opcr(struct hpsb_host *host, int opcr_number, int payload,
101 void (*update)(struct cmp_pcr *pcr, void *data),
102 void *data)
104 struct cmp_host *ch;
105 struct plug *plug;
107 ch = hpsb_get_hostinfo(&cmp_highlevel, host);
109 if (opcr_number >= ch->u.ompr.nplugs ||
110 ch->opcr[opcr_number].update != NULL)
111 return NULL;
113 plug = &ch->opcr[opcr_number];
114 plug->u.pcr.online = 1;
115 plug->u.pcr.bcast_count = 0;
116 plug->u.pcr.p2p_count = 0;
117 plug->u.pcr.overhead = 0;
118 plug->u.pcr.payload = payload;
119 plug->update = update;
120 plug->data = data;
122 return &plug->u.pcr;
125 void cmp_unregister_opcr(struct hpsb_host *host, struct cmp_pcr *opcr)
127 struct cmp_host *ch;
128 struct plug *plug;
130 ch = hpsb_get_hostinfo(&cmp_highlevel, host);
131 plug = (struct plug *)opcr;
132 if (plug - ch->opcr >= ch->u.ompr.nplugs) BUG();
134 plug->u.pcr.online = 0;
135 plug->update = NULL;
138 static void reset_plugs(struct cmp_host *ch)
140 int i;
142 ch->u.ompr.non_persistent_ext = 0xff;
143 for (i = 0; i < ch->u.ompr.nplugs; i++) {
144 ch->opcr[i].u.pcr.bcast_count = 0;
145 ch->opcr[i].u.pcr.p2p_count = 0;
146 ch->opcr[i].u.pcr.overhead = 0;
150 static void cmp_add_host(struct hpsb_host *host)
152 struct cmp_host *ch = hpsb_create_hostinfo(&cmp_highlevel, host, sizeof (*ch));
154 if (ch == NULL) {
155 HPSB_ERR("Failed to allocate cmp_host");
156 return;
159 hpsb_register_addrspace(&cmp_highlevel, host, &pcr_ops,
160 CSR_REGISTER_BASE + CSR_PCR_MAP,
161 CSR_REGISTER_BASE + CSR_PCR_MAP_END);
163 ch->host = host;
164 ch->u.ompr.rate = IEEE1394_SPEED_100;
165 ch->u.ompr.bcast_channel_base = 63;
166 ch->u.ompr.nplugs = 2;
168 reset_plugs(ch);
171 static void cmp_host_reset(struct hpsb_host *host)
173 struct cmp_host *ch;
175 ch = hpsb_get_hostinfo(&cmp_highlevel, host);
176 if (ch == NULL) {
177 HPSB_ERR("cmp: Tried to reset unknown host");
178 return;
181 reset_plugs(ch);
184 static int pcr_read(struct hpsb_host *host, int nodeid, quadlet_t *buf,
185 u64 addr, size_t length, u16 flags)
187 int csraddr = addr - CSR_REGISTER_BASE;
188 int plug;
189 struct cmp_host *ch;
191 if (length != 4)
192 return RCODE_TYPE_ERROR;
194 ch = hpsb_get_hostinfo(&cmp_highlevel, host);
195 if (csraddr == 0x900) {
196 *buf = cpu_to_be32(ch->u.ompr_quadlet);
197 return RCODE_COMPLETE;
199 else if (csraddr < 0x904 + ch->u.ompr.nplugs * 4) {
200 plug = (csraddr - 0x904) / 4;
201 *buf = cpu_to_be32(ch->opcr[plug].u.quadlet);
202 return RCODE_COMPLETE;
204 else if (csraddr < 0x980) {
205 return RCODE_ADDRESS_ERROR;
207 else if (csraddr == 0x980) {
208 *buf = cpu_to_be32(ch->v.impr_quadlet);
209 return RCODE_COMPLETE;
211 else if (csraddr < 0x984 + ch->v.impr.nplugs * 4) {
212 plug = (csraddr - 0x984) / 4;
213 *buf = cpu_to_be32(ch->ipcr[plug].u.quadlet);
214 return RCODE_COMPLETE;
216 else
217 return RCODE_ADDRESS_ERROR;
220 static int pcr_lock(struct hpsb_host *host, int nodeid, quadlet_t *store,
221 u64 addr, quadlet_t data, quadlet_t arg, int extcode, u16 flags)
223 int csraddr = addr - CSR_REGISTER_BASE;
224 int plug;
225 struct cmp_host *ch;
227 ch = hpsb_get_hostinfo(&cmp_highlevel, host);
229 if (extcode != EXTCODE_COMPARE_SWAP)
230 return RCODE_TYPE_ERROR;
232 if (csraddr == 0x900) {
233 /* FIXME: Ignore writes to bits 30-31 and 0-7 */
234 *store = cpu_to_be32(ch->u.ompr_quadlet);
235 if (arg == cpu_to_be32(ch->u.ompr_quadlet))
236 ch->u.ompr_quadlet = be32_to_cpu(data);
238 return RCODE_COMPLETE;
240 if (csraddr < 0x904 + ch->u.ompr.nplugs * 4) {
241 plug = (csraddr - 0x904) / 4;
242 *store = cpu_to_be32(ch->opcr[plug].u.quadlet);
244 if (arg == *store)
245 ch->opcr[plug].u.quadlet = be32_to_cpu(data);
247 if (be32_to_cpu(*store) != ch->opcr[plug].u.quadlet &&
248 ch->opcr[plug].update != NULL)
249 ch->opcr[plug].update(&ch->opcr[plug].u.pcr,
250 ch->opcr[plug].data);
252 return RCODE_COMPLETE;
254 else if (csraddr < 0x980) {
255 return RCODE_ADDRESS_ERROR;
257 else if (csraddr == 0x980) {
258 /* FIXME: Ignore writes to bits 24-31 and 0-7 */
259 *store = cpu_to_be32(ch->u.ompr_quadlet);
260 if (arg == cpu_to_be32(ch->u.ompr_quadlet))
261 ch->u.ompr_quadlet = be32_to_cpu(data);
263 return RCODE_COMPLETE;
265 else if (csraddr < 0x984 + ch->v.impr.nplugs * 4) {
266 plug = (csraddr - 0x984) / 4;
267 *store = cpu_to_be32(ch->ipcr[plug].u.quadlet);
269 if (arg == *store)
270 ch->ipcr[plug].u.quadlet = be32_to_cpu(data);
272 if (be32_to_cpu(*store) != ch->ipcr[plug].u.quadlet &&
273 ch->ipcr[plug].update != NULL)
274 ch->ipcr[plug].update(&ch->ipcr[plug].u.pcr,
275 ch->ipcr[plug].data);
277 return RCODE_COMPLETE;
279 else
280 return RCODE_ADDRESS_ERROR;
284 /* Module interface */
286 MODULE_AUTHOR("Kristian Hogsberg <hogsberg@users.sf.net>");
287 MODULE_DESCRIPTION("Connection Management Procedures (CMP)");
288 MODULE_SUPPORTED_DEVICE("cmp");
289 MODULE_LICENSE("GPL");
291 EXPORT_SYMBOL(cmp_register_opcr);
292 EXPORT_SYMBOL(cmp_unregister_opcr);
294 static int __init cmp_init_module (void)
296 hpsb_register_highlevel (&cmp_highlevel);
298 HPSB_INFO("Loaded CMP driver");
300 return 0;
303 static void __exit cmp_exit_module (void)
305 hpsb_unregister_highlevel(&cmp_highlevel);
307 HPSB_INFO("Unloaded CMP driver");
310 module_init(cmp_init_module);
311 module_exit(cmp_exit_module);