import of ftp.dlink.com/GPL/DSMG-600_reB/ppclinux.tar.gz
[linux-2.4.21-pre4.git] / drivers / ieee1394 / highlevel.c
1 /*
2  * IEEE 1394 for Linux
3  *
4  * Copyright (C) 1999 Andreas E. Bombe
5  *
6  * This code is licensed under the GPL.  See the file COPYING in the root
7  * directory of the kernel sources for details.
8  *
9  *
10  * Contributions:
11  *
12  * Christian Toegel <christian.toegel@gmx.at>
13  *        unregister address space
14  *
15  * Manfred Weihs <weihs@ict.tuwien.ac.at>
16  *        unregister address space
17  *
18  */
19
20 #include <linux/config.h>
21 #include <linux/slab.h>
22
23 #include "ieee1394.h"
24 #include "ieee1394_types.h"
25 #include "hosts.h"
26 #include "ieee1394_core.h"
27 #include "highlevel.h"
28
29
30 LIST_HEAD(hl_drivers);
31 rwlock_t hl_drivers_lock = RW_LOCK_UNLOCKED;
32
33 LIST_HEAD(addr_space);
34 rwlock_t addr_space_lock = RW_LOCK_UNLOCKED;
35
36 /* addr_space list will have zero and max already included as bounds */
37 static struct hpsb_address_ops dummy_ops = { NULL, NULL, NULL, NULL };
38 static struct hpsb_address_serve dummy_zero_addr, dummy_max_addr;
39
40 struct hpsb_highlevel *hpsb_register_highlevel(const char *name,
41                                                struct hpsb_highlevel_ops *ops)
42 {
43         struct hpsb_highlevel *hl;
44
45         hl = (struct hpsb_highlevel *)kmalloc(sizeof(struct hpsb_highlevel),
46                                               GFP_KERNEL);
47         if (hl == NULL) {
48                 return NULL;
49         }
50
51         INIT_LIST_HEAD(&hl->hl_list);
52         INIT_LIST_HEAD(&hl->addr_list);
53         hl->name = name;
54         hl->op = ops;
55
56         write_lock_irq(&hl_drivers_lock);
57         list_add_tail(&hl->hl_list, &hl_drivers);
58         write_unlock_irq(&hl_drivers_lock);
59
60         hl_all_hosts(hl->op->add_host);
61
62         return hl;
63 }
64
65 void hpsb_unregister_highlevel(struct hpsb_highlevel *hl)
66 {
67         struct list_head *entry;
68         struct hpsb_address_serve *as;
69
70         if (hl == NULL) {
71                 return;
72         }
73
74         write_lock_irq(&addr_space_lock);
75         entry = hl->addr_list.next;
76
77         while (entry != &hl->addr_list) {
78                 as = list_entry(entry, struct hpsb_address_serve, addr_list);
79                 list_del(&as->as_list);
80                 entry = entry->next;
81                 kfree(as);
82         }
83         write_unlock_irq(&addr_space_lock);
84
85         write_lock_irq(&hl_drivers_lock);
86         list_del(&hl->hl_list);
87         write_unlock_irq(&hl_drivers_lock);
88
89         if (hl->op->remove_host)
90                 hl_all_hosts(hl->op->remove_host);
91
92         kfree(hl);
93 }
94
95 int hpsb_register_addrspace(struct hpsb_highlevel *hl,
96                             struct hpsb_address_ops *ops, u64 start, u64 end)
97 {
98         struct hpsb_address_serve *as;
99         struct list_head *entry;
100         int retval = 0;
101
102         if (((start|end) & 3) || (start >= end) || (end > 0x1000000000000ULL)) {
103                 HPSB_ERR("%s called with invalid addresses", __FUNCTION__);
104                 return 0;
105         }
106
107         as = (struct hpsb_address_serve *)
108                 kmalloc(sizeof(struct hpsb_address_serve), GFP_KERNEL);
109         if (as == NULL) {
110                 return 0;
111         }
112
113         INIT_LIST_HEAD(&as->as_list);
114         INIT_LIST_HEAD(&as->addr_list);
115         as->op = ops;
116         as->start = start;
117         as->end = end;
118
119         write_lock_irq(&addr_space_lock);
120         entry = addr_space.next;
121
122         while (list_entry(entry, struct hpsb_address_serve, as_list)->end
123                <= start) {
124                 if (list_entry(entry->next, struct hpsb_address_serve, as_list)
125                     ->start >= end) {
126                         list_add(&as->as_list, entry);
127                         list_add_tail(&as->addr_list, &hl->addr_list);
128                         retval = 1;
129                         break;
130                 }
131                 entry = entry->next;
132         }
133         write_unlock_irq(&addr_space_lock);
134
135         if (retval == 0) {
136                 kfree(as);
137         }
138
139         return retval;
140 }
141
142 int hpsb_unregister_addrspace(struct hpsb_highlevel *hl, u64 start)
143 {
144         int retval = 0;
145         struct hpsb_address_serve *as;
146         struct list_head *entry;
147
148         write_lock_irq(&addr_space_lock);
149
150         entry = hl->addr_list.next;
151
152         while (entry != &hl->addr_list) {
153                 as = list_entry(entry, struct hpsb_address_serve, addr_list);
154                 entry = entry->next;
155                 if (as->start == start) {
156                         list_del(&as->as_list);
157                         list_del(&as->addr_list);
158                         kfree(as);
159                         retval = 1;
160                         break;
161                 }
162         }
163
164         write_unlock_irq(&addr_space_lock);
165
166         return retval;
167 }
168
169 void hpsb_listen_channel(struct hpsb_highlevel *hl, struct hpsb_host *host,
170                          unsigned int channel)
171 {
172         if (channel > 63) {
173                 HPSB_ERR("%s called with invalid channel", __FUNCTION__);
174                 return;
175         }
176
177         if (host->iso_listen_count[channel]++ == 0) {
178                 host->driver->devctl(host, ISO_LISTEN_CHANNEL, channel);
179         }
180 }
181
182 void hpsb_unlisten_channel(struct hpsb_highlevel *hl, struct hpsb_host *host, 
183                            unsigned int channel)
184 {
185         if (channel > 63) {
186                 HPSB_ERR("%s called with invalid channel", __FUNCTION__);
187                 return;
188         }
189
190         if (--host->iso_listen_count[channel] == 0) {
191                 host->driver->devctl(host, ISO_UNLISTEN_CHANNEL, channel);
192         }
193 }
194
195
196 void highlevel_add_host(struct hpsb_host *host)
197 {
198         struct list_head *entry;
199         struct hpsb_highlevel *hl;
200
201         read_lock(&hl_drivers_lock);
202         list_for_each(entry, &hl_drivers) {
203                 hl = list_entry(entry, struct hpsb_highlevel, hl_list);
204
205                 hl->op->add_host(host);
206         }
207         read_unlock(&hl_drivers_lock);
208 }
209
210 void highlevel_remove_host(struct hpsb_host *host)
211 {
212         struct list_head *entry;
213         struct hpsb_highlevel *hl;
214
215         write_lock_irq(&hl_drivers_lock);
216         list_for_each(entry, &hl_drivers) {
217                 hl = list_entry(entry, struct hpsb_highlevel, hl_list);
218
219                 if (hl->op->remove_host)
220                         hl->op->remove_host(host);
221         }
222         write_unlock_irq(&hl_drivers_lock);
223 }
224
225 void highlevel_host_reset(struct hpsb_host *host)
226 {
227         struct list_head *entry;
228         struct hpsb_highlevel *hl;
229
230         read_lock(&hl_drivers_lock);
231         list_for_each(entry, &hl_drivers) {
232                 hl = list_entry(entry, struct hpsb_highlevel, hl_list);
233
234                 if (hl->op->host_reset)
235                         hl->op->host_reset(host);
236         }
237         read_unlock(&hl_drivers_lock);
238 }
239
240 void highlevel_iso_receive(struct hpsb_host *host, quadlet_t *data,
241                            unsigned int length)
242 {
243         struct list_head *entry;
244         struct hpsb_highlevel *hl;
245         int channel = (data[0] >> 8) & 0x3f;
246
247         read_lock(&hl_drivers_lock);
248         entry = hl_drivers.next;
249
250         while (entry != &hl_drivers) {
251                 hl = list_entry(entry, struct hpsb_highlevel, hl_list);
252                 if (hl->op->iso_receive) {
253                         hl->op->iso_receive(host, channel, data, length);
254                 }
255                 entry = entry->next;
256         }
257         read_unlock(&hl_drivers_lock);
258 }
259
260 void highlevel_fcp_request(struct hpsb_host *host, int nodeid, int direction,
261                            u8 *data, unsigned int length)
262 {
263         struct list_head *entry;
264         struct hpsb_highlevel *hl;
265         int cts = data[0] >> 4;
266
267         read_lock(&hl_drivers_lock);
268         entry = hl_drivers.next;
269
270         while (entry != &hl_drivers) {
271                 hl = list_entry(entry, struct hpsb_highlevel, hl_list);
272                 if (hl->op->fcp_request) {
273                         hl->op->fcp_request(host, nodeid, direction, cts, data,
274                                             length);
275                 }
276                 entry = entry->next;
277         }
278         read_unlock(&hl_drivers_lock);
279 }
280
281 int highlevel_read(struct hpsb_host *host, int nodeid, quadlet_t *buffer,
282                    u64 addr, unsigned int length, u16 flags)
283 {
284         struct hpsb_address_serve *as;
285         struct list_head *entry;
286         unsigned int partlength;
287         int rcode = RCODE_ADDRESS_ERROR;
288
289         read_lock(&addr_space_lock);
290
291         entry = addr_space.next;
292         as = list_entry(entry, struct hpsb_address_serve, as_list);
293
294         while (as->start <= addr) {
295                 if (as->end > addr) {
296                         partlength = min(as->end - addr, (u64) length);
297
298                         if (as->op->read != NULL) {
299                                 rcode = as->op->read(host, nodeid, buffer,
300                                                      addr, partlength, flags);
301                         } else {
302                                 rcode = RCODE_TYPE_ERROR;
303                         }
304
305                         length -= partlength;
306                         addr += partlength;
307
308                         if ((rcode != RCODE_COMPLETE) || !length) {
309                                 break;
310                         }
311                 }
312
313                 entry = entry->next;
314                 as = list_entry(entry, struct hpsb_address_serve, as_list);
315         }
316
317         read_unlock(&addr_space_lock);
318
319         if (length && (rcode == RCODE_COMPLETE)) {
320                 rcode = RCODE_ADDRESS_ERROR;
321         }
322
323         return rcode;
324 }
325
326 int highlevel_write(struct hpsb_host *host, int nodeid, int destid,
327                     quadlet_t *data, u64 addr, unsigned int length, u16 flags)
328 {
329         struct hpsb_address_serve *as;
330         struct list_head *entry;
331         unsigned int partlength;
332         int rcode = RCODE_ADDRESS_ERROR;
333
334         read_lock(&addr_space_lock);
335
336         entry = addr_space.next;
337         as = list_entry(entry, struct hpsb_address_serve, as_list);
338
339         while (as->start <= addr) {
340                 if (as->end > addr) {
341                         partlength = min(as->end - addr, (u64) length);
342
343                         if (as->op->write != NULL) {
344                                 rcode = as->op->write(host, nodeid, destid,
345                                                       data, addr, partlength, flags);
346                         } else {
347                                 rcode = RCODE_TYPE_ERROR;
348                         }
349
350                         length -= partlength;
351                         addr += partlength;
352
353                         if ((rcode != RCODE_COMPLETE) || !length) {
354                                 break;
355                         }
356                 }
357
358                 entry = entry->next;
359                 as = list_entry(entry, struct hpsb_address_serve, as_list);
360         }
361
362         read_unlock(&addr_space_lock);
363
364         if (length && (rcode == RCODE_COMPLETE)) {
365                 rcode = RCODE_ADDRESS_ERROR;
366         }
367
368         return rcode;
369 }
370
371
372 int highlevel_lock(struct hpsb_host *host, int nodeid, quadlet_t *store,
373                    u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode, u16 flags)
374 {
375         struct hpsb_address_serve *as;
376         struct list_head *entry;
377         int rcode = RCODE_ADDRESS_ERROR;
378
379         read_lock(&addr_space_lock);
380
381         entry = addr_space.next;
382         as = list_entry(entry, struct hpsb_address_serve, as_list);
383
384         while (as->start <= addr) {
385                 if (as->end > addr) {
386                         if (as->op->lock != NULL) {
387                                 rcode = as->op->lock(host, nodeid, store, addr,
388                                                      data, arg, ext_tcode, flags);
389                         } else {
390                                 rcode = RCODE_TYPE_ERROR;
391                         }
392
393                         break;
394                 }
395
396                 entry = entry->next;
397                 as = list_entry(entry, struct hpsb_address_serve, as_list);
398         }
399
400         read_unlock(&addr_space_lock);
401
402         return rcode;
403 }
404
405 int highlevel_lock64(struct hpsb_host *host, int nodeid, octlet_t *store,
406                      u64 addr, octlet_t data, octlet_t arg, int ext_tcode, u16 flags)
407 {
408         struct hpsb_address_serve *as;
409         struct list_head *entry;
410         int rcode = RCODE_ADDRESS_ERROR;
411
412         read_lock(&addr_space_lock);
413
414         entry = addr_space.next;
415         as = list_entry(entry, struct hpsb_address_serve, as_list);
416
417         while (as->start <= addr) {
418                 if (as->end > addr) {
419                         if (as->op->lock64 != NULL) {
420                                 rcode = as->op->lock64(host, nodeid, store,
421                                                        addr, data, arg,
422                                                        ext_tcode, flags);
423                         } else {
424                                 rcode = RCODE_TYPE_ERROR;
425                         }
426
427                         break;
428                 }
429
430                 entry = entry->next;
431                 as = list_entry(entry, struct hpsb_address_serve, as_list);
432         }
433
434         read_unlock(&addr_space_lock);
435
436         return rcode;
437 }
438
439 void init_hpsb_highlevel(void)
440 {
441         INIT_LIST_HEAD(&dummy_zero_addr.as_list);
442         INIT_LIST_HEAD(&dummy_zero_addr.addr_list);
443         INIT_LIST_HEAD(&dummy_max_addr.as_list);
444         INIT_LIST_HEAD(&dummy_max_addr.addr_list);
445
446         dummy_zero_addr.op = dummy_max_addr.op = &dummy_ops;
447
448         dummy_zero_addr.start = dummy_zero_addr.end = 0;
449         dummy_max_addr.start = dummy_max_addr.end = ((u64) 1) << 48;
450
451         list_add_tail(&dummy_zero_addr.as_list, &addr_space);
452         list_add_tail(&dummy_max_addr.as_list, &addr_space);
453 }