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.
10 #include <linux/config.h>
11 #include <linux/slab.h>
14 #include "ieee1394_types.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
),
41 INIT_LIST_HEAD(&hl
->hl_list
);
42 INIT_LIST_HEAD(&hl
->addr_list
);
46 write_lock_irq(&hl_drivers_lock
);
48 list_add_tail(&hl
->hl_list
, &hl_drivers
);
49 write_unlock_irq(&hl_drivers_lock
);
54 void hpsb_unregister_highlevel(struct hpsb_highlevel
*hl
)
56 struct list_head
*entry
;
57 struct hpsb_address_serve
*as
;
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
);
72 write_unlock_irq(&addr_space_lock
);
74 write_lock_irq(&hl_drivers_lock
);
75 list_del(&hl
->hl_list
);
77 write_unlock_irq(&hl_drivers_lock
);
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
;
89 if (((start
|end
) & 3) || (start
>= end
) || (end
> 0x1000000000000ULL
)) {
90 HPSB_ERR(__FUNCTION__
" called with invalid addresses");
94 as
= (struct hpsb_address_serve
*)
95 kmalloc(sizeof(struct hpsb_address_serve
), GFP_KERNEL
);
100 INIT_LIST_HEAD(&as
->as_list
);
101 INIT_LIST_HEAD(&as
->addr_list
);
106 write_lock_irq(&addr_space_lock
);
107 entry
= addr_space
.next
;
109 while (list_entry(entry
, struct hpsb_address_serve
, as_list
)->end
111 if (list_entry(entry
->next
, struct hpsb_address_serve
, as_list
)
113 list_add(&as
->as_list
, entry
);
114 list_add_tail(&as
->addr_list
, &hl
->addr_list
);
120 write_unlock_irq(&addr_space_lock
);
130 void hpsb_listen_channel(struct hpsb_highlevel
*hl
, struct hpsb_host
*host
,
131 unsigned int channel
)
134 HPSB_ERR(__FUNCTION__
" called with invalid channel");
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
)
147 HPSB_ERR(__FUNCTION__
" called with invalid channel");
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) \
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
,
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
);
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
;
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
,
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
),
237 if (as
->op
->read
!= NULL
) {
238 rcode
= as
->op
->read(host
, nodeid
, buffer
, addr
,
241 rcode
= RCODE_TYPE_ERROR
;
244 length
-= partlength
;
247 if ((rcode
!= RCODE_COMPLETE
) || !length
) {
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
;
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
),
283 if (as
->op
->write
!= NULL
) {
284 rcode
= as
->op
->write(host
, nodeid
, data
, addr
,
287 rcode
= RCODE_TYPE_ERROR
;
290 length
-= partlength
;
293 if ((rcode
!= RCODE_COMPLETE
) || !length
) {
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
;
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
);
330 rcode
= RCODE_TYPE_ERROR
;
337 as
= list_entry(entry
, struct hpsb_address_serve
, as_list
);
340 read_unlock(&addr_space_lock
);
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
,
364 rcode
= RCODE_TYPE_ERROR
;
371 as
= list_entry(entry
, struct hpsb_address_serve
, as_list
);
374 read_unlock(&addr_space_lock
);
383 void register_builtin_highlevels(void)
385 #ifdef CONFIG_IEEE1394_RAWIO
387 int init_raw1394(void);
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
);
412 register_builtin_highlevels();