PageRenderTime 29ms CodeModel.GetById 12ms app.highlight 14ms RepoModel.GetById 1ms app.codeStats 0ms

/drivers/ieee1394/cmp.c

https://bitbucket.org/abioy/linux
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);