ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / drivers / isdn / hardware / eicon / idifunc.c
1 /* $Id: idifunc.c,v 1.14.4.2 2004/05/09 16:42:20 armin Exp $
2  *
3  * Driver for Eicon DIVA Server ISDN cards.
4  * User Mode IDI Interface 
5  *
6  * Copyright 2000-2003 by Armin Schindler (mac@melware.de)
7  * Copyright 2000-2003 Cytronics & Melware (info@melware.de)
8  *
9  * This software may be used and distributed according to the terms
10  * of the GNU General Public License, incorporated herein by reference.
11  */
12
13 #include "platform.h"
14 #include "di_defs.h"
15 #include "divasync.h"
16 #include "um_xdi.h"
17 #include "um_idi.h"
18
19 #define DBG_MINIMUM  (DL_LOG + DL_FTL + DL_ERR)
20 #define DBG_DEFAULT  (DBG_MINIMUM + DL_XLOG + DL_REG)
21
22 extern char *DRIVERRELEASE_IDI;
23
24 extern void DIVA_DIDD_Read(void *, int);
25 extern int diva_user_mode_idi_create_adapter(const DESCRIPTOR *, int);
26 extern void diva_user_mode_idi_remove_adapter(int);
27
28 #define MAX_DESCRIPTORS  32
29
30 static dword notify_handle;
31 static DESCRIPTOR DAdapter;
32 static DESCRIPTOR MAdapter;
33
34 static void no_printf(unsigned char *x, ...)
35 {
36         /* dummy debug function */
37 }
38
39 #include "debuglib.c"
40
41 /*
42  * stop debug
43  */
44 static void stop_dbg(void)
45 {
46         DbgDeregister();
47         memset(&MAdapter, 0, sizeof(MAdapter));
48         dprintf = no_printf;
49 }
50
51 typedef struct _udiva_card {
52         struct list_head list;
53         int Id;
54         DESCRIPTOR d;
55 } udiva_card;
56
57 static LIST_HEAD(cards);
58 static diva_os_spin_lock_t ll_lock;
59
60 /*
61  * find card in list
62  */
63 static udiva_card *find_card_in_list(DESCRIPTOR * d)
64 {
65         udiva_card *card;
66         struct list_head *tmp;
67         diva_os_spin_lock_magic_t old_irql;
68
69         diva_os_enter_spin_lock(&ll_lock, &old_irql, "find card");
70         list_for_each(tmp, &cards) {
71                 card = list_entry(tmp, udiva_card, list);
72                 if (card->d.request == d->request) {
73                         diva_os_leave_spin_lock(&ll_lock, &old_irql,
74                                                 "find card");
75                         return (card);
76                 }
77         }
78         diva_os_leave_spin_lock(&ll_lock, &old_irql, "find card");
79         return ((udiva_card *) NULL);
80 }
81
82 /*
83  * new card
84  */
85 static void um_new_card(DESCRIPTOR * d)
86 {
87         int adapter_nr = 0;
88         udiva_card *card = NULL;
89         IDI_SYNC_REQ sync_req;
90         diva_os_spin_lock_magic_t old_irql;
91
92         if (!(card = diva_os_malloc(0, sizeof(udiva_card)))) {
93                 DBG_ERR(("cannot get buffer for card"));
94                 return;
95         }
96         memcpy(&card->d, d, sizeof(DESCRIPTOR));
97         sync_req.xdi_logical_adapter_number.Req = 0;
98         sync_req.xdi_logical_adapter_number.Rc =
99             IDI_SYNC_REQ_XDI_GET_LOGICAL_ADAPTER_NUMBER;
100         card->d.request((ENTITY *) & sync_req);
101         adapter_nr =
102             sync_req.xdi_logical_adapter_number.info.logical_adapter_number;
103         card->Id = adapter_nr;
104         if (!(diva_user_mode_idi_create_adapter(d, adapter_nr))) {
105                 diva_os_enter_spin_lock(&ll_lock, &old_irql, "add card");
106                 list_add_tail(&card->list, &cards);
107                 diva_os_leave_spin_lock(&ll_lock, &old_irql, "add card");
108         } else {
109                 DBG_ERR(("could not create user mode idi card %d",
110                          adapter_nr));
111         }
112 }
113
114 /*
115  * remove card
116  */
117 static void um_remove_card(DESCRIPTOR * d)
118 {
119         diva_os_spin_lock_magic_t old_irql;
120         udiva_card *card = NULL;
121
122         if (!(card = find_card_in_list(d))) {
123                 DBG_ERR(("cannot find card to remove"));
124                 return;
125         }
126         diva_user_mode_idi_remove_adapter(card->Id);
127         diva_os_enter_spin_lock(&ll_lock, &old_irql, "remove card");
128         list_del(&card->list);
129         diva_os_leave_spin_lock(&ll_lock, &old_irql, "remove card");
130         DBG_LOG(("idi proc entry removed for card %d", card->Id));
131         diva_os_free(0, card);
132 }
133
134 /*
135  * remove all adapter
136  */
137 static void DIVA_EXIT_FUNCTION remove_all_idi_proc(void)
138 {
139         udiva_card *card;
140         diva_os_spin_lock_magic_t old_irql;
141
142 rescan:
143         diva_os_enter_spin_lock(&ll_lock, &old_irql, "remove all");
144         if (!list_empty(&cards)) {
145                 card = list_entry(cards.next, udiva_card, list);
146                 list_del(&card->list);
147                 diva_os_leave_spin_lock(&ll_lock, &old_irql, "remove all");
148                 diva_user_mode_idi_remove_adapter(card->Id);
149                 diva_os_free(0, card);
150                 goto rescan;
151         }
152         diva_os_leave_spin_lock(&ll_lock, &old_irql, "remove all");
153 }
154
155 /*
156  * DIDD notify callback
157  */
158 static void *didd_callback(void *context, DESCRIPTOR * adapter,
159                            int removal)
160 {
161         if (adapter->type == IDI_DADAPTER) {
162                 DBG_ERR(("Notification about IDI_DADAPTER change ! Oops."));
163                 return (NULL);
164         } else if (adapter->type == IDI_DIMAINT) {
165                 if (removal) {
166                         stop_dbg();
167                 } else {
168                         memcpy(&MAdapter, adapter, sizeof(MAdapter));
169                         dprintf = (DIVA_DI_PRINTF) MAdapter.request;
170                         DbgRegister("User IDI", DRIVERRELEASE_IDI, DBG_DEFAULT);
171                 }
172         } else if ((adapter->type > 0) && (adapter->type < 16)) {       /* IDI Adapter */
173                 if (removal) {
174                         um_remove_card(adapter);
175                 } else {
176                         um_new_card(adapter);
177                 }
178         }
179         return (NULL);
180 }
181
182 /*
183  * connect DIDD
184  */
185 static int DIVA_INIT_FUNCTION connect_didd(void)
186 {
187         int x = 0;
188         int dadapter = 0;
189         IDI_SYNC_REQ req;
190         DESCRIPTOR DIDD_Table[MAX_DESCRIPTORS];
191
192         DIVA_DIDD_Read(DIDD_Table, sizeof(DIDD_Table));
193
194         for (x = 0; x < MAX_DESCRIPTORS; x++) {
195                 if (DIDD_Table[x].type == IDI_DADAPTER) {       /* DADAPTER found */
196                         dadapter = 1;
197                         memcpy(&DAdapter, &DIDD_Table[x], sizeof(DAdapter));
198                         req.didd_notify.e.Req = 0;
199                         req.didd_notify.e.Rc =
200                             IDI_SYNC_REQ_DIDD_REGISTER_ADAPTER_NOTIFY;
201                         req.didd_notify.info.callback = (void *)didd_callback;
202                         req.didd_notify.info.context = 0;
203                         DAdapter.request((ENTITY *) & req);
204                         if (req.didd_notify.e.Rc != 0xff) {
205                                 stop_dbg();
206                                 return (0);
207                         }
208                         notify_handle = req.didd_notify.info.handle;
209                 } else if (DIDD_Table[x].type == IDI_DIMAINT) { /* MAINT found */
210                         memcpy(&MAdapter, &DIDD_Table[x], sizeof(DAdapter));
211                         dprintf = (DIVA_DI_PRINTF) MAdapter.request;
212                         DbgRegister("User IDI", DRIVERRELEASE_IDI, DBG_DEFAULT);
213                 } else if ((DIDD_Table[x].type > 0)
214                            && (DIDD_Table[x].type < 16)) {      /* IDI Adapter found */
215                         um_new_card(&DIDD_Table[x]);
216                 }
217         }
218
219         if (!dadapter) {
220                 stop_dbg();
221         }
222
223         return (dadapter);
224 }
225
226 /*
227  *  Disconnect from DIDD
228  */
229 static void DIVA_EXIT_FUNCTION disconnect_didd(void)
230 {
231         IDI_SYNC_REQ req;
232
233         stop_dbg();
234
235         req.didd_notify.e.Req = 0;
236         req.didd_notify.e.Rc = IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER_NOTIFY;
237         req.didd_notify.info.handle = notify_handle;
238         DAdapter.request((ENTITY *) & req);
239 }
240
241 /*
242  * init
243  */
244 int DIVA_INIT_FUNCTION idifunc_init(void)
245 {
246         diva_os_initialize_spin_lock(&ll_lock, "idifunc");
247
248         if (diva_user_mode_idi_init()) {
249                 DBG_ERR(("init: init failed."));
250                 return (0);
251         }
252
253         if (!connect_didd()) {
254                 diva_user_mode_idi_finit();
255                 DBG_ERR(("init: failed to connect to DIDD."));
256                 return (0);
257         }
258         return (1);
259 }
260
261 /*
262  * finit
263  */
264 void DIVA_EXIT_FUNCTION idifunc_finit(void)
265 {
266         diva_user_mode_idi_finit();
267         disconnect_didd();
268         remove_all_idi_proc();
269 }