Blame view

drivers/ieee1394/highlevel.c 17.5 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  /*
   * IEEE 1394 for Linux
   *
   * Copyright (C) 1999 Andreas E. Bombe
   *
   * This code is licensed under the GPL.  See the file COPYING in the root
   * directory of the kernel sources for details.
   *
   *
   * Contributions:
   *
   * Christian Toegel <christian.toegel@gmx.at>
   *        unregister address space
   *
   * Manfred Weihs <weihs@ict.tuwien.ac.at>
   *        unregister address space
   *
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
  #include <linux/slab.h>
  #include <linux/list.h>
  #include <linux/bitops.h>
  
  #include "ieee1394.h"
  #include "ieee1394_types.h"
  #include "hosts.h"
  #include "ieee1394_core.h"
  #include "highlevel.h"
  #include "nodemgr.h"
  
  
  struct hl_host_info {
  	struct list_head list;
  	struct hpsb_host *host;
  	size_t size;
  	unsigned long key;
  	void *data;
  };
  
  
  static LIST_HEAD(hl_drivers);
  static DECLARE_RWSEM(hl_drivers_sem);
  
  static LIST_HEAD(hl_irqs);
  static DEFINE_RWLOCK(hl_irqs_lock);
  
  static DEFINE_RWLOCK(addr_space_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
47
48
  
  static struct hl_host_info *hl_get_hostinfo(struct hpsb_highlevel *hl,
2c4b69bd7   Ben Collins   ieee1394: adjust ...
49
  					    struct hpsb_host *host)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
50
51
52
53
54
55
56
57
58
59
60
61
62
63
  {
  	struct hl_host_info *hi = NULL;
  
  	if (!hl || !host)
  		return NULL;
  
  	read_lock(&hl->host_info_lock);
  	list_for_each_entry(hi, &hl->host_info_list, list) {
  		if (hi->host == host) {
  			read_unlock(&hl->host_info_lock);
  			return hi;
  		}
  	}
  	read_unlock(&hl->host_info_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
64
65
  	return NULL;
  }
afd6546d8   Stefan Richter   ieee1394: move so...
66
67
68
69
70
71
  /**
   * hpsb_get_hostinfo - retrieve a hostinfo pointer bound to this driver/host
   *
   * Returns a per @host and @hl driver data structure that was previously stored
   * by hpsb_create_hostinfo.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
72
73
74
  void *hpsb_get_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host)
  {
  	struct hl_host_info *hi = hl_get_hostinfo(hl, host);
2c4b69bd7   Ben Collins   ieee1394: adjust ...
75
  	return hi ? hi->data : NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
76
  }
afd6546d8   Stefan Richter   ieee1394: move so...
77
78
79
80
81
82
83
  /**
   * hpsb_create_hostinfo - allocate a hostinfo pointer bound to this driver/host
   *
   * Allocate a hostinfo pointer backed by memory with @data_size and bind it to
   * to this @hl driver and @host.  If @data_size is zero, then the return here is
   * only valid for error checking.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
84
85
86
87
88
89
90
91
92
  void *hpsb_create_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host,
  			   size_t data_size)
  {
  	struct hl_host_info *hi;
  	void *data;
  	unsigned long flags;
  
  	hi = hl_get_hostinfo(hl, host);
  	if (hi) {
2c4b69bd7   Ben Collins   ieee1394: adjust ...
93
94
  		HPSB_ERR("%s called hpsb_create_hostinfo when hostinfo already"
  			 " exists", hl->name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
95
96
  		return NULL;
  	}
8551158ab   Stefan Richter   kmalloc/kzalloc c...
97
  	hi = kzalloc(sizeof(*hi) + data_size, GFP_ATOMIC);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
98
99
  	if (!hi)
  		return NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
100
101
102
103
104
105
106
107
108
109
110
111
112
113
  	if (data_size) {
  		data = hi->data = hi + 1;
  		hi->size = data_size;
  	} else
  		data = hi;
  
  	hi->host = host;
  
  	write_lock_irqsave(&hl->host_info_lock, flags);
  	list_add_tail(&hi->list, &hl->host_info_list);
  	write_unlock_irqrestore(&hl->host_info_lock, flags);
  
  	return data;
  }
afd6546d8   Stefan Richter   ieee1394: move so...
114
115
116
117
118
  /**
   * hpsb_set_hostinfo - set the hostinfo pointer to something useful
   *
   * Usually follows a call to hpsb_create_hostinfo, where the size is 0.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
119
120
121
122
123
124
125
126
127
128
129
  int hpsb_set_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host,
  		      void *data)
  {
  	struct hl_host_info *hi;
  
  	hi = hl_get_hostinfo(hl, host);
  	if (hi) {
  		if (!hi->size && !hi->data) {
  			hi->data = data;
  			return 0;
  		} else
2c4b69bd7   Ben Collins   ieee1394: adjust ...
130
131
  			HPSB_ERR("%s called hpsb_set_hostinfo when hostinfo "
  				 "already has data", hl->name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
132
133
134
  	} else
  		HPSB_ERR("%s called hpsb_set_hostinfo when no hostinfo exists",
  			 hl->name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
135
136
  	return -EINVAL;
  }
afd6546d8   Stefan Richter   ieee1394: move so...
137
138
139
140
141
  /**
   * hpsb_destroy_hostinfo - free and remove a hostinfo pointer
   *
   * Free and remove the hostinfo pointer bound to this @hl driver and @host.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
142
143
144
145
146
147
148
149
150
151
152
153
  void hpsb_destroy_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host)
  {
  	struct hl_host_info *hi;
  
  	hi = hl_get_hostinfo(hl, host);
  	if (hi) {
  		unsigned long flags;
  		write_lock_irqsave(&hl->host_info_lock, flags);
  		list_del(&hi->list);
  		write_unlock_irqrestore(&hl->host_info_lock, flags);
  		kfree(hi);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
154
155
  	return;
  }
afd6546d8   Stefan Richter   ieee1394: move so...
156
157
158
159
160
161
  /**
   * hpsb_set_hostinfo_key - set an alternate lookup key for an hostinfo
   *
   * Sets an alternate lookup key for the hostinfo bound to this @hl driver and
   * @host.
   */
