diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/isdn/hardware/eicon/um_idi.c |
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/isdn/hardware/eicon/um_idi.c')
-rw-r--r-- | drivers/isdn/hardware/eicon/um_idi.c | 885 |
1 files changed, 885 insertions, 0 deletions
diff --git a/drivers/isdn/hardware/eicon/um_idi.c b/drivers/isdn/hardware/eicon/um_idi.c new file mode 100644 index 000000000000..6563db998d06 --- /dev/null +++ b/drivers/isdn/hardware/eicon/um_idi.c | |||
@@ -0,0 +1,885 @@ | |||
1 | /* $Id: um_idi.c,v 1.14 2004/03/21 17:54:37 armin Exp $ */ | ||
2 | |||
3 | #include "platform.h" | ||
4 | #include "di_defs.h" | ||
5 | #include "pc.h" | ||
6 | #include "dqueue.h" | ||
7 | #include "adapter.h" | ||
8 | #include "entity.h" | ||
9 | #include "um_xdi.h" | ||
10 | #include "um_idi.h" | ||
11 | #include "debuglib.h" | ||
12 | #include "divasync.h" | ||
13 | |||
14 | #define DIVAS_MAX_XDI_ADAPTERS 64 | ||
15 | |||
16 | /* -------------------------------------------------------------------------- | ||
17 | IMPORTS | ||
18 | -------------------------------------------------------------------------- */ | ||
19 | extern void diva_os_wakeup_read(void *os_context); | ||
20 | extern void diva_os_wakeup_close(void *os_context); | ||
21 | /* -------------------------------------------------------------------------- | ||
22 | LOCALS | ||
23 | -------------------------------------------------------------------------- */ | ||
24 | static LIST_HEAD(adapter_q); | ||
25 | static diva_os_spin_lock_t adapter_lock; | ||
26 | |||
27 | static diva_um_idi_adapter_t *diva_um_idi_find_adapter(dword nr); | ||
28 | static void cleanup_adapter(diva_um_idi_adapter_t * a); | ||
29 | static void cleanup_entity(divas_um_idi_entity_t * e); | ||
30 | static int diva_user_mode_idi_adapter_features(diva_um_idi_adapter_t * a, | ||
31 | diva_um_idi_adapter_features_t | ||
32 | * features); | ||
33 | static int process_idi_request(divas_um_idi_entity_t * e, | ||
34 | const diva_um_idi_req_hdr_t * req); | ||
35 | static int process_idi_rc(divas_um_idi_entity_t * e, byte rc); | ||
36 | static int process_idi_ind(divas_um_idi_entity_t * e, byte ind); | ||
37 | static int write_return_code(divas_um_idi_entity_t * e, byte rc); | ||
38 | |||
39 | /* -------------------------------------------------------------------------- | ||
40 | MAIN | ||
41 | -------------------------------------------------------------------------- */ | ||
42 | int diva_user_mode_idi_init(void) | ||
43 | { | ||
44 | diva_os_initialize_spin_lock(&adapter_lock, "adapter"); | ||
45 | return (0); | ||
46 | } | ||
47 | |||
48 | /* -------------------------------------------------------------------------- | ||
49 | Copy adapter features to user supplied buffer | ||
50 | -------------------------------------------------------------------------- */ | ||
51 | static int | ||
52 | diva_user_mode_idi_adapter_features(diva_um_idi_adapter_t * a, | ||
53 | diva_um_idi_adapter_features_t * | ||
54 | features) | ||
55 | { | ||
56 | IDI_SYNC_REQ sync_req; | ||
57 | |||
58 | if ((a) && (a->d.request)) { | ||
59 | features->type = a->d.type; | ||
60 | features->features = a->d.features; | ||
61 | features->channels = a->d.channels; | ||
62 | memset(features->name, 0, sizeof(features->name)); | ||
63 | |||
64 | sync_req.GetName.Req = 0; | ||
65 | sync_req.GetName.Rc = IDI_SYNC_REQ_GET_NAME; | ||
66 | (*(a->d.request)) ((ENTITY *) & sync_req); | ||
67 | strlcpy(features->name, sync_req.GetName.name, | ||
68 | sizeof(features->name)); | ||
69 | |||
70 | sync_req.GetSerial.Req = 0; | ||
71 | sync_req.GetSerial.Rc = IDI_SYNC_REQ_GET_SERIAL; | ||
72 | sync_req.GetSerial.serial = 0; | ||
73 | (*(a->d.request)) ((ENTITY *) & sync_req); | ||
74 | features->serial_number = sync_req.GetSerial.serial; | ||
75 | } | ||
76 | |||
77 | return ((a) ? 0 : -1); | ||
78 | } | ||
79 | |||
80 | /* -------------------------------------------------------------------------- | ||
81 | REMOVE ADAPTER | ||
82 | -------------------------------------------------------------------------- */ | ||
83 | void diva_user_mode_idi_remove_adapter(int adapter_nr) | ||
84 | { | ||
85 | struct list_head *tmp; | ||
86 | diva_um_idi_adapter_t *a; | ||
87 | |||
88 | list_for_each(tmp, &adapter_q) { | ||
89 | a = list_entry(tmp, diva_um_idi_adapter_t, link); | ||
90 | if (a->adapter_nr == adapter_nr) { | ||
91 | list_del(tmp); | ||
92 | cleanup_adapter(a); | ||
93 | DBG_LOG(("DIDD: del adapter(%d)", a->adapter_nr)); | ||
94 | diva_os_free(0, a); | ||
95 | break; | ||
96 | } | ||
97 | } | ||
98 | } | ||
99 | |||
100 | /* -------------------------------------------------------------------------- | ||
101 | CALLED ON DRIVER EXIT (UNLOAD) | ||
102 | -------------------------------------------------------------------------- */ | ||
103 | void diva_user_mode_idi_finit(void) | ||
104 | { | ||
105 | struct list_head *tmp, *safe; | ||
106 | diva_um_idi_adapter_t *a; | ||
107 | |||
108 | list_for_each_safe(tmp, safe, &adapter_q) { | ||
109 | a = list_entry(tmp, diva_um_idi_adapter_t, link); | ||
110 | list_del(tmp); | ||
111 | cleanup_adapter(a); | ||
112 | DBG_LOG(("DIDD: del adapter(%d)", a->adapter_nr)); | ||
113 | diva_os_free(0, a); | ||
114 | } | ||
115 | diva_os_destroy_spin_lock(&adapter_lock, "adapter"); | ||
116 | } | ||
117 | |||
118 | /* ------------------------------------------------------------------------- | ||
119 | CREATE AND INIT IDI ADAPTER | ||
120 | ------------------------------------------------------------------------- */ | ||
121 | int diva_user_mode_idi_create_adapter(const DESCRIPTOR * d, int adapter_nr) | ||
122 | { | ||
123 | diva_os_spin_lock_magic_t old_irql; | ||
124 | diva_um_idi_adapter_t *a = | ||
125 | (diva_um_idi_adapter_t *) diva_os_malloc(0, | ||
126 | sizeof | ||
127 | (diva_um_idi_adapter_t)); | ||
128 | |||
129 | if (!a) { | ||
130 | return (-1); | ||
131 | } | ||
132 | memset(a, 0x00, sizeof(*a)); | ||
133 | INIT_LIST_HEAD(&a->entity_q); | ||
134 | |||
135 | a->d = *d; | ||
136 | a->adapter_nr = adapter_nr; | ||
137 | |||
138 | DBG_LOG(("DIDD_ADD A(%d), type:%02x, features:%04x, channels:%d", | ||
139 | adapter_nr, a->d.type, a->d.features, a->d.channels)); | ||
140 | |||
141 | diva_os_enter_spin_lock(&adapter_lock, &old_irql, "create_adapter"); | ||
142 | list_add_tail(&a->link, &adapter_q); | ||
143 | diva_os_leave_spin_lock(&adapter_lock, &old_irql, "create_adapter"); | ||
144 | return (0); | ||
145 | } | ||
146 | |||
147 | /* ------------------------------------------------------------------------ | ||
148 | Find adapter by Adapter number | ||
149 | ------------------------------------------------------------------------ */ | ||
150 | static diva_um_idi_adapter_t *diva_um_idi_find_adapter(dword nr) | ||
151 | { | ||
152 | diva_um_idi_adapter_t *a = NULL; | ||
153 | struct list_head *tmp; | ||
154 | |||
155 | list_for_each(tmp, &adapter_q) { | ||
156 | a = list_entry(tmp, diva_um_idi_adapter_t, link); | ||
157 | DBG_TRC(("find_adapter: (%d)-(%d)", nr, a->adapter_nr)); | ||
158 | if (a->adapter_nr == (int)nr) | ||
159 | break; | ||
160 | a = NULL; | ||
161 | } | ||
162 | return(a); | ||
163 | } | ||
164 | |||
165 | /* ------------------------------------------------------------------------ | ||
166 | Cleanup this adapter and cleanup/delete all entities assigned | ||
167 | to this adapter | ||
168 | ------------------------------------------------------------------------ */ | ||
169 | static void cleanup_adapter(diva_um_idi_adapter_t * a) | ||
170 | { | ||
171 | struct list_head *tmp, *safe; | ||
172 | divas_um_idi_entity_t *e; | ||
173 | |||
174 | list_for_each_safe(tmp, safe, &a->entity_q) { | ||
175 | e = list_entry(tmp, divas_um_idi_entity_t, link); | ||
176 | list_del(tmp); | ||
177 | cleanup_entity(e); | ||
178 | if (e->os_context) { | ||
179 | diva_os_wakeup_read(e->os_context); | ||
180 | diva_os_wakeup_close(e->os_context); | ||
181 | } | ||
182 | } | ||
183 | memset(&a->d, 0x00, sizeof(DESCRIPTOR)); | ||
184 | } | ||
185 | |||
186 | /* ------------------------------------------------------------------------ | ||
187 | Cleanup, but NOT delete this entity | ||
188 | ------------------------------------------------------------------------ */ | ||
189 | static void cleanup_entity(divas_um_idi_entity_t * e) | ||
190 | { | ||
191 | e->os_ref = NULL; | ||
192 | e->status = 0; | ||
193 | e->adapter = NULL; | ||
194 | e->e.Id = 0; | ||
195 | e->rc_count = 0; | ||
196 | |||
197 | e->status |= DIVA_UM_IDI_REMOVED; | ||
198 | e->status |= DIVA_UM_IDI_REMOVE_PENDING; | ||
199 | |||
200 | diva_data_q_finit(&e->data); | ||
201 | diva_data_q_finit(&e->rc); | ||
202 | } | ||
203 | |||
204 | |||
205 | /* ------------------------------------------------------------------------ | ||
206 | Create ENTITY, link it to the adapter and remove pointer to entity | ||
207 | ------------------------------------------------------------------------ */ | ||
208 | void *divas_um_idi_create_entity(dword adapter_nr, void *file) | ||
209 | { | ||
210 | divas_um_idi_entity_t *e; | ||
211 | diva_um_idi_adapter_t *a; | ||
212 | diva_os_spin_lock_magic_t old_irql; | ||
213 | |||
214 | if ((e = (divas_um_idi_entity_t *) diva_os_malloc(0, sizeof(*e)))) { | ||
215 | memset(e, 0x00, sizeof(*e)); | ||
216 | if (! | ||
217 | (e->os_context = | ||
218 | diva_os_malloc(0, diva_os_get_context_size()))) { | ||
219 | DBG_LOG(("E(%08x) no memory for os context", e)); | ||
220 | diva_os_free(0, e); | ||
221 | return NULL; | ||
222 | } | ||
223 | memset(e->os_context, 0x00, diva_os_get_context_size()); | ||
224 | |||
225 | if ((diva_data_q_init(&e->data, 2048 + 512, 16))) { | ||
226 | diva_os_free(0, e->os_context); | ||
227 | diva_os_free(0, e); | ||
228 | return NULL; | ||
229 | } | ||
230 | if ((diva_data_q_init(&e->rc, sizeof(diva_um_idi_ind_hdr_t), 2))) { | ||
231 | diva_data_q_finit(&e->data); | ||
232 | diva_os_free(0, e->os_context); | ||
233 | diva_os_free(0, e); | ||
234 | return NULL; | ||
235 | } | ||
236 | |||
237 | diva_os_enter_spin_lock(&adapter_lock, &old_irql, "create_entity"); | ||
238 | /* | ||
239 | Look for Adapter requested | ||
240 | */ | ||
241 | if (!(a = diva_um_idi_find_adapter(adapter_nr))) { | ||
242 | /* | ||
243 | No adapter was found, or this adapter was removed | ||
244 | */ | ||
245 | diva_os_leave_spin_lock(&adapter_lock, &old_irql, "create_entity"); | ||
246 | |||
247 | DBG_LOG(("A: no adapter(%ld)", adapter_nr)); | ||
248 | |||
249 | cleanup_entity(e); | ||
250 | diva_os_free(0, e->os_context); | ||
251 | diva_os_free(0, e); | ||
252 | |||
253 | return NULL; | ||
254 | } | ||
255 | |||
256 | e->os_ref = file; /* link to os handle */ | ||
257 | e->adapter = a; /* link to adapter */ | ||
258 | |||
259 | list_add_tail(&e->link, &a->entity_q); /* link from adapter */ | ||
260 | |||
261 | diva_os_leave_spin_lock(&adapter_lock, &old_irql, "create_entity"); | ||
262 | |||
263 | DBG_LOG(("A(%ld), create E(%08x)", adapter_nr, e)); | ||
264 | } | ||
265 | |||
266 | return (e); | ||
267 | } | ||
268 | |||
269 | /* ------------------------------------------------------------------------ | ||
270 | Unlink entity and free memory | ||
271 | ------------------------------------------------------------------------ */ | ||
272 | int divas_um_idi_delete_entity(int adapter_nr, void *entity) | ||
273 | { | ||
274 | divas_um_idi_entity_t *e; | ||
275 | diva_um_idi_adapter_t *a; | ||
276 | diva_os_spin_lock_magic_t old_irql; | ||
277 | |||
278 | if (!(e = (divas_um_idi_entity_t *) entity)) | ||
279 | return (-1); | ||
280 | |||
281 | diva_os_enter_spin_lock(&adapter_lock, &old_irql, "delete_entity"); | ||
282 | if ((a = e->adapter)) { | ||
283 | list_del(&e->link); | ||
284 | } | ||
285 | diva_os_leave_spin_lock(&adapter_lock, &old_irql, "delete_entity"); | ||
286 | |||
287 | diva_um_idi_stop_wdog(entity); | ||
288 | cleanup_entity(e); | ||
289 | diva_os_free(0, e->os_context); | ||
290 | memset(e, 0x00, sizeof(*e)); | ||
291 | diva_os_free(0, e); | ||
292 | |||
293 | DBG_LOG(("A(%d) remove E:%08x", adapter_nr, e)); | ||
294 | |||
295 | return (0); | ||
296 | } | ||
297 | |||
298 | /* -------------------------------------------------------------------------- | ||
299 | Called by application to read data from IDI | ||
300 | -------------------------------------------------------------------------- */ | ||
301 | int diva_um_idi_read(void *entity, | ||
302 | void *os_handle, | ||
303 | void *dst, | ||
304 | int max_length, divas_um_idi_copy_to_user_fn_t cp_fn) | ||
305 | { | ||
306 | divas_um_idi_entity_t *e; | ||
307 | diva_um_idi_adapter_t *a; | ||
308 | const void *data; | ||
309 | int length, ret = 0; | ||
310 | diva_um_idi_data_queue_t *q; | ||
311 | diva_os_spin_lock_magic_t old_irql; | ||
312 | |||
313 | diva_os_enter_spin_lock(&adapter_lock, &old_irql, "read"); | ||
314 | |||
315 | e = (divas_um_idi_entity_t *) entity; | ||
316 | if (!e || (!(a = e->adapter)) || | ||
317 | (e->status & DIVA_UM_IDI_REMOVE_PENDING) || | ||
318 | (e->status & DIVA_UM_IDI_REMOVED) || | ||
319 | (a->status & DIVA_UM_IDI_ADAPTER_REMOVED)) { | ||
320 | diva_os_leave_spin_lock(&adapter_lock, &old_irql, "read"); | ||
321 | DBG_ERR(("E(%08x) read failed - adapter removed", e)) | ||
322 | return (-1); | ||
323 | } | ||
324 | |||
325 | DBG_TRC(("A(%d) E(%08x) read(%d)", a->adapter_nr, e, max_length)); | ||
326 | |||
327 | /* | ||
328 | Try to read return code first | ||
329 | */ | ||
330 | data = diva_data_q_get_segment4read(&e->rc); | ||
331 | q = &e->rc; | ||
332 | |||
333 | /* | ||
334 | No return codes available, read indications now | ||
335 | */ | ||
336 | if (!data) { | ||
337 | if (!(e->status & DIVA_UM_IDI_RC_PENDING)) { | ||
338 | DBG_TRC(("A(%d) E(%08x) read data", a->adapter_nr, e)); | ||
339 | data = diva_data_q_get_segment4read(&e->data); | ||
340 | q = &e->data; | ||
341 | } | ||
342 | } else { | ||
343 | e->status &= ~DIVA_UM_IDI_RC_PENDING; | ||
344 | DBG_TRC(("A(%d) E(%08x) read rc", a->adapter_nr, e)); | ||
345 | } | ||
346 | |||
347 | if (data) { | ||
348 | if ((length = diva_data_q_get_segment_length(q)) > | ||
349 | max_length) { | ||
350 | /* | ||
351 | Not enough space to read message | ||
352 | */ | ||
353 | DBG_ERR(("A: A(%d) E(%08x) read small buffer", | ||
354 | a->adapter_nr, e, ret)); | ||
355 | diva_os_leave_spin_lock(&adapter_lock, &old_irql, | ||
356 | "read"); | ||
357 | return (-2); | ||
358 | } | ||
359 | /* | ||
360 | Copy it to user, this function does access ONLY locked an verified | ||
361 | memory, also we can access it witch spin lock held | ||
362 | */ | ||
363 | |||
364 | if ((ret = (*cp_fn) (os_handle, dst, data, length)) >= 0) { | ||
365 | /* | ||
366 | Acknowledge only if read was successfull | ||
367 | */ | ||
368 | diva_data_q_ack_segment4read(q); | ||
369 | } | ||
370 | } | ||
371 | |||
372 | |||
373 | DBG_TRC(("A(%d) E(%08x) read=%d", a->adapter_nr, e, ret)); | ||
374 | |||
375 | diva_os_leave_spin_lock(&adapter_lock, &old_irql, "read"); | ||
376 | |||
377 | return (ret); | ||
378 | } | ||
379 | |||
380 | |||
381 | int diva_um_idi_write(void *entity, | ||
382 | void *os_handle, | ||
383 | const void *src, | ||
384 | int length, divas_um_idi_copy_from_user_fn_t cp_fn) | ||
385 | { | ||
386 | divas_um_idi_entity_t *e; | ||
387 | diva_um_idi_adapter_t *a; | ||
388 | diva_um_idi_req_hdr_t *req; | ||
389 | void *data; | ||
390 | int ret = 0; | ||
391 | diva_os_spin_lock_magic_t old_irql; | ||
392 | |||
393 | diva_os_enter_spin_lock(&adapter_lock, &old_irql, "write"); | ||
394 | |||
395 | e = (divas_um_idi_entity_t *) entity; | ||
396 | if (!e || (!(a = e->adapter)) || | ||
397 | (e->status & DIVA_UM_IDI_REMOVE_PENDING) || | ||
398 | (e->status & DIVA_UM_IDI_REMOVED) || | ||
399 | (a->status & DIVA_UM_IDI_ADAPTER_REMOVED)) { | ||
400 | diva_os_leave_spin_lock(&adapter_lock, &old_irql, "write"); | ||
401 | DBG_ERR(("E(%08x) write failed - adapter removed", e)) | ||
402 | return (-1); | ||
403 | } | ||
404 | |||
405 | DBG_TRC(("A(%d) E(%08x) write(%d)", a->adapter_nr, e, length)); | ||
406 | |||
407 | if ((length < sizeof(*req)) || (length > sizeof(e->buffer))) { | ||
408 | diva_os_leave_spin_lock(&adapter_lock, &old_irql, "write"); | ||
409 | return (-2); | ||
410 | } | ||
411 | |||
412 | if (e->status & DIVA_UM_IDI_RC_PENDING) { | ||
413 | DBG_ERR(("A: A(%d) E(%08x) rc pending", a->adapter_nr, e)); | ||
414 | diva_os_leave_spin_lock(&adapter_lock, &old_irql, "write"); | ||
415 | return (-1); /* should wait for RC code first */ | ||
416 | } | ||
417 | |||
418 | /* | ||
419 | Copy function does access only locked verified memory, | ||
420 | also it can be called with spin lock held | ||
421 | */ | ||
422 | if ((ret = (*cp_fn) (os_handle, e->buffer, src, length)) < 0) { | ||
423 | DBG_TRC(("A: A(%d) E(%08x) write error=%d", a->adapter_nr, | ||
424 | e, ret)); | ||
425 | diva_os_leave_spin_lock(&adapter_lock, &old_irql, "write"); | ||
426 | return (ret); | ||
427 | } | ||
428 | |||
429 | req = (diva_um_idi_req_hdr_t *) & e->buffer[0]; | ||
430 | |||
431 | switch (req->type) { | ||
432 | case DIVA_UM_IDI_GET_FEATURES:{ | ||
433 | DBG_LOG(("A(%d) get_features", a->adapter_nr)); | ||
434 | if (!(data = | ||
435 | diva_data_q_get_segment4write(&e->data))) { | ||
436 | DBG_ERR(("A(%d) get_features, no free buffer", | ||
437 | a->adapter_nr)); | ||
438 | diva_os_leave_spin_lock(&adapter_lock, | ||
439 | &old_irql, | ||
440 | "write"); | ||
441 | return (0); | ||
442 | } | ||
443 | diva_user_mode_idi_adapter_features(a, &(((diva_um_idi_ind_hdr_t | ||
444 | *) data)->hdr.features)); | ||
445 | ((diva_um_idi_ind_hdr_t *) data)->type = | ||
446 | DIVA_UM_IDI_IND_FEATURES; | ||
447 | ((diva_um_idi_ind_hdr_t *) data)->data_length = 0; | ||
448 | diva_data_q_ack_segment4write(&e->data, | ||
449 | sizeof(diva_um_idi_ind_hdr_t)); | ||
450 | |||
451 | diva_os_leave_spin_lock(&adapter_lock, &old_irql, "write"); | ||
452 | |||
453 | diva_os_wakeup_read(e->os_context); | ||
454 | } | ||
455 | break; | ||
456 | |||
457 | case DIVA_UM_IDI_REQ: | ||
458 | case DIVA_UM_IDI_REQ_MAN: | ||
459 | case DIVA_UM_IDI_REQ_SIG: | ||
460 | case DIVA_UM_IDI_REQ_NET: | ||
461 | DBG_TRC(("A(%d) REQ(%02d)-(%02d)-(%08x)", a->adapter_nr, | ||
462 | req->Req, req->ReqCh, | ||
463 | req->type & DIVA_UM_IDI_REQ_TYPE_MASK)); | ||
464 | switch (process_idi_request(e, req)) { | ||
465 | case -1: | ||
466 | diva_os_leave_spin_lock(&adapter_lock, &old_irql, "write"); | ||
467 | return (-1); | ||
468 | case -2: | ||
469 | diva_os_leave_spin_lock(&adapter_lock, &old_irql, "write"); | ||
470 | diva_os_wakeup_read(e->os_context); | ||
471 | break; | ||
472 | default: | ||
473 | diva_os_leave_spin_lock(&adapter_lock, &old_irql, "write"); | ||
474 | break; | ||
475 | } | ||
476 | break; | ||
477 | |||
478 | default: | ||
479 | diva_os_leave_spin_lock(&adapter_lock, &old_irql, "write"); | ||
480 | return (-1); | ||
481 | } | ||
482 | |||
483 | DBG_TRC(("A(%d) E(%08x) write=%d", a->adapter_nr, e, ret)); | ||
484 | |||
485 | return (ret); | ||
486 | } | ||
487 | |||
488 | /* -------------------------------------------------------------------------- | ||
489 | CALLBACK FROM XDI | ||
490 | -------------------------------------------------------------------------- */ | ||
491 | static void diva_um_idi_xdi_callback(ENTITY * entity) | ||
492 | { | ||
493 | divas_um_idi_entity_t *e = DIVAS_CONTAINING_RECORD(entity, | ||
494 | divas_um_idi_entity_t, | ||
495 | e); | ||
496 | diva_os_spin_lock_magic_t old_irql; | ||
497 | int call_wakeup = 0; | ||
498 | |||
499 | diva_os_enter_spin_lock(&adapter_lock, &old_irql, "xdi_callback"); | ||
500 | |||
501 | if (e->e.complete == 255) { | ||
502 | if (!(e->status & DIVA_UM_IDI_REMOVE_PENDING)) { | ||
503 | diva_um_idi_stop_wdog(e); | ||
504 | } | ||
505 | if ((call_wakeup = process_idi_rc(e, e->e.Rc))) { | ||
506 | if (e->rc_count) { | ||
507 | e->rc_count--; | ||
508 | } | ||
509 | } | ||
510 | e->e.Rc = 0; | ||
511 | diva_os_leave_spin_lock(&adapter_lock, &old_irql, "xdi_callback"); | ||
512 | |||
513 | if (call_wakeup) { | ||
514 | diva_os_wakeup_read(e->os_context); | ||
515 | diva_os_wakeup_close(e->os_context); | ||
516 | } | ||
517 | } else { | ||
518 | if (e->status & DIVA_UM_IDI_REMOVE_PENDING) { | ||
519 | e->e.RNum = 0; | ||
520 | e->e.RNR = 2; | ||
521 | } else { | ||
522 | call_wakeup = process_idi_ind(e, e->e.Ind); | ||
523 | } | ||
524 | e->e.Ind = 0; | ||
525 | diva_os_leave_spin_lock(&adapter_lock, &old_irql, "xdi_callback"); | ||
526 | if (call_wakeup) { | ||
527 | diva_os_wakeup_read(e->os_context); | ||
528 | } | ||
529 | } | ||
530 | } | ||
531 | |||
532 | static int process_idi_request(divas_um_idi_entity_t * e, | ||
533 | const diva_um_idi_req_hdr_t * req) | ||
534 | { | ||
535 | int assign = 0; | ||
536 | byte Req = (byte) req->Req; | ||
537 | dword type = req->type & DIVA_UM_IDI_REQ_TYPE_MASK; | ||
538 | |||
539 | if (!e->e.Id || !e->e.callback) { /* not assigned */ | ||
540 | if (Req != ASSIGN) { | ||
541 | DBG_ERR(("A: A(%d) E(%08x) not assigned", | ||
542 | e->adapter->adapter_nr, e)); | ||
543 | return (-1); /* NOT ASSIGNED */ | ||
544 | } else { | ||
545 | switch (type) { | ||
546 | case DIVA_UM_IDI_REQ_TYPE_MAN: | ||
547 | e->e.Id = MAN_ID; | ||
548 | DBG_TRC(("A(%d) E(%08x) assign MAN", | ||
549 | e->adapter->adapter_nr, e)); | ||
550 | break; | ||
551 | |||
552 | case DIVA_UM_IDI_REQ_TYPE_SIG: | ||
553 | e->e.Id = DSIG_ID; | ||
554 | DBG_TRC(("A(%d) E(%08x) assign SIG", | ||
555 | e->adapter->adapter_nr, e)); | ||
556 | break; | ||
557 | |||
558 | case DIVA_UM_IDI_REQ_TYPE_NET: | ||
559 | e->e.Id = NL_ID; | ||
560 | DBG_TRC(("A(%d) E(%08x) assign NET", | ||
561 | e->adapter->adapter_nr, e)); | ||
562 | break; | ||
563 | |||
564 | default: | ||
565 | DBG_ERR(("A: A(%d) E(%08x) unknown type=%08x", | ||
566 | e->adapter->adapter_nr, e, | ||
567 | type)); | ||
568 | return (-1); | ||
569 | } | ||
570 | } | ||
571 | e->e.XNum = 1; | ||
572 | e->e.RNum = 1; | ||
573 | e->e.callback = diva_um_idi_xdi_callback; | ||
574 | e->e.X = &e->XData; | ||
575 | e->e.R = &e->RData; | ||
576 | assign = 1; | ||
577 | } | ||
578 | e->status |= DIVA_UM_IDI_RC_PENDING; | ||
579 | e->e.Req = Req; | ||
580 | e->e.ReqCh = (byte) req->ReqCh; | ||
581 | e->e.X->PLength = (word) req->data_length; | ||
582 | e->e.X->P = (byte *) & req[1]; /* Our buffer is safe */ | ||
583 | |||
584 | DBG_TRC(("A(%d) E(%08x) request(%02x-%02x-%02x (%d))", | ||
585 | e->adapter->adapter_nr, e, e->e.Id, e->e.Req, | ||
586 | e->e.ReqCh, e->e.X->PLength)); | ||
587 | |||
588 | e->rc_count++; | ||
589 | |||
590 | if (e->adapter && e->adapter->d.request) { | ||
591 | diva_um_idi_start_wdog(e); | ||
592 | (*(e->adapter->d.request)) (&e->e); | ||
593 | } | ||
594 | |||
595 | if (assign) { | ||
596 | if (e->e.Rc == OUT_OF_RESOURCES) { | ||
597 | /* | ||
598 | XDI has no entities more, call was not forwarded to the card, | ||
599 | no callback will be scheduled | ||
600 | */ | ||
601 | DBG_ERR(("A: A(%d) E(%08x) XDI out of entities", | ||
602 | e->adapter->adapter_nr, e)); | ||
603 | |||
604 | e->e.Id = 0; | ||
605 | e->e.ReqCh = 0; | ||
606 | e->e.RcCh = 0; | ||
607 | e->e.Ind = 0; | ||
608 | e->e.IndCh = 0; | ||
609 | e->e.XNum = 0; | ||
610 | e->e.RNum = 0; | ||
611 | e->e.callback = NULL; | ||
612 | e->e.X = NULL; | ||
613 | e->e.R = NULL; | ||
614 | write_return_code(e, ASSIGN_RC | OUT_OF_RESOURCES); | ||
615 | return (-2); | ||
616 | } else { | ||
617 | e->status |= DIVA_UM_IDI_ASSIGN_PENDING; | ||
618 | } | ||
619 | } | ||
620 | |||
621 | return (0); | ||
622 | } | ||
623 | |||
624 | static int process_idi_rc(divas_um_idi_entity_t * e, byte rc) | ||
625 | { | ||
626 | DBG_TRC(("A(%d) E(%08x) rc(%02x-%02x-%02x)", | ||
627 | e->adapter->adapter_nr, e, e->e.Id, rc, e->e.RcCh)); | ||
628 | |||
629 | if (e->status & DIVA_UM_IDI_ASSIGN_PENDING) { | ||
630 | e->status &= ~DIVA_UM_IDI_ASSIGN_PENDING; | ||
631 | if (rc != ASSIGN_OK) { | ||
632 | DBG_ERR(("A: A(%d) E(%08x) ASSIGN failed", | ||
633 | e->adapter->adapter_nr, e)); | ||
634 | e->e.callback = NULL; | ||
635 | e->e.Id = 0; | ||
636 | e->e.Req = 0; | ||
637 | e->e.ReqCh = 0; | ||
638 | e->e.Rc = 0; | ||
639 | e->e.RcCh = 0; | ||
640 | e->e.Ind = 0; | ||
641 | e->e.IndCh = 0; | ||
642 | e->e.X = NULL; | ||
643 | e->e.R = NULL; | ||
644 | e->e.XNum = 0; | ||
645 | e->e.RNum = 0; | ||
646 | } | ||
647 | } | ||
648 | if ((e->e.Req == REMOVE) && e->e.Id && (rc == 0xff)) { | ||
649 | DBG_ERR(("A: A(%d) E(%08x) discard OK in REMOVE", | ||
650 | e->adapter->adapter_nr, e)); | ||
651 | return (0); /* let us do it in the driver */ | ||
652 | } | ||
653 | if ((e->e.Req == REMOVE) && (!e->e.Id)) { /* REMOVE COMPLETE */ | ||
654 | e->e.callback = NULL; | ||
655 | e->e.Id = 0; | ||
656 | e->e.Req = 0; | ||
657 | e->e.ReqCh = 0; | ||
658 | e->e.Rc = 0; | ||
659 | e->e.RcCh = 0; | ||
660 | e->e.Ind = 0; | ||
661 | e->e.IndCh = 0; | ||
662 | e->e.X = NULL; | ||
663 | e->e.R = NULL; | ||
664 | e->e.XNum = 0; | ||
665 | e->e.RNum = 0; | ||
666 | e->rc_count = 0; | ||
667 | } | ||
668 | if ((e->e.Req == REMOVE) && (rc != 0xff)) { /* REMOVE FAILED */ | ||
669 | DBG_ERR(("A: A(%d) E(%08x) REMOVE FAILED", | ||
670 | e->adapter->adapter_nr, e)); | ||
671 | } | ||
672 | write_return_code(e, rc); | ||
673 | |||
674 | return (1); | ||
675 | } | ||
676 | |||
677 | static int process_idi_ind(divas_um_idi_entity_t * e, byte ind) | ||
678 | { | ||
679 | int do_wakeup = 0; | ||
680 | |||
681 | if (e->e.complete != 0x02) { | ||
682 | diva_um_idi_ind_hdr_t *pind = | ||
683 | (diva_um_idi_ind_hdr_t *) | ||
684 | diva_data_q_get_segment4write(&e->data); | ||
685 | if (pind) { | ||
686 | e->e.RNum = 1; | ||
687 | e->e.R->P = (byte *) & pind[1]; | ||
688 | e->e.R->PLength = | ||
689 | (word) (diva_data_q_get_max_length(&e->data) - | ||
690 | sizeof(*pind)); | ||
691 | DBG_TRC(("A(%d) E(%08x) ind_1(%02x-%02x-%02x)-[%d-%d]", | ||
692 | e->adapter->adapter_nr, e, e->e.Id, ind, | ||
693 | e->e.IndCh, e->e.RLength, | ||
694 | e->e.R->PLength)); | ||
695 | |||
696 | } else { | ||
697 | DBG_TRC(("A(%d) E(%08x) ind(%02x-%02x-%02x)-RNR", | ||
698 | e->adapter->adapter_nr, e, e->e.Id, ind, | ||
699 | e->e.IndCh)); | ||
700 | e->e.RNum = 0; | ||
701 | e->e.RNR = 1; | ||
702 | do_wakeup = 1; | ||
703 | } | ||
704 | } else { | ||
705 | diva_um_idi_ind_hdr_t *pind = | ||
706 | (diva_um_idi_ind_hdr_t *) (e->e.R->P); | ||
707 | |||
708 | DBG_TRC(("A(%d) E(%08x) ind(%02x-%02x-%02x)-[%d]", | ||
709 | e->adapter->adapter_nr, e, e->e.Id, ind, | ||
710 | e->e.IndCh, e->e.R->PLength)); | ||
711 | |||
712 | pind--; | ||
713 | pind->type = DIVA_UM_IDI_IND; | ||
714 | pind->hdr.ind.Ind = ind; | ||
715 | pind->hdr.ind.IndCh = e->e.IndCh; | ||
716 | pind->data_length = e->e.R->PLength; | ||
717 | diva_data_q_ack_segment4write(&e->data, | ||
718 | (int) (sizeof(*pind) + | ||
719 | e->e.R->PLength)); | ||
720 | do_wakeup = 1; | ||
721 | } | ||
722 | |||
723 | if ((e->status & DIVA_UM_IDI_RC_PENDING) && !e->rc.count) { | ||
724 | do_wakeup = 0; | ||
725 | } | ||
726 | |||
727 | return (do_wakeup); | ||
728 | } | ||
729 | |||
730 | /* -------------------------------------------------------------------------- | ||
731 | Write return code to the return code queue of entity | ||
732 | -------------------------------------------------------------------------- */ | ||
733 | static int write_return_code(divas_um_idi_entity_t * e, byte rc) | ||
734 | { | ||
735 | diva_um_idi_ind_hdr_t *prc; | ||
736 | |||
737 | if (!(prc = | ||
738 | (diva_um_idi_ind_hdr_t *) diva_data_q_get_segment4write(&e->rc))) | ||
739 | { | ||
740 | DBG_ERR(("A: A(%d) E(%08x) rc(%02x) lost", | ||
741 | e->adapter->adapter_nr, e, rc)); | ||
742 | e->status &= ~DIVA_UM_IDI_RC_PENDING; | ||
743 | return (-1); | ||
744 | } | ||
745 | |||
746 | prc->type = DIVA_UM_IDI_IND_RC; | ||
747 | prc->hdr.rc.Rc = rc; | ||
748 | prc->hdr.rc.RcCh = e->e.RcCh; | ||
749 | prc->data_length = 0; | ||
750 | diva_data_q_ack_segment4write(&e->rc, sizeof(*prc)); | ||
751 | |||
752 | return (0); | ||
753 | } | ||
754 | |||
755 | /* -------------------------------------------------------------------------- | ||
756 | Return amount of entries that can be bead from this entity or | ||
757 | -1 if adapter was removed | ||
758 | -------------------------------------------------------------------------- */ | ||
759 | int diva_user_mode_idi_ind_ready(void *entity, void *os_handle) | ||
760 | { | ||
761 | divas_um_idi_entity_t *e; | ||
762 | diva_um_idi_adapter_t *a; | ||
763 | diva_os_spin_lock_magic_t old_irql; | ||
764 | int ret; | ||
765 | |||
766 | if (!entity) | ||
767 | return (-1); | ||
768 | diva_os_enter_spin_lock(&adapter_lock, &old_irql, "ind_ready"); | ||
769 | e = (divas_um_idi_entity_t *) entity; | ||
770 | a = e->adapter; | ||
771 | |||
772 | if ((!a) || (a->status & DIVA_UM_IDI_ADAPTER_REMOVED)) { | ||
773 | /* | ||
774 | Adapter was unloaded | ||
775 | */ | ||
776 | diva_os_leave_spin_lock(&adapter_lock, &old_irql, "ind_ready"); | ||
777 | return (-1); /* adapter was removed */ | ||
778 | } | ||
779 | if (e->status & DIVA_UM_IDI_REMOVED) { | ||
780 | /* | ||
781 | entity was removed as result of adapter removal | ||
782 | user should assign this entity again | ||
783 | */ | ||
784 | diva_os_leave_spin_lock(&adapter_lock, &old_irql, "ind_ready"); | ||
785 | return (-1); | ||
786 | } | ||
787 | |||
788 | ret = e->rc.count + e->data.count; | ||
789 | |||
790 | if ((e->status & DIVA_UM_IDI_RC_PENDING) && !e->rc.count) { | ||
791 | ret = 0; | ||
792 | } | ||
793 | |||
794 | diva_os_leave_spin_lock(&adapter_lock, &old_irql, "ind_ready"); | ||
795 | |||
796 | return (ret); | ||
797 | } | ||
798 | |||
799 | void *diva_um_id_get_os_context(void *entity) | ||
800 | { | ||
801 | return (((divas_um_idi_entity_t *) entity)->os_context); | ||
802 | } | ||
803 | |||
804 | int divas_um_idi_entity_assigned(void *entity) | ||
805 | { | ||
806 | divas_um_idi_entity_t *e; | ||
807 | diva_um_idi_adapter_t *a; | ||
808 | int ret; | ||
809 | diva_os_spin_lock_magic_t old_irql; | ||
810 | |||
811 | diva_os_enter_spin_lock(&adapter_lock, &old_irql, "assigned?"); | ||
812 | |||
813 | |||
814 | e = (divas_um_idi_entity_t *) entity; | ||
815 | if (!e || (!(a = e->adapter)) || | ||
816 | (e->status & DIVA_UM_IDI_REMOVED) || | ||
817 | (a->status & DIVA_UM_IDI_ADAPTER_REMOVED)) { | ||
818 | diva_os_leave_spin_lock(&adapter_lock, &old_irql, "assigned?"); | ||
819 | return (0); | ||
820 | } | ||
821 | |||
822 | e->status |= DIVA_UM_IDI_REMOVE_PENDING; | ||
823 | |||
824 | ret = (e->e.Id || e->rc_count | ||
825 | || (e->status & DIVA_UM_IDI_ASSIGN_PENDING)); | ||
826 | |||
827 | DBG_TRC(("Id:%02x, rc_count:%d, status:%08x", e->e.Id, e->rc_count, | ||
828 | e->status)) | ||
829 | |||
830 | diva_os_leave_spin_lock(&adapter_lock, &old_irql, "assigned?"); | ||
831 | |||
832 | return (ret); | ||
833 | } | ||
834 | |||
835 | int divas_um_idi_entity_start_remove(void *entity) | ||
836 | { | ||
837 | divas_um_idi_entity_t *e; | ||
838 | diva_um_idi_adapter_t *a; | ||
839 | diva_os_spin_lock_magic_t old_irql; | ||
840 | |||
841 | diva_os_enter_spin_lock(&adapter_lock, &old_irql, "start_remove"); | ||
842 | |||
843 | e = (divas_um_idi_entity_t *) entity; | ||
844 | if (!e || (!(a = e->adapter)) || | ||
845 | (e->status & DIVA_UM_IDI_REMOVED) || | ||
846 | (a->status & DIVA_UM_IDI_ADAPTER_REMOVED)) { | ||
847 | diva_os_leave_spin_lock(&adapter_lock, &old_irql, "start_remove"); | ||
848 | return (0); | ||
849 | } | ||
850 | |||
851 | if (e->rc_count) { | ||
852 | /* | ||
853 | Entity BUSY | ||
854 | */ | ||
855 | diva_os_leave_spin_lock(&adapter_lock, &old_irql, "start_remove"); | ||
856 | return (1); | ||
857 | } | ||
858 | |||
859 | if (!e->e.Id) { | ||
860 | /* | ||
861 | Remove request was already pending, and arrived now | ||
862 | */ | ||
863 | diva_os_leave_spin_lock(&adapter_lock, &old_irql, "start_remove"); | ||
864 | return (0); /* REMOVE was pending */ | ||
865 | } | ||
866 | |||
867 | /* | ||
868 | Now send remove request | ||
869 | */ | ||
870 | e->e.Req = REMOVE; | ||
871 | e->e.ReqCh = 0; | ||
872 | |||
873 | e->rc_count++; | ||
874 | |||
875 | DBG_TRC(("A(%d) E(%08x) request(%02x-%02x-%02x (%d))", | ||
876 | e->adapter->adapter_nr, e, e->e.Id, e->e.Req, | ||
877 | e->e.ReqCh, e->e.X->PLength)); | ||
878 | |||
879 | if (a->d.request) | ||
880 | (*(a->d.request)) (&e->e); | ||
881 | |||
882 | diva_os_leave_spin_lock(&adapter_lock, &old_irql, "start_remove"); | ||
883 | |||
884 | return (0); | ||
885 | } | ||