/drivers/ieee1394/cmp.c
C | 303 lines | 216 code | 55 blank | 32 comment | 47 complexity | 8e1b78802128dfb322c85cc853b2639d MD5 | raw file
Possible License(s): CC-BY-SA-3.0, GPL-2.0, LGPL-2.0, AGPL-1.0
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
21/* TODO
22 * ----
23 *
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.
27 *
28 * - Add IEC61883 unit directory when loading this module. This
29 * requires a run-time changeable config rom.
30 */
31
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>
38
39#include "hosts.h"
40#include "highlevel.h"
41#include "ieee1394.h"
42#include "ieee1394_core.h"
43#include "cmp.h"
44
45struct 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;
52};
53
54struct cmp_host {
55 struct hpsb_host *host;
56
57 union {
58 struct cmp_mpr ompr;
59 quadlet_t ompr_quadlet;
60 } u;
61 struct plug opcr[2];
62
63 union {
64 struct cmp_mpr impr;
65 quadlet_t impr_quadlet;
66 } v;
67 struct plug ipcr[2];
68};
69
70enum {
71 CMP_P2P_CONNECTION,
72 CMP_BC_CONNECTION
73};
74
75#define CSR_PCR_MAP 0x900
76#define CSR_PCR_MAP_END 0x9fc
77
78static struct hpsb_highlevel cmp_highlevel;
79
80struct cmp_pcr *
81cmp_register_opcr(struct hpsb_host *host, int opcr_number, int payload,
82 void (*update)(struct cmp_pcr *pcr, void *data),
83 void *data)
84{
85 struct cmp_host *ch;
86 struct plug *plug;
87
88 ch = hpsb_get_hostinfo(&cmp_highlevel, host);
89
90 if (opcr_number >= ch->u.ompr.nplugs ||
91 ch->opcr[opcr_number].update != NULL)
92 return NULL;
93
94 plug = &ch->opcr[opcr_number];
95 plug->u.pcr.online = 1;
96 plug->u.pcr.bcast_count = 0;
97 plug->u.pcr.p2p_count = 0;
98 plug->u.pcr.overhead = 0;
99 plug->u.pcr.payload = payload;
100 plug->update = update;
101 plug->data = data;
102
103 return &plug->u.pcr;
104}
105
106void cmp_unregister_opcr(struct hpsb_host *host, struct cmp_pcr *opcr)
107{
108 struct cmp_host *ch;
109 struct plug *plug;
110
111 ch = hpsb_get_hostinfo(&cmp_highlevel, host);
112 plug = (struct plug *)opcr;
113 if (plug - ch->opcr >= ch->u.ompr.nplugs) BUG();
114
115 plug->u.pcr.online = 0;
116 plug->update = NULL;
117}
118
119static void reset_plugs(struct cmp_host *ch)
120{
121 int i;
122
123 ch->u.ompr.non_persistent_ext = 0xff;
124 for (i = 0; i < ch->u.ompr.nplugs; i++) {
125 ch->opcr[i].u.pcr.bcast_count = 0;
126 ch->opcr[i].u.pcr.p2p_count = 0;
127 ch->opcr[i].u.pcr.overhead = 0;
128 }
129}
130
131static void cmp_add_host(struct hpsb_host *host)
132{
133 struct cmp_host *ch = hpsb_create_hostinfo(&cmp_highlevel, host, sizeof (*ch));
134
135 if (ch == NULL) {
136 HPSB_ERR("Failed to allocate cmp_host");
137 return;
138 }
139
140 ch->host = host;
141 ch->u.ompr.rate = IEEE1394_SPEED_100;
142 ch->u.ompr.bcast_channel_base = 63;
143 ch->u.ompr.nplugs = 2;
144
145 reset_plugs(ch);
146}
147
148static void cmp_host_reset(struct hpsb_host *host)
149{
150 struct cmp_host *ch;
151
152 ch = hpsb_get_hostinfo(&cmp_highlevel, host);
153 if (ch == NULL) {
154 HPSB_ERR("cmp: Tried to reset unknown host");
155 return;
156 }
157
158 reset_plugs(ch);
159}
160
161static int pcr_read(struct hpsb_host *host, int nodeid, quadlet_t *buf,
162 u64 addr, size_t length, u16 flags)
163{
164 int csraddr = addr - CSR_REGISTER_BASE;
165 int plug;
166 struct cmp_host *ch;
167
168 if (length != 4)
169 return RCODE_TYPE_ERROR;
170
171 ch = hpsb_get_hostinfo(&cmp_highlevel, host);
172 if (csraddr == 0x900) {
173 *buf = cpu_to_be32(ch->u.ompr_quadlet);
174 return RCODE_COMPLETE;
175 }
176 else if (csraddr < 0x904 + ch->u.ompr.nplugs * 4) {
177 plug = (csraddr - 0x904) / 4;
178 *buf = cpu_to_be32(ch->opcr[plug].u.quadlet);
179 return RCODE_COMPLETE;
180 }
181 else if (csraddr < 0x980) {
182 return RCODE_ADDRESS_ERROR;
183 }
184 else if (csraddr == 0x980) {
185 *buf = cpu_to_be32(ch->v.impr_quadlet);
186 return RCODE_COMPLETE;
187 }
188 else if (csraddr < 0x984 + ch->v.impr.nplugs * 4) {
189 plug = (csraddr - 0x984) / 4;
190 *buf = cpu_to_be32(ch->ipcr[plug].u.quadlet);
191 return RCODE_COMPLETE;
192 }
193 else
194 return RCODE_ADDRESS_ERROR;
195}
196
197static int pcr_lock(struct hpsb_host *host, int nodeid, quadlet_t *store,
198 u64 addr, quadlet_t data, quadlet_t arg, int extcode, u16 flags)
199{
200 int csraddr = addr - CSR_REGISTER_BASE;
201 int plug;
202 struct cmp_host *ch;
203
204 ch = hpsb_get_hostinfo(&cmp_highlevel, host);
205
206 if (extcode != EXTCODE_COMPARE_SWAP)
207 return RCODE_TYPE_ERROR;
208
209 if (csraddr == 0x900) {
210 /* FIXME: Ignore writes to bits 30-31 and 0-7 */
211 *store = cpu_to_be32(ch->u.ompr_quadlet);
212 if (arg == cpu_to_be32(ch->u.ompr_quadlet))
213 ch->u.ompr_quadlet = be32_to_cpu(data);
214
215 return RCODE_COMPLETE;
216 }
217 if (csraddr < 0x904 + ch->u.ompr.nplugs * 4) {
218 plug = (csraddr - 0x904) / 4;
219 *store = cpu_to_be32(ch->opcr[plug].u.quadlet);
220
221 if (arg == *store)
222 ch->opcr[plug].u.quadlet = be32_to_cpu(data);
223
224 if (be32_to_cpu(*store) != ch->opcr[plug].u.quadlet &&
225 ch->opcr[plug].update != NULL)
226 ch->opcr[plug].update(&ch->opcr[plug].u.pcr,
227 ch->opcr[plug].data);
228
229 return RCODE_COMPLETE;
230 }
231 else if (csraddr < 0x980) {
232 return RCODE_ADDRESS_ERROR;
233 }
234 else if (csraddr == 0x980) {
235 /* FIXME: Ignore writes to bits 24-31 and 0-7 */
236 *store = cpu_to_be32(ch->u.ompr_quadlet);
237 if (arg == cpu_to_be32(ch->u.ompr_quadlet))
238 ch->u.ompr_quadlet = be32_to_cpu(data);
239
240 return RCODE_COMPLETE;
241 }
242 else if (csraddr < 0x984 + ch->v.impr.nplugs * 4) {
243 plug = (csraddr - 0x984) / 4;
244 *store = cpu_to_be32(ch->ipcr[plug].u.quadlet);
245
246 if (arg == *store)
247 ch->ipcr[plug].u.quadlet = be32_to_cpu(data);
248
249 if (be32_to_cpu(*store) != ch->ipcr[plug].u.quadlet &&
250 ch->ipcr[plug].update != NULL)
251 ch->ipcr[plug].update(&ch->ipcr[plug].u.pcr,
252 ch->ipcr[plug].data);
253
254 return RCODE_COMPLETE;
255 }
256 else
257 return RCODE_ADDRESS_ERROR;
258}
259
260
261static struct hpsb_highlevel cmp_highlevel = {
262 .name = "cmp",
263 .add_host = cmp_add_host,
264 .host_reset = cmp_host_reset,
265};
266
267static struct hpsb_address_ops pcr_ops = {
268 .read = pcr_read,
269 .lock = pcr_lock,
270};
271
272/* Module interface */
273
274MODULE_AUTHOR("Kristian Hogsberg <hogsberg@users.sf.net>");
275MODULE_DESCRIPTION("Connection Management Procedures (CMP)");
276MODULE_SUPPORTED_DEVICE("cmp");
277MODULE_LICENSE("GPL");
278
279EXPORT_SYMBOL(cmp_register_opcr);
280EXPORT_SYMBOL(cmp_unregister_opcr);
281
282static int __init cmp_init_module (void)
283{
284 hpsb_register_highlevel (&cmp_highlevel);
285
286 hpsb_register_addrspace(&cmp_highlevel, &pcr_ops,
287 CSR_REGISTER_BASE + CSR_PCR_MAP,
288 CSR_REGISTER_BASE + CSR_PCR_MAP_END);
289
290 HPSB_INFO("Loaded CMP driver");
291
292 return 0;
293}
294
295static void __exit cmp_exit_module (void)
296{
297 hpsb_unregister_highlevel(&cmp_highlevel);
298
299 HPSB_INFO("Unloaded CMP driver");
300}
301
302module_init(cmp_init_module);
303module_exit(cmp_exit_module);