import of ftp.dlink.com/GPL/DSMG-600_reB/ppclinux.tar.gz
[linux-2.4.21-pre4.git] / drivers / ieee1394 / hosts.c
1 /*
2  * IEEE 1394 for Linux
3  *
4  * Low level (host adapter) management.
5  *
6  * Copyright (C) 1999 Andreas E. Bombe
7  * Copyright (C) 1999 Emanuel Pirker
8  *
9  * This code is licensed under the GPL.  See the file COPYING in the root
10  * directory of the kernel sources for details.
11  */
12
13 #include <linux/config.h>
14 #include <linux/types.h>
15 #include <linux/list.h>
16 #include <linux/init.h>
17 #include <linux/slab.h>
18
19 #include "ieee1394_types.h"
20 #include "hosts.h"
21 #include "ieee1394_core.h"
22 #include "highlevel.h"
23
24 static struct list_head hosts = LIST_HEAD_INIT(hosts);
25 static struct list_head host_drivers = LIST_HEAD_INIT(host_drivers);
26
27 spinlock_t hosts_lock = SPIN_LOCK_UNLOCKED;
28 spinlock_t host_drivers_lock = SPIN_LOCK_UNLOCKED;
29
30
31 static int dummy_transmit_packet(struct hpsb_host *h, struct hpsb_packet *p)
32 {
33         return 0;
34 }
35
36 static int dummy_devctl(struct hpsb_host *h, enum devctl_cmd c, int arg)
37 {
38         return -1;
39 }
40
41 static struct hpsb_host_driver dummy_driver = {
42         .transmit_packet = dummy_transmit_packet,
43         .devctl =          dummy_devctl
44 };
45
46 /**
47  * hpsb_ref_host - increase reference count for host controller.
48  * @host: the host controller
49  *
50  * Increase the reference count for the specified host controller.
51  * When holding a reference to a host, the memory allocated for the
52  * host struct will not be freed and the host is guaranteed to be in a
53  * consistent state.  The driver may be unloaded or the controller may
54  * be removed (PCMCIA), but the host struct will remain valid.
55  */
56
57 int hpsb_ref_host(struct hpsb_host *host)
58 {
59         struct list_head *lh;
60         unsigned long flags;
61         int retval = 0;
62
63         spin_lock_irqsave(&hosts_lock, flags);
64         list_for_each(lh, &hosts) {
65                 if (host == list_entry(lh, struct hpsb_host, host_list)) {
66                         host->driver->devctl(host, MODIFY_USAGE, 1);
67                         host->refcount++;
68                         retval = 1;
69                         break;
70                 }
71         }
72         spin_unlock_irqrestore(&hosts_lock, flags);
73
74         return retval;
75 }
76
77 /**
78  * hpsb_unref_host - decrease reference count for host controller.
79  * @host: the host controller
80  *
81  * Decrease the reference count for the specified host controller.
82  * When the reference count reaches zero, the memory allocated for the
83  * &hpsb_host will be freed.
84  */
85
86 void hpsb_unref_host(struct hpsb_host *host)
87 {
88         unsigned long flags;
89
90         host->driver->devctl(host, MODIFY_USAGE, 0);
91
92         spin_lock_irqsave(&hosts_lock, flags);
93         host->refcount--;
94
95         if (!host->refcount && host->is_shutdown)
96                 kfree(host);
97         spin_unlock_irqrestore(&hosts_lock, flags);
98 }
99
100 /**
101  * hpsb_alloc_host - allocate a new host controller.
102  * @drv: the driver that will manage the host controller
103  * @extra: number of extra bytes to allocate for the driver
104  *
105  * Allocate a &hpsb_host and initialize the general subsystem specific
106  * fields.  If the driver needs to store per host data, as drivers
107  * usually do, the amount of memory required can be specified by the
108  * @extra parameter.  Once allocated, the driver should initialize the
109  * driver specific parts, enable the controller and make it available
110  * to the general subsystem using hpsb_add_host().
111  *
112  * The &hpsb_host is allocated with an single initial reference
113  * belonging to the driver.  Once the driver is done with the struct,
114  * for example, when the driver is unloaded, it should release this
115  * reference using hpsb_unref_host().
116  *
117  * Return Value: a pointer to the &hpsb_host if succesful, %NULL if
118  * no memory was available.
119  */
120
121 struct hpsb_host *hpsb_alloc_host(struct hpsb_host_driver *drv, size_t extra)
122 {
123         struct hpsb_host *h;
124
125         h = kmalloc(sizeof(struct hpsb_host) + extra, SLAB_KERNEL);
126         if (!h) return NULL;
127         memset(h, 0, sizeof(struct hpsb_host) + extra);
128
129         h->hostdata = h + 1;
130         h->driver = drv;
131         h->refcount = 1;
132
133         INIT_LIST_HEAD(&h->pending_packets);
134         spin_lock_init(&h->pending_pkt_lock);
135
136         sema_init(&h->tlabel_count, 64);
137         spin_lock_init(&h->tlabel_lock);
138
139         atomic_set(&h->generation, 0);
140
141         HPSB_INIT_WORK(&h->timeout_tq, (void (*)(void*))abort_timedouts, h);
142
143         h->topology_map = h->csr.topology_map + 3;
144         h->speed_map = (u8 *)(h->csr.speed_map + 2);
145
146         return h;
147 }
148
149 void hpsb_add_host(struct hpsb_host *host)
150 {
151         unsigned long flags;
152
153         spin_lock_irqsave(&hosts_lock, flags);
154         list_add_tail(&host->host_list, &hosts);
155         spin_unlock_irqrestore(&hosts_lock, flags);
156
157         highlevel_add_host(host);
158         host->driver->devctl(host, RESET_BUS, 0);
159 }
160
161 void hpsb_remove_host(struct hpsb_host *host)
162 {
163         unsigned long flags;
164
165         host->is_shutdown = 1;
166         host->driver = &dummy_driver;
167         highlevel_remove_host(host);
168
169         spin_lock_irqsave(&hosts_lock, flags);
170         list_del(&host->host_list);
171         spin_unlock_irqrestore(&hosts_lock, flags);
172 }
173
174 /*
175  * This function calls the given function for every host currently registered.
176  */
177 void hl_all_hosts(void (*function)(struct hpsb_host*))
178 {
179         struct list_head *lh;
180         struct hpsb_host *host;
181
182         spin_lock_irq(&hosts_lock);
183         list_for_each (lh, &hosts) {
184                 host = list_entry(lh, struct hpsb_host, host_list);
185                 function(host);
186         }
187         spin_unlock_irq(&hosts_lock);
188 }