2c4b69bd7   Ben Collins   ieee1394: adjust ...
162
163
  void hpsb_set_hostinfo_key(struct hpsb_highlevel *hl, struct hpsb_host *host,
  			   unsigned long key)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
164
165
166
167
168
169
  {
  	struct hl_host_info *hi;
  
  	hi = hl_get_hostinfo(hl, host);
  	if (hi)
  		hi->key = key;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
170
171
  	return;
  }
afd6546d8   Stefan Richter   ieee1394: move so...
172
173
174
  /**
   * hpsb_get_hostinfo_bykey - retrieve a hostinfo pointer by its alternate key
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
  void *hpsb_get_hostinfo_bykey(struct hpsb_highlevel *hl, unsigned long key)
  {
  	struct hl_host_info *hi;
  	void *data = NULL;
  
  	if (!hl)
  		return NULL;
  
  	read_lock(&hl->host_info_lock);
  	list_for_each_entry(hi, &hl->host_info_list, list) {
  		if (hi->key == key) {
  			data = hi->data;
  			break;
  		}
  	}
  	read_unlock(&hl->host_info_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
191
192
  	return data;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
193
194
195
196
197
  static int highlevel_for_each_host_reg(struct hpsb_host *host, void *__data)
  {
  	struct hpsb_highlevel *hl = __data;
  
  	hl->add_host(host);
2c4b69bd7   Ben Collins   ieee1394: adjust ...
198
199
200
  	if (host->update_config_rom && hpsb_update_config_rom_image(host) < 0)
  		HPSB_ERR("Failed to generate Configuration ROM image for host "
  			 "%s-%d", hl->name, host->id);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
201
202
  	return 0;
  }
afd6546d8   Stefan Richter   ieee1394: move so...
203
204
205
206
207
208
  /**
   * hpsb_register_highlevel - register highlevel driver
   *
   * The name pointer in @hl has to stay valid at all times because the string is
   * not copied.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
209
210
  void hpsb_register_highlevel(struct hpsb_highlevel *hl)
  {
445151932   Ben Collins   ieee1394: hl_irqs...
211
  	unsigned long flags;
055a7da0b   Stefan Richter   ieee1394: video13...
212
  	hpsb_init_highlevel(hl);
2c4b69bd7   Ben Collins   ieee1394: adjust ...
213
  	INIT_LIST_HEAD(&hl->addr_list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
214
215
  
  	down_write(&hl_drivers_sem);
2c4b69bd7   Ben Collins   ieee1394: adjust ...
216
  	list_add_tail(&hl->hl_list, &hl_drivers);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
217
  	up_write(&hl_drivers_sem);
445151932   Ben Collins   ieee1394: hl_irqs...
218
  	write_lock_irqsave(&hl_irqs_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
219
  	list_add_tail(&hl->irq_list, &hl_irqs);
445151932   Ben Collins   ieee1394: hl_irqs...
220
  	write_unlock_irqrestore(&hl_irqs_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
221
222
223
  
  	if (hl->add_host)
  		nodemgr_for_each_host(hl, highlevel_for_each_host_reg);
2c4b69bd7   Ben Collins   ieee1394: adjust ...
224
  	return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
225
226
227
228
229
230
231
232
  }
  
  static void __delete_addr(struct hpsb_address_serve *as)
  {
  	list_del(&as->host_list);
  	list_del(&as->hl_list);
  	kfree(as);
  }
2c4b69bd7   Ben Collins   ieee1394: adjust ...
233
234
  static void __unregister_host(struct hpsb_highlevel *hl, struct hpsb_host *host,
  			      int update_cr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
235
236
237
238
239
240
241
242
243
244
245
246
247
248
  {
  	unsigned long flags;
  	struct list_head *lh, *next;
  	struct hpsb_address_serve *as;
  
  	/* First, let the highlevel driver unreg */
  	if (hl->remove_host)
  		hl->remove_host(host);
  
  	/* Remove any addresses that are matched for this highlevel driver
  	 * and this particular host. */
  	write_lock_irqsave(&addr_space_lock, flags);
  	list_for_each_safe (lh, next, &hl->addr_list) {
  		as = list_entry(lh, struct hpsb_address_serve, hl_list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
249
250
251
252
253
254
255
  		if (as->host == host)
  			__delete_addr(as);
  	}
  	write_unlock_irqrestore(&addr_space_lock, flags);
  
  	/* Now update the config-rom to reflect anything removed by the
  	 * highlevel driver. */
2c4b69bd7   Ben Collins   ieee1394: adjust ...
256
257
258
259
  	if (update_cr && host->update_config_rom &&
  	    hpsb_update_config_rom_image(host) < 0)
  		HPSB_ERR("Failed to generate Configuration ROM image for host "
  			 "%s-%d", hl->name, host->id);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
260

2c4b69bd7   Ben Collins   ieee1394: adjust ...
261
  	/* Finally remove all the host info associated between these two. */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
262
263
264
265
266
267
268
269
  	hpsb_destroy_hostinfo(hl, host);
  }
  
  static int highlevel_for_each_host_unreg(struct hpsb_host *host, void *__data)
  {
  	struct hpsb_highlevel *hl = __data;
  
  	__unregister_host(hl, host, 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
270
271
  	return 0;
  }
afd6546d8   Stefan Richter   ieee1394: move so...
272
273
274
  /**
   * hpsb_unregister_highlevel - unregister highlevel driver
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
275
276
  void hpsb_unregister_highlevel(struct hpsb_highlevel *hl)
  {
445151932   Ben Collins   ieee1394: hl_irqs...
277
278
279
  	unsigned long flags;
  
  	write_lock_irqsave(&hl_irqs_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
280
  	list_del(&hl->irq_list);
445151932   Ben Collins   ieee1394: hl_irqs...
281
  	write_unlock_irqrestore(&hl_irqs_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
282
283
  
  	down_write(&hl_drivers_sem);
2c4b69bd7   Ben Collins   ieee1394: adjust ...
284
  	list_del(&hl->hl_list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
285
286
287
288
  	up_write(&hl_drivers_sem);
  
  	nodemgr_for_each_host(hl, highlevel_for_each_host_unreg);
  }
afd6546d8   Stefan Richter   ieee1394: move so...
289
290
291
292
293
294
295
296
297
298
299
300
301
  /**
   * hpsb_allocate_and_register_addrspace - alloc' and reg' a host address space
   *
   * @start and @end are 48 bit pointers and have to be quadlet aligned.
   * @end points to the first address behind the handled addresses.  This
   * function can be called multiple times for a single hpsb_highlevel @hl to
   * implement sparse register sets.  The requested region must not overlap any
   * previously allocated region, otherwise registering will fail.
   *
   * It returns true for successful allocation.  Address spaces can be
   * unregistered with hpsb_unregister_addrspace.  All remaining address spaces
   * are automatically deallocated together with the hpsb_highlevel @hl.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
302
303
  u64 hpsb_allocate_and_register_addrspace(struct hpsb_highlevel *hl,
  					 struct hpsb_host *host,
b17a55096   Stefan Richter   ieee1394: mark al...
304
  					 const struct hpsb_address_ops *ops,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
305
306
307
308
309
  					 u64 size, u64 alignment,
  					 u64 start, u64 end)
  {
  	struct hpsb_address_serve *as, *a1, *a2;
  	struct list_head *entry;
6737231ea   Ben Collins   ieee1394: add pre...
310
  	u64 retval = CSR1212_INVALID_ADDR_SPACE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
311
312
313
314
  	unsigned long flags;
  	u64 align_mask = ~(alignment - 1);
  
  	if ((alignment & 3) || (alignment > 0x800000000000ULL) ||
37d54111c   Akinobu Mita   [PATCH] bitops: h...
315
  	    (hweight64(alignment) != 1)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
316
  		HPSB_ERR("%s called with invalid alignment: 0x%048llx",
b1ce1fd77   Harvey Harrison   ieee1394: replace...
317
  			 __func__, (unsigned long long)alignment);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
318
319
  		return retval;
  	}
8aef63ff0   Ben Collins   ieee1394: extend ...
320
321
  	/* default range,
  	 * avoids controller's posted write area (see OHCI 1.1 clause 1.5) */
6737231ea   Ben Collins   ieee1394: add pre...
322
323
  	if (start == CSR1212_INVALID_ADDR_SPACE &&
  	    end   == CSR1212_INVALID_ADDR_SPACE) {
8aef63ff0   Ben Collins   ieee1394: extend ...
324
  		start = host->middle_addr_space;
6737231ea   Ben Collins   ieee1394: add pre...
325
  		end   = CSR1212_ALL_SPACE_END;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
326
  	}
2c4b69bd7   Ben Collins   ieee1394: adjust ...
327
328
329
  	if (((start|end) & ~align_mask) || (start >= end) ||
  	    (end > CSR1212_ALL_SPACE_END)) {
  		HPSB_ERR("%s called with invalid addresses "
b1ce1fd77   Harvey Harrison   ieee1394: replace...
330
  			 "(start = %012Lx  end = %012Lx)", __func__,
2c4b69bd7   Ben Collins   ieee1394: adjust ...
331
  			 (unsigned long long)start,(unsigned long long)end);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
332
333
  		return retval;
  	}
8551158ab   Stefan Richter   kmalloc/kzalloc c...
334
335
  	as = kmalloc(sizeof(*as), GFP_KERNEL);
  	if (!as)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
336
  		return retval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
337
338
339
340
341
342
343
  
  	INIT_LIST_HEAD(&as->host_list);
  	INIT_LIST_HEAD(&as->hl_list);
  	as->op = ops;
  	as->host = host;
  
  	write_lock_irqsave(&addr_space_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
344
345
346
347
348
  	list_for_each(entry, &host->addr_space) {
  		u64 a1sa, a1ea;
  		u64 a2sa, a2ea;
  
  		a1 = list_entry(entry, struct hpsb_address_serve, host_list);
2c4b69bd7   Ben Collins   ieee1394: adjust ...
349
350
  		a2 = list_entry(entry->next, struct hpsb_address_serve,
  				host_list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
351
352
353
354
355
  
  		a1sa = a1->start & align_mask;
  		a1ea = (a1->end + alignment -1) & align_mask;
  		a2sa = a2->start & align_mask;
  		a2ea = (a2->end + alignment -1) & align_mask;
2c4b69bd7   Ben Collins   ieee1394: adjust ...
356
357
  		if ((a2sa - a1ea >= size) && (a2sa - start >= size) &&
  		    (a2sa > start)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
358
359
360
361
362
363
364
365
  			as->start = max(start, a1ea);
  			as->end = as->start + size;
  			list_add(&as->host_list, entry);
  			list_add_tail(&as->hl_list, &hl->addr_list);
  			retval = as->start;
  			break;
  		}
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
366
  	write_unlock_irqrestore(&addr_space_lock, flags);
2c4b69bd7   Ben Collins   ieee1394: adjust ...
367
  	if (retval == CSR1212_INVALID_ADDR_SPACE)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
368
  		kfree(as);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
369
370
  	return retval;
  }
afd6546d8   Stefan Richter   ieee1394: move so...
371
372
373
374
375
376
377
378
379
380
381
382
383
  /**
   * hpsb_register_addrspace - register a host address space
   *
   * @start and @end are 48 bit pointers and have to be quadlet aligned.
   * @end points to the first address behind the handled addresses.  This
   * function can be called multiple times for a single hpsb_highlevel @hl to
   * implement sparse register sets.  The requested region must not overlap any
   * previously allocated region, otherwise registering will fail.
   *
   * It returns true for successful allocation.  Address spaces can be
   * unregistered with hpsb_unregister_addrspace.  All remaining address spaces
   * are automatically deallocated together with the hpsb_highlevel @hl.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
384
  int hpsb_register_addrspace(struct hpsb_highlevel *hl, struct hpsb_host *host,
b17a55096   Stefan Richter   ieee1394: mark al...
385
386
  			    const struct hpsb_address_ops *ops,
  			    u64 start, u64 end)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
387
  {
2c4b69bd7   Ben Collins   ieee1394: adjust ...
388
  	struct hpsb_address_serve *as;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
389
  	struct list_head *lh;
2c4b69bd7   Ben Collins   ieee1394: adjust ...
390
391
  	int retval = 0;
  	unsigned long flags;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
392

2c4b69bd7   Ben Collins   ieee1394: adjust ...
393
394
  	if (((start|end) & 3) || (start >= end) ||
  	    (end > CSR1212_ALL_SPACE_END)) {
b1ce1fd77   Harvey Harrison   ieee1394: replace...
395
  		HPSB_ERR("%s called with invalid addresses", __func__);
2c4b69bd7   Ben Collins   ieee1394: adjust ...
396
397
  		return 0;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
398

adb0a6168   Stefan Richter   ieee1394: replace...
399
  	as = kmalloc(sizeof(*as), GFP_KERNEL);
8551158ab   Stefan Richter   kmalloc/kzalloc c...
400
401
  	if (!as)
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
402

2c4b69bd7   Ben Collins   ieee1394: adjust ...
403
404
405
406
407
  	INIT_LIST_HEAD(&as->host_list);
  	INIT_LIST_HEAD(&as->hl_list);
  	as->op = ops;
  	as->start = start;
  	as->end = end;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
408
409
410
  	as->host = host;
  
  	write_lock_irqsave(&addr_space_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
411
412
413
414
  	list_for_each(lh, &host->addr_space) {
  		struct hpsb_address_serve *as_this =
  			list_entry(lh, struct hpsb_address_serve, host_list);
  		struct hpsb_address_serve *as_next =
2c4b69bd7   Ben Collins   ieee1394: adjust ...
415
416
  			list_entry(lh->next, struct hpsb_address_serve,
  				   host_list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
  
  		if (as_this->end > as->start)
  			break;
  
  		if (as_next->start >= as->end) {
  			list_add(&as->host_list, lh);
  			list_add_tail(&as->hl_list, &hl->addr_list);
  			retval = 1;
  			break;
  		}
  	}
  	write_unlock_irqrestore(&addr_space_lock, flags);
  
  	if (retval == 0)
  		kfree(as);
2c4b69bd7   Ben Collins   ieee1394: adjust ...
432
  	return retval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
433
434
435
  }
  
  int hpsb_unregister_addrspace(struct hpsb_highlevel *hl, struct hpsb_host *host,
2c4b69bd7   Ben Collins   ieee1394: adjust ...
436
  			      u64 start)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
437
  {
2c4b69bd7   Ben Collins   ieee1394: adjust ...
438
439
440
441
  	int retval = 0;
  	struct hpsb_address_serve *as;
  	struct list_head *lh, *next;
  	unsigned long flags;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
442

2c4b69bd7   Ben Collins   ieee1394: adjust ...
443
  	write_lock_irqsave(&addr_space_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
444
  	list_for_each_safe (lh, next, &hl->addr_list) {
2c4b69bd7   Ben Collins   ieee1394: adjust ...
445
446
  		as = list_entry(lh, struct hpsb_address_serve, hl_list);
  		if (as->start == start && as->host == host) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
447
  			__delete_addr(as);
2c4b69bd7   Ben Collins   ieee1394: adjust ...
448
449
450
451
452
453
  			retval = 1;
  			break;
  		}
  	}
  	write_unlock_irqrestore(&addr_space_lock, flags);
  	return retval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
454
  }
1c4fb577a   Tobias Klauser   ieee1394: Storage...
455
  static const struct hpsb_address_ops dummy_ops;
e47c1feb1   Stefan Richter   ieee1394: fix lis...
456
457
  
  /* dummy address spaces as lower and upper bounds of the host's a.s. list */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
458
459
  static void init_hpsb_highlevel(struct hpsb_host *host)
  {
e47c1feb1   Stefan Richter   ieee1394: fix lis...
460
461
462
463
  	INIT_LIST_HEAD(&host->dummy_zero_addr.host_list);
  	INIT_LIST_HEAD(&host->dummy_zero_addr.hl_list);
  	INIT_LIST_HEAD(&host->dummy_max_addr.host_list);
  	INIT_LIST_HEAD(&host->dummy_max_addr.hl_list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
464

e47c1feb1   Stefan Richter   ieee1394: fix lis...
465
  	host->dummy_zero_addr.op = host->dummy_max_addr.op = &dummy_ops;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
466

e47c1feb1   Stefan Richter   ieee1394: fix lis...
467
468
  	host->dummy_zero_addr.start = host->dummy_zero_addr.end = 0;
  	host->dummy_max_addr.start = host->dummy_max_addr.end = ((u64) 1) << 48;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
469

e47c1feb1   Stefan Richter   ieee1394: fix lis...
470
471
  	list_add_tail(&host->dummy_zero_addr.host_list, &host->addr_space);
  	list_add_tail(&host->dummy_max_addr.host_list, &host->addr_space);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
472
473
474
475
  }
  
  void highlevel_add_host(struct hpsb_host *host)
  {
2c4b69bd7   Ben Collins   ieee1394: adjust ...
476
  	struct hpsb_highlevel *hl;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
477
478
479
480
  
  	init_hpsb_highlevel(host);
  
  	down_read(&hl_drivers_sem);
2c4b69bd7   Ben Collins   ieee1394: adjust ...
481
  	list_for_each_entry(hl, &hl_drivers, hl_list) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
482
483
  		if (hl->add_host)
  			hl->add_host(host);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
484
  	}
2c4b69bd7   Ben Collins   ieee1394: adjust ...
485
486
487
488
  	up_read(&hl_drivers_sem);
  	if (host->update_config_rom && hpsb_update_config_rom_image(host) < 0)
  		HPSB_ERR("Failed to generate Configuration ROM image for host "
  			 "%s-%d", hl->name, host->id);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
489
490
491
492
  }
  
  void highlevel_remove_host(struct hpsb_host *host)
  {
2c4b69bd7   Ben Collins   ieee1394: adjust ...
493
  	struct hpsb_highlevel *hl;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
494
495
496
497
498
499
500
501
502
  
  	down_read(&hl_drivers_sem);
  	list_for_each_entry(hl, &hl_drivers, hl_list)
  		__unregister_host(hl, host, 0);
  	up_read(&hl_drivers_sem);
  }
  
  void highlevel_host_reset(struct hpsb_host *host)
  {
445151932   Ben Collins   ieee1394: hl_irqs...
503
  	unsigned long flags;
2c4b69bd7   Ben Collins   ieee1394: adjust ...
504
  	struct hpsb_highlevel *hl;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
505

445151932   Ben Collins   ieee1394: hl_irqs...
506
  	read_lock_irqsave(&hl_irqs_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
507
  	list_for_each_entry(hl, &hl_irqs, irq_list) {
2c4b69bd7   Ben Collins   ieee1394: adjust ...
508
509
510
  		if (hl->host_reset)
  			hl->host_reset(host);
  	}
445151932   Ben Collins   ieee1394: hl_irqs...
511
  	read_unlock_irqrestore(&hl_irqs_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
512
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
513
514
515
  void highlevel_fcp_request(struct hpsb_host *host, int nodeid, int direction,
  			   void *data, size_t length)
  {
445151932   Ben Collins   ieee1394: hl_irqs...
516
  	unsigned long flags;
2c4b69bd7   Ben Collins   ieee1394: adjust ...
517
518
  	struct hpsb_highlevel *hl;
  	int cts = ((quadlet_t *)data)[0] >> 4;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
519

2c4b69bd7   Ben Collins   ieee1394: adjust ...
520
  	read_lock_irqsave(&hl_irqs_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
521
  	list_for_each_entry(hl, &hl_irqs, irq_list) {
2c4b69bd7   Ben Collins   ieee1394: adjust ...
522
523
  		if (hl->fcp_request)
  			hl->fcp_request(host, nodeid, direction, cts, data,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
524
  					length);
2c4b69bd7   Ben Collins   ieee1394: adjust ...
525
526
  	}
  	read_unlock_irqrestore(&hl_irqs_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
527
  }
afd6546d8   Stefan Richter   ieee1394: move so...
528
529
530
531
532
533
534
535
536
537
538
  /*
   * highlevel_read, highlevel_write, highlevel_lock, highlevel_lock64:
   *
   * These functions are called to handle transactions. They are called when a
   * packet arrives.  The flags argument contains the second word of the first
   * header quadlet of the incoming packet (containing transaction label, retry
   * code, transaction code and priority).  These functions either return a
   * response code or a negative number.  In the first case a response will be
   * generated.  In the latter case, no response will be sent and the driver which
   * handled the request will send the response itself.
   */
2c4b69bd7   Ben Collins   ieee1394: adjust ...
539
540
  int highlevel_read(struct hpsb_host *host, int nodeid, void *data, u64 addr,
  		   unsigned int length, u16 flags)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
541
  {
2c4b69bd7   Ben Collins   ieee1394: adjust ...
542
543
544
  	struct hpsb_address_serve *as;
  	unsigned int partlength;
  	int rcode = RCODE_ADDRESS_ERROR;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
545

2c4b69bd7   Ben Collins   ieee1394: adjust ...
546
  	read_lock(&addr_space_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
547
548
549
  	list_for_each_entry(as, &host->addr_space, host_list) {
  		if (as->start > addr)
  			break;
2c4b69bd7   Ben Collins   ieee1394: adjust ...
550
551
  		if (as->end > addr) {
  			partlength = min(as->end - addr, (u64) length);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
552

2c4b69bd7   Ben Collins   ieee1394: adjust ...
553
554
  			if (as->op->read)
  				rcode = as->op->read(host, nodeid, data,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
555
  						     addr, partlength, flags);
2c4b69bd7   Ben Collins   ieee1394: adjust ...
556
557
  			else
  				rcode = RCODE_TYPE_ERROR;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
558
559
  
  			data += partlength;
2c4b69bd7   Ben Collins   ieee1394: adjust ...
560
561
  			length -= partlength;
  			addr += partlength;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
562

2c4b69bd7   Ben Collins   ieee1394: adjust ...
563
564
565
566
567
  			if ((rcode != RCODE_COMPLETE) || !length)
  				break;
  		}
  	}
  	read_unlock(&addr_space_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
568

2c4b69bd7   Ben Collins   ieee1394: adjust ...
569
570
571
  	if (length && (rcode == RCODE_COMPLETE))
  		rcode = RCODE_ADDRESS_ERROR;
  	return rcode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
572
  }
2c4b69bd7   Ben Collins   ieee1394: adjust ...
573
574
  int highlevel_write(struct hpsb_host *host, int nodeid, int destid, void *data,
  		    u64 addr, unsigned int length, u16 flags)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
575
  {
2c4b69bd7   Ben Collins   ieee1394: adjust ...
576
577
578
  	struct hpsb_address_serve *as;
  	unsigned int partlength;
  	int rcode = RCODE_ADDRESS_ERROR;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
579

2c4b69bd7   Ben Collins   ieee1394: adjust ...
580
  	read_lock(&addr_space_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
581
582
583
  	list_for_each_entry(as, &host->addr_space, host_list) {
  		if (as->start > addr)
  			break;
2c4b69bd7   Ben Collins   ieee1394: adjust ...
584
585
  		if (as->end > addr) {
  			partlength = min(as->end - addr, (u64) length);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
586

2c4b69bd7   Ben Collins   ieee1394: adjust ...
587
588
589
590
591
592
  			if (as->op->write)
  				rcode = as->op->write(host, nodeid, destid,
  						      data, addr, partlength,
  						      flags);
  			else
  				rcode = RCODE_TYPE_ERROR;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
593
594
  
  			data += partlength;
2c4b69bd7   Ben Collins   ieee1394: adjust ...
595
596
  			length -= partlength;
  			addr += partlength;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
597

2c4b69bd7   Ben Collins   ieee1394: adjust ...
598
599
600
601
602
  			if ((rcode != RCODE_COMPLETE) || !length)
  				break;
  		}
  	}
  	read_unlock(&addr_space_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
603

2c4b69bd7   Ben Collins   ieee1394: adjust ...
604
605
606
  	if (length && (rcode == RCODE_COMPLETE))
  		rcode = RCODE_ADDRESS_ERROR;
  	return rcode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
607
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
608
  int highlevel_lock(struct hpsb_host *host, int nodeid, quadlet_t *store,
2c4b69bd7   Ben Collins   ieee1394: adjust ...
609
610
  		   u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode,
  		   u16 flags)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
611
  {
2c4b69bd7   Ben Collins   ieee1394: adjust ...
612
613
  	struct hpsb_address_serve *as;
  	int rcode = RCODE_ADDRESS_ERROR;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
614

2c4b69bd7   Ben Collins   ieee1394: adjust ...
615
  	read_lock(&addr_space_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
616
617
618
  	list_for_each_entry(as, &host->addr_space, host_list) {
  		if (as->start > addr)
  			break;
2c4b69bd7   Ben Collins   ieee1394: adjust ...
619
620
621
622
623
624
625
626
627
628
629
630
  		if (as->end > addr) {
  			if (as->op->lock)
  				rcode = as->op->lock(host, nodeid, store, addr,
  						     data, arg, ext_tcode,
  						     flags);
  			else
  				rcode = RCODE_TYPE_ERROR;
  			break;
  		}
  	}
  	read_unlock(&addr_space_lock);
  	return rcode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
631
632
633
  }
  
  int highlevel_lock64(struct hpsb_host *host, int nodeid, octlet_t *store,
2c4b69bd7   Ben Collins   ieee1394: adjust ...
634
635
  		     u64 addr, octlet_t data, octlet_t arg, int ext_tcode,
  		     u16 flags)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
636
  {
2c4b69bd7   Ben Collins   ieee1394: adjust ...
637
638
  	struct hpsb_address_serve *as;
  	int rcode = RCODE_ADDRESS_ERROR;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
639

2c4b69bd7   Ben Collins   ieee1394: adjust ...
640
  	read_lock(&addr_space_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
641
642
643
644
  
  	list_for_each_entry(as, &host->addr_space, host_list) {
  		if (as->start > addr)
  			break;
2c4b69bd7   Ben Collins   ieee1394: adjust ...
645
646
647
648
649
650
651
652
653
654
655
656
  		if (as->end > addr) {
  			if (as->op->lock64)
  				rcode = as->op->lock64(host, nodeid, store,
  						       addr, data, arg,
  						       ext_tcode, flags);
  			else
  				rcode = RCODE_TYPE_ERROR;
  			break;
  		}
  	}
  	read_unlock(&addr_space_lock);
  	return rcode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
657
  }