- Kai Germaschewski: ymfpci cleanups and resource leak fixes
[davej-history.git] / drivers / ieee1394 / highlevel.c
blob130a405273e6e76b0f080a8238fde61c5f8c8119
1 /*
2 * IEEE 1394 for Linux
4 * Copyright (C) 1999 Andreas E. Bombe
6 * This code is licensed under the GPL. See the file COPYING in the root
7 * directory of the kernel sources for details.
8 */
10 #include <linux/config.h>
11 #include <linux/slab.h>
13 #include "ieee1394.h"
14 #include "ieee1394_types.h"
15 #include "hosts.h"
16 #include "ieee1394_core.h"
17 #include "highlevel.h"
20 LIST_HEAD(hl_drivers);
21 rwlock_t hl_drivers_lock = RW_LOCK_UNLOCKED;
23 LIST_HEAD(addr_space);
24 rwlock_t addr_space_lock = RW_LOCK_UNLOCKED;
26 /* addr_space list will have zero and max already included as bounds */
27 static struct hpsb_address_ops dummy_ops = { NULL, NULL, NULL, NULL };
28 static struct hpsb_address_serve dummy_zero_addr, dummy_max_addr;
30 struct hpsb_highlevel *hpsb_register_highlevel(const char *name,
31 struct hpsb_highlevel_ops *ops)
33 struct hpsb_highlevel *hl;
35 hl = (struct hpsb_highlevel *)kmalloc(sizeof(struct hpsb_highlevel),
36 GFP_KERNEL);
37 if (hl == NULL) {
38 return NULL;
41 INIT_LIST_HEAD(&hl->hl_list);
42 INIT_LIST_HEAD(&hl->addr_list);
43 hl->name = name;
44 hl->op = ops;
46 write_lock_irq(&hl_drivers_lock);
47 hl_all_hosts(hl, 1);
48 list_add_tail(&hl->hl_list, &hl_drivers);
49 write_unlock_irq(&hl_drivers_lock);
51 return hl;
54 void hpsb_unregister_highlevel(struct hpsb_highlevel *hl)
56 struct list_head *entry;
57 struct hpsb_address_serve *as;
59 if (hl == NULL) {
60 return;
63 write_lock_irq(&addr_space_lock);
64 entry = hl->addr_list.next;
66 while (entry != &hl->addr_list) {
67 as = list_entry(entry, struct hpsb_address_serve, addr_list);
68 list_del(&as->as_list);
69 entry = entry->next;
70 kfree(as);
72 write_unlock_irq(&addr_space_lock);
74 write_lock_irq(&hl_drivers_lock);
75 list_del(&hl->hl_list);
76 hl_all_hosts(hl, 0);
77 write_unlock_irq(&hl_drivers_lock);
79 kfree(hl);
82 int hpsb_register_addrspace(struct hpsb_highlevel *hl,
83 struct hpsb_address_ops *ops, u64 start, u64 end)
85 struct hpsb_address_serve *as;
86 struct list_head *entry;
87 int retval = 0;
89 if (((start|end) & 3) || (start >= end) || (end > 0x1000000000000ULL)) {
90 HPSB_ERR(__FUNCTION__ " called with invalid addresses");
91 return 0;
94 as = (struct hpsb_address_serve *)
95 kmalloc(sizeof(struct hpsb_address_serve), GFP_KERNEL);
96 if (as == NULL) {
97 return 0;
100 INIT_LIST_HEAD(&as->as_list);
101 INIT_LIST_HEAD(&as->addr_list);
102 as->op = ops;
103 as->start = start;
104 as->end = end;
106 write_lock_irq(&addr_space_lock);
107 entry = addr_space.next;
109 while (list_entry(entry, struct hpsb_address_serve, as_list)->end
110 <= start) {
111 if (list_entry(entry->next, struct hpsb_address_serve, as_list)
112 ->start >= end) {
113 list_add(&as->as_list, entry);
114 list_add_tail(&as->addr_list, &hl->addr_list);
115 retval = 1;
116 break;
118 entry = entry->next;
120 write_unlock_irq(&addr_space_lock);
122 if (retval == 0) {
123 kfree(as);
126 return retval;
130 void hpsb_listen_channel(struct hpsb_highlevel *hl, struct hpsb_host *host,
131 unsigned int channel)
133 if (channel > 63) {
134 HPSB_ERR(__FUNCTION__ " called with invalid channel");
135 return;
138 if (host->iso_listen_count[channel]++ == 0) {
139 host->template->devctl(host, ISO_LISTEN_CHANNEL, channel);
143 void hpsb_unlisten_channel(struct hpsb_highlevel *hl, struct hpsb_host *host,
144 unsigned int channel)
146 if (channel > 63) {
147 HPSB_ERR(__FUNCTION__ " called with invalid channel");
148 return;
151 if (--host->iso_listen_count[channel] == 0) {
152 host->template->devctl(host, ISO_UNLISTEN_CHANNEL, channel);
157 #define DEFINE_MULTIPLEXER(Function) \
158 void highlevel_##Function(struct hpsb_host *host) \
160 struct list_head *entry; \
161 void (*funcptr)(struct hpsb_host*); \
162 read_lock(&hl_drivers_lock); \
163 entry = hl_drivers.next; \
164 while (entry != &hl_drivers) { \
165 funcptr = list_entry(entry, struct hpsb_highlevel, hl_list) \
166 ->op->Function; \
167 if (funcptr) funcptr(host); \
168 entry = entry->next; \
170 read_unlock(&hl_drivers_lock); \
173 DEFINE_MULTIPLEXER(add_host)
174 DEFINE_MULTIPLEXER(remove_host)
175 DEFINE_MULTIPLEXER(host_reset)
176 #undef DEFINE_MULTIPLEXER
178 void highlevel_iso_receive(struct hpsb_host *host, quadlet_t *data,
179 unsigned int length)
181 struct list_head *entry;
182 struct hpsb_highlevel *hl;
183 int channel = (data[0] >> 8) & 0x3f;
185 read_lock(&hl_drivers_lock);
186 entry = hl_drivers.next;
188 while (entry != &hl_drivers) {
189 hl = list_entry(entry, struct hpsb_highlevel, hl_list);
190 if (hl->op->iso_receive) {
191 hl->op->iso_receive(host, channel, data, length);
193 entry = entry->next;
195 read_unlock(&hl_drivers_lock);
198 void highlevel_fcp_request(struct hpsb_host *host, int nodeid, int direction,
199 u8 *data, unsigned int length)
201 struct list_head *entry;
202 struct hpsb_highlevel *hl;
203 int cts = data[0];
205 read_lock(&hl_drivers_lock);
206 entry = hl_drivers.next;
208 while (entry != &hl_drivers) {
209 hl = list_entry(entry, struct hpsb_highlevel, hl_list);
210 if (hl->op->fcp_request) {
211 hl->op->fcp_request(host, nodeid, direction, cts, data,
212 length);
214 entry = entry->next;
216 read_unlock(&hl_drivers_lock);
219 int highlevel_read(struct hpsb_host *host, int nodeid, quadlet_t *buffer,
220 u64 addr, unsigned int length)
222 struct hpsb_address_serve *as;
223 struct list_head *entry;
224 unsigned int partlength;
225 int rcode = RCODE_ADDRESS_ERROR;
227 read_lock(&addr_space_lock);
229 entry = addr_space.next;
230 as = list_entry(entry, struct hpsb_address_serve, as_list);
232 while (as->start <= addr) {
233 if (as->end > addr) {
234 partlength = MIN((unsigned int)(as->end - addr),
235 length);
237 if (as->op->read != NULL) {
238 rcode = as->op->read(host, nodeid, buffer, addr,
239 partlength);
240 } else {
241 rcode = RCODE_TYPE_ERROR;
244 length -= partlength;
245 addr += partlength;
247 if ((rcode != RCODE_COMPLETE) || !length) {
248 break;
252 entry = entry->next;
253 as = list_entry(entry, struct hpsb_address_serve, as_list);
256 read_unlock(&addr_space_lock);
258 if (length && (rcode == RCODE_COMPLETE)) {
259 rcode = RCODE_ADDRESS_ERROR;
262 return rcode;
265 int highlevel_write(struct hpsb_host *host, int nodeid, quadlet_t *data,
266 u64 addr, unsigned int length)
268 struct hpsb_address_serve *as;
269 struct list_head *entry;
270 unsigned int partlength;
271 int rcode = RCODE_ADDRESS_ERROR;
273 read_lock(&addr_space_lock);
275 entry = addr_space.next;
276 as = list_entry(entry, struct hpsb_address_serve, as_list);
278 while (as->start <= addr) {
279 if (as->end > addr) {
280 partlength = MIN((unsigned int)(as->end - addr),
281 length);
283 if (as->op->write != NULL) {
284 rcode = as->op->write(host, nodeid, data, addr,
285 partlength);
286 } else {
287 rcode = RCODE_TYPE_ERROR;
290 length -= partlength;
291 addr += partlength;
293 if ((rcode != RCODE_COMPLETE) || !length) {
294 break;
298 entry = entry->next;
299 as = list_entry(entry, struct hpsb_address_serve, as_list);
302 read_unlock(&addr_space_lock);
304 if (length && (rcode == RCODE_COMPLETE)) {
305 rcode = RCODE_ADDRESS_ERROR;
308 return rcode;
312 int highlevel_lock(struct hpsb_host *host, int nodeid, quadlet_t *store,
313 u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode)
315 struct hpsb_address_serve *as;
316 struct list_head *entry;
317 int rcode = RCODE_ADDRESS_ERROR;
319 read_lock(&addr_space_lock);
321 entry = addr_space.next;
322 as = list_entry(entry, struct hpsb_address_serve, as_list);
324 while (as->start <= addr) {
325 if (as->end > addr) {
326 if (as->op->lock != NULL) {
327 rcode = as->op->lock(host, nodeid, store, addr,
328 data, arg, ext_tcode);
329 } else {
330 rcode = RCODE_TYPE_ERROR;
333 break;
336 entry = entry->next;
337 as = list_entry(entry, struct hpsb_address_serve, as_list);
340 read_unlock(&addr_space_lock);
342 return rcode;
345 int highlevel_lock64(struct hpsb_host *host, int nodeid, octlet_t *store,
346 u64 addr, octlet_t data, octlet_t arg, int ext_tcode)
348 struct hpsb_address_serve *as;
349 struct list_head *entry;
350 int rcode = RCODE_ADDRESS_ERROR;
352 read_lock(&addr_space_lock);
354 entry = addr_space.next;
355 as = list_entry(entry, struct hpsb_address_serve, as_list);
357 while (as->start <= addr) {
358 if (as->end > addr) {
359 if (as->op->lock64 != NULL) {
360 rcode = as->op->lock64(host, nodeid, store,
361 addr, data, arg,
362 ext_tcode);
363 } else {
364 rcode = RCODE_TYPE_ERROR;
367 break;
370 entry = entry->next;
371 as = list_entry(entry, struct hpsb_address_serve, as_list);
374 read_unlock(&addr_space_lock);
376 return rcode;
381 #ifndef MODULE
383 void register_builtin_highlevels(void)
385 #ifdef CONFIG_IEEE1394_RAWIO
387 int init_raw1394(void);
388 init_raw1394();
390 #endif
393 #endif /* !MODULE */
396 void init_hpsb_highlevel(void)
398 INIT_LIST_HEAD(&dummy_zero_addr.as_list);
399 INIT_LIST_HEAD(&dummy_zero_addr.addr_list);
400 INIT_LIST_HEAD(&dummy_max_addr.as_list);
401 INIT_LIST_HEAD(&dummy_max_addr.addr_list);
403 dummy_zero_addr.op = dummy_max_addr.op = &dummy_ops;
405 dummy_zero_addr.start = dummy_zero_addr.end = 0;
406 dummy_max_addr.start = dummy_max_addr.end = ((u64) 1) << 48;
408 list_add_tail(&dummy_zero_addr.as_list, &addr_space);
409 list_add_tail(&dummy_max_addr.as_list, &addr_space);
411 #ifndef MODULE
412 register_builtin_highlevels();
413 #endif