diff options
Diffstat (limited to 'drivers/ieee1394/cmp.c')
-rw-r--r-- | drivers/ieee1394/cmp.c | 311 |
1 files changed, 311 insertions, 0 deletions
diff --git a/drivers/ieee1394/cmp.c b/drivers/ieee1394/cmp.c new file mode 100644 index 000000000000..69aed26e83a1 --- /dev/null +++ b/drivers/ieee1394/cmp.c | |||
@@ -0,0 +1,311 @@ | |||
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 | |||
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; | ||
52 | }; | ||
53 | |||
54 | struct 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 | |||
70 | enum { | ||
71 | CMP_P2P_CONNECTION, | ||
72 | CMP_BC_CONNECTION | ||
73 | }; | ||
74 | |||
75 | #define CSR_PCR_MAP 0x900 | ||
76 | #define CSR_PCR_MAP_END 0x9fc | ||
77 | |||
78 | static struct hpsb_highlevel cmp_highlevel; | ||
79 | |||
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); | ||
86 | |||
87 | static struct hpsb_highlevel cmp_highlevel = { | ||
88 | .name = "cmp", | ||
89 | .add_host = cmp_add_host, | ||
90 | .host_reset = cmp_host_reset, | ||
91 | }; | ||
92 | |||
93 | static struct hpsb_address_ops pcr_ops = { | ||
94 | .read = pcr_read, | ||
95 | .lock = pcr_lock, | ||
96 | }; | ||
97 | |||
98 | |||
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) | ||
103 | { | ||
104 | struct cmp_host *ch; | ||
105 | struct plug *plug; | ||
106 | |||
107 | ch = hpsb_get_hostinfo(&cmp_highlevel, host); | ||
108 | |||
109 | if (opcr_number >= ch->u.ompr.nplugs || | ||
110 | ch->opcr[opcr_number].update != NULL) | ||
111 | return NULL; | ||
112 | |||
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; | ||
121 | |||
122 | return &plug->u.pcr; | ||
123 | } | ||
124 | |||
125 | void cmp_unregister_opcr(struct hpsb_host *host, struct cmp_pcr *opcr) | ||
126 | { | ||
127 | struct cmp_host *ch; | ||
128 | struct plug *plug; | ||
129 | |||
130 | ch = hpsb_get_hostinfo(&cmp_highlevel, host); | ||
131 | plug = (struct plug *)opcr; | ||
132 | if (plug - ch->opcr >= ch->u.ompr.nplugs) BUG(); | ||
133 | |||
134 | plug->u.pcr.online = 0; | ||
135 | plug->update = NULL; | ||
136 | } | ||
137 | |||
138 | static void reset_plugs(struct cmp_host *ch) | ||
139 | { | ||
140 | int i; | ||
141 | |||
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; | ||
147 | } | ||
148 | } | ||
149 | |||
150 | static void cmp_add_host(struct hpsb_host *host) | ||
151 | { | ||
152 | struct cmp_host *ch = hpsb_create_hostinfo(&cmp_highlevel, host, sizeof (*ch)); | ||
153 | |||
154 | if (ch == NULL) { | ||
155 | HPSB_ERR("Failed to allocate cmp_host"); | ||
156 | return; | ||
157 | } | ||
158 | |||
159 | hpsb_register_addrspace(&cmp_highlevel, host, &pcr_ops, | ||
160 | CSR_REGISTER_BASE + CSR_PCR_MAP, | ||
161 | CSR_REGISTER_BASE + CSR_PCR_MAP_END); | ||
162 | |||
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; | ||
167 | |||
168 | reset_plugs(ch); | ||
169 | } | ||
170 | |||
171 | static void cmp_host_reset(struct hpsb_host *host) | ||
172 | { | ||
173 | struct cmp_host *ch; | ||
174 | |||
175 | ch = hpsb_get_hostinfo(&cmp_highlevel, host); | ||
176 | if (ch == NULL) { | ||
177 | HPSB_ERR("cmp: Tried to reset unknown host"); | ||
178 | return; | ||
179 | } | ||
180 | |||
181 | reset_plugs(ch); | ||
182 | } | ||
183 | |||
184 | static int pcr_read(struct hpsb_host *host, int nodeid, quadlet_t *buf, | ||
185 | u64 addr, size_t length, u16 flags) | ||
186 | { | ||
187 | int csraddr = addr - CSR_REGISTER_BASE; | ||
188 | int plug; | ||
189 | struct cmp_host *ch; | ||
190 | |||
191 | if (length != 4) | ||
192 | return RCODE_TYPE_ERROR; | ||
193 | |||
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; | ||
198 | } | ||
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; | ||
203 | } | ||
204 | else if (csraddr < 0x980) { | ||
205 | return RCODE_ADDRESS_ERROR; | ||
206 | } | ||
207 | else if (csraddr == 0x980) { | ||
208 | *buf = cpu_to_be32(ch->v.impr_quadlet); | ||
209 | return RCODE_COMPLETE; | ||
210 | } | ||
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; | ||
215 | } | ||
216 | else | ||
217 | return RCODE_ADDRESS_ERROR; | ||
218 | } | ||
219 | |||
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) | ||
222 | { | ||
223 | int csraddr = addr - CSR_REGISTER_BASE; | ||
224 | int plug; | ||
225 | struct cmp_host *ch; | ||
226 | |||
227 | ch = hpsb_get_hostinfo(&cmp_highlevel, host); | ||
228 | |||
229 | if (extcode != EXTCODE_COMPARE_SWAP) | ||
230 | return RCODE_TYPE_ERROR; | ||
231 | |||
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); | ||
237 | |||
238 | return RCODE_COMPLETE; | ||
239 | } | ||
240 | if (csraddr < 0x904 + ch->u.ompr.nplugs * 4) { | ||
241 | plug = (csraddr - 0x904) / 4; | ||
242 | *store = cpu_to_be32(ch->opcr[plug].u.quadlet); | ||
243 | |||
244 | if (arg == *store) | ||
245 | ch->opcr[plug].u.quadlet = be32_to_cpu(data); | ||
246 | |||
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); | ||
251 | |||
252 | return RCODE_COMPLETE; | ||
253 | } | ||
254 | else if (csraddr < 0x980) { | ||
255 | return RCODE_ADDRESS_ERROR; | ||
256 | } | ||
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); | ||
262 | |||
263 | return RCODE_COMPLETE; | ||
264 | } | ||
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); | ||
268 | |||
269 | if (arg == *store) | ||
270 | ch->ipcr[plug].u.quadlet = be32_to_cpu(data); | ||
271 | |||
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); | ||
276 | |||
277 | return RCODE_COMPLETE; | ||
278 | } | ||
279 | else | ||
280 | return RCODE_ADDRESS_ERROR; | ||
281 | } | ||
282 | |||
283 | |||
284 | /* Module interface */ | ||
285 | |||
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"); | ||
290 | |||
291 | EXPORT_SYMBOL(cmp_register_opcr); | ||
292 | EXPORT_SYMBOL(cmp_unregister_opcr); | ||
293 | |||
294 | static int __init cmp_init_module (void) | ||
295 | { | ||
296 | hpsb_register_highlevel (&cmp_highlevel); | ||
297 | |||
298 | HPSB_INFO("Loaded CMP driver"); | ||
299 | |||
300 | return 0; | ||
301 | } | ||
302 | |||
303 | static void __exit cmp_exit_module (void) | ||
304 | { | ||
305 | hpsb_unregister_highlevel(&cmp_highlevel); | ||
306 | |||
307 | HPSB_INFO("Unloaded CMP driver"); | ||
308 | } | ||
309 | |||
310 | module_init(cmp_init_module); | ||
311 | module_exit(cmp_exit_module); | ||