1 /*
2  *
3  Copyright (c) Eicon Networks, 2002.
4  *
5  This source file is supplied for the use with
6  Eicon Networks range of DIVA Server Adapters.
7  *
8  Eicon File Revision :    2.1
9  *
10  This program is free software; you can redistribute it and/or modify
11  it under the terms of the GNU General Public License as published by
12  the Free Software Foundation; either version 2, or (at your option)
13  any later version.
14  *
15  This program is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
17  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18  See the GNU General Public License for more details.
19  *
20  You should have received a copy of the GNU General Public License
21  along with this program; if not, write to the Free Software
22  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23  *
24  */
25 #include "platform.h"
26 #include "pc.h"
27 #include "debuglib.h"
28 #include "di_defs.h"
29 #include "divasync.h"
30 #include "dadapter.h"
31 /* --------------------------------------------------------------------------
32    Adapter array change notification framework
33    -------------------------------------------------------------------------- */
34 typedef struct _didd_adapter_change_notification {
35 	didd_adapter_change_callback_t callback;
36 	void IDI_CALL_ENTITY_T *context;
37 } didd_adapter_change_notification_t,				\
38 	* IDI_CALL_ENTITY_T pdidd_adapter_change_notification_t;
39 #define DIVA_DIDD_MAX_NOTIFICATIONS 256
40 static didd_adapter_change_notification_t	\
41 NotificationTable[DIVA_DIDD_MAX_NOTIFICATIONS];
42 /* --------------------------------------------------------------------------
43    Array to held adapter information
44    -------------------------------------------------------------------------- */
45 static DESCRIPTOR  HandleTable[NEW_MAX_DESCRIPTORS];
46 static dword Adapters = 0; /* Number of adapters */
47 /* --------------------------------------------------------------------------
48    Shadow IDI_DIMAINT
49    and 'shadow' debug stuff
50    -------------------------------------------------------------------------- */
no_printf(unsigned char * format,...)51 static void no_printf(unsigned char *format, ...)
52 {
53 #ifdef EBUG
54 	va_list ap;
55 	va_start(ap, format);
56 	debug((format, ap));
57 	va_end(ap);
58 #endif
59 }
60 
61 /* -------------------------------------------------------------------------
62    Portable debug Library
63    ------------------------------------------------------------------------- */
64 #include "debuglib.c"
65 
66 static DESCRIPTOR  MAdapter =  {IDI_DIMAINT, /* Adapter Type */
67 				0x00,     /* Channels */
68 				0x0000,    /* Features */
69 				(IDI_CALL)no_printf};
70 /* --------------------------------------------------------------------------
71    DAdapter. Only IDI clients with buffer, that is huge enough to
72    get all descriptors will receive information about DAdapter
73    { byte type, byte channels, word features, IDI_CALL request }
74    -------------------------------------------------------------------------- */
75 static void IDI_CALL_LINK_T diva_dadapter_request(ENTITY IDI_CALL_ENTITY_T *);
76 static DESCRIPTOR  DAdapter =  {IDI_DADAPTER, /* Adapter Type */
77 				0x00,     /* Channels */
78 				0x0000,    /* Features */
79 				diva_dadapter_request };
80 /* --------------------------------------------------------------------------
81    LOCALS
82    -------------------------------------------------------------------------- */
83 static dword diva_register_adapter_callback(\
84 	didd_adapter_change_callback_t callback,
85 	void IDI_CALL_ENTITY_T *context);
86 static void diva_remove_adapter_callback(dword handle);
87 static void diva_notify_adapter_change(DESCRIPTOR *d, int removal);
88 static diva_os_spin_lock_t didd_spin;
89 /* --------------------------------------------------------------------------
90    Should be called as first step, after driver init
91    -------------------------------------------------------------------------- */
diva_didd_load_time_init(void)92 void diva_didd_load_time_init(void) {
93 	memset(&HandleTable[0], 0x00, sizeof(HandleTable));
94 	memset(&NotificationTable[0], 0x00, sizeof(NotificationTable));
95 	diva_os_initialize_spin_lock(&didd_spin, "didd");
96 }
97 /* --------------------------------------------------------------------------
98    Should be called as last step, if driver does unload
99    -------------------------------------------------------------------------- */
diva_didd_load_time_finit(void)100 void diva_didd_load_time_finit(void) {
101 	diva_os_destroy_spin_lock(&didd_spin, "didd");
102 }
103 /* --------------------------------------------------------------------------
104    Called in order to register new adapter in adapter array
105    return adapter handle (> 0) on success
106    return -1 adapter array overflow
107    -------------------------------------------------------------------------- */
diva_didd_add_descriptor(DESCRIPTOR * d)108 static int diva_didd_add_descriptor(DESCRIPTOR *d) {
109 	diva_os_spin_lock_magic_t      irql;
110 	int i;
111 	if (d->type == IDI_DIMAINT) {
112 		if (d->request) {
113 			MAdapter.request = d->request;
114 			dprintf = (DIVA_DI_PRINTF)d->request;
115 			diva_notify_adapter_change(&MAdapter, 0); /* Inserted */
116 			DBG_TRC(("DIMAINT registered, dprintf=%08x", d->request))
117 				} else {
118 			DBG_TRC(("DIMAINT removed"))
119 				diva_notify_adapter_change(&MAdapter, 1); /* About to remove */
120 			MAdapter.request = (IDI_CALL)no_printf;
121 			dprintf = no_printf;
122 		}
123 		return (NEW_MAX_DESCRIPTORS);
124 	}
125 	for (i = 0; i < NEW_MAX_DESCRIPTORS; i++) {
126 		diva_os_enter_spin_lock(&didd_spin, &irql, "didd_add");
127 		if (HandleTable[i].type == 0) {
128 			memcpy(&HandleTable[i], d, sizeof(*d));
129 			Adapters++;
130 			diva_os_leave_spin_lock(&didd_spin, &irql, "didd_add");
131 			diva_notify_adapter_change(d, 0); /* we have new adapter */
132 			DBG_TRC(("Add adapter[%d], request=%08x", (i + 1), d->request))
133 				return (i + 1);
134 		}
135 		diva_os_leave_spin_lock(&didd_spin, &irql, "didd_add");
136 	}
137 	DBG_ERR(("Can't add adapter, out of resources"))
138 		return (-1);
139 }
140 /* --------------------------------------------------------------------------
141    Called in order to remove one registered adapter from array
142    return adapter handle (> 0) on success
143    return 0 on success
144    -------------------------------------------------------------------------- */
diva_didd_remove_descriptor(IDI_CALL request)145 static int diva_didd_remove_descriptor(IDI_CALL request) {
146 	diva_os_spin_lock_magic_t      irql;
147 	int i;
148 	if (request == MAdapter.request) {
149 		DBG_TRC(("DIMAINT removed"))
150 			dprintf = no_printf;
151 		diva_notify_adapter_change(&MAdapter, 1); /* About to remove */
152 		MAdapter.request = (IDI_CALL)no_printf;
153 		return (0);
154 	}
155 	for (i = 0; (Adapters && (i < NEW_MAX_DESCRIPTORS)); i++) {
156 		if (HandleTable[i].request == request) {
157 			diva_notify_adapter_change(&HandleTable[i], 1); /* About to remove */
158 			diva_os_enter_spin_lock(&didd_spin, &irql, "didd_rm");
159 			memset(&HandleTable[i], 0x00, sizeof(HandleTable[0]));
160 			Adapters--;
161 			diva_os_leave_spin_lock(&didd_spin, &irql, "didd_rm");
162 			DBG_TRC(("Remove adapter[%d], request=%08x", (i + 1), request))
163 				return (0);
164 		}
165 	}
166 	DBG_ERR(("Invalid request=%08x, can't remove adapter", request))
167 		return (-1);
168 }
169 /* --------------------------------------------------------------------------
170    Read adapter array
171    return 1 if not enough space to save all available adapters
172    -------------------------------------------------------------------------- */
diva_didd_read_adapter_array(DESCRIPTOR * buffer,int length)173 static int diva_didd_read_adapter_array(DESCRIPTOR *buffer, int length) {
174 	diva_os_spin_lock_magic_t      irql;
175 	int src, dst;
176 	memset(buffer, 0x00, length);
177 	length /= sizeof(DESCRIPTOR);
178 	DBG_TRC(("DIDD_Read, space = %d, Adapters = %d", length, Adapters + 2))
179 
180 		diva_os_enter_spin_lock(&didd_spin, &irql, "didd_read");
181 	for (src = 0, dst = 0;
182 	     (Adapters && (src < NEW_MAX_DESCRIPTORS) && (dst < length));
183 	     src++) {
184 		if (HandleTable[src].type) {
185 			memcpy(&buffer[dst], &HandleTable[src], sizeof(DESCRIPTOR));
186 			dst++;
187 		}
188 	}
189 	diva_os_leave_spin_lock(&didd_spin, &irql, "didd_read");
190 	if (dst < length) {
191 		memcpy(&buffer[dst], &MAdapter, sizeof(DESCRIPTOR));
192 		dst++;
193 	} else {
194 		DBG_ERR(("Can't write DIMAINT. Array too small"))
195 			}
196 	if (dst < length) {
197 		memcpy(&buffer[dst], &DAdapter, sizeof(DESCRIPTOR));
198 		dst++;
199 	} else {
200 		DBG_ERR(("Can't write DADAPTER. Array too small"))
201 			}
202 	DBG_TRC(("Read %d adapters", dst))
203 		return (dst == length);
204 }
205 /* --------------------------------------------------------------------------
206    DAdapter request function.
207    This function does process only synchronous requests, and is used
208    for reception/registration of new interfaces
209    -------------------------------------------------------------------------- */
diva_dadapter_request(ENTITY IDI_CALL_ENTITY_T * e)210 static void IDI_CALL_LINK_T diva_dadapter_request(	\
211 	ENTITY IDI_CALL_ENTITY_T *e) {
212 	IDI_SYNC_REQ *syncReq = (IDI_SYNC_REQ *)e;
213 	if (e->Req) { /* We do not process it, also return error */
214 		e->Rc = OUT_OF_RESOURCES;
215 		DBG_ERR(("Can't process async request, Req=%02x", e->Req))
216 			return;
217 	}
218 	/*
219 	  So, we process sync request
220 	*/
221 	switch (e->Rc) {
222 	case IDI_SYNC_REQ_DIDD_REGISTER_ADAPTER_NOTIFY: {
223 		diva_didd_adapter_notify_t *pinfo = &syncReq->didd_notify.info;
224 		pinfo->handle = diva_register_adapter_callback(		\
225 			(didd_adapter_change_callback_t)pinfo->callback,
226 			(void IDI_CALL_ENTITY_T *)pinfo->context);
227 		e->Rc = 0xff;
228 	} break;
229 	case IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER_NOTIFY: {
230 		diva_didd_adapter_notify_t *pinfo = &syncReq->didd_notify.info;
231 		diva_remove_adapter_callback(pinfo->handle);
232 		e->Rc = 0xff;
233 	} break;
234 	case IDI_SYNC_REQ_DIDD_ADD_ADAPTER: {
235 		diva_didd_add_adapter_t *pinfo = &syncReq->didd_add_adapter.info;
236 		if (diva_didd_add_descriptor((DESCRIPTOR *)pinfo->descriptor) < 0) {
237 			e->Rc = OUT_OF_RESOURCES;
238 		} else {
239 			e->Rc = 0xff;
240 		}
241 	} break;
242 	case IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER: {
243 		diva_didd_remove_adapter_t *pinfo = &syncReq->didd_remove_adapter.info;
244 		if (diva_didd_remove_descriptor((IDI_CALL)pinfo->p_request) < 0) {
245 			e->Rc = OUT_OF_RESOURCES;
246 		} else {
247 			e->Rc = 0xff;
248 		}
249 	} break;
250 	case IDI_SYNC_REQ_DIDD_READ_ADAPTER_ARRAY: {
251 		diva_didd_read_adapter_array_t *pinfo =\
252 			&syncReq->didd_read_adapter_array.info;
253 		if (diva_didd_read_adapter_array((DESCRIPTOR *)pinfo->buffer,
254 						  (int)pinfo->length)) {
255 			e->Rc = OUT_OF_RESOURCES;
256 		} else {
257 			e->Rc = 0xff;
258 		}
259 	} break;
260 	default:
261 		DBG_ERR(("Can't process sync request, Req=%02x", e->Rc))
262 			e->Rc = OUT_OF_RESOURCES;
263 	}
264 }
265 /* --------------------------------------------------------------------------
266    IDI client does register his notification function
267    -------------------------------------------------------------------------- */
diva_register_adapter_callback(didd_adapter_change_callback_t callback,void IDI_CALL_ENTITY_T * context)268 static dword diva_register_adapter_callback(		\
269 	didd_adapter_change_callback_t callback,
270 	void IDI_CALL_ENTITY_T *context) {
271 	diva_os_spin_lock_magic_t irql;
272 	dword i;
273 
274 	for (i = 0; i < DIVA_DIDD_MAX_NOTIFICATIONS; i++) {
275 		diva_os_enter_spin_lock(&didd_spin, &irql, "didd_nfy_add");
276 		if (!NotificationTable[i].callback) {
277 			NotificationTable[i].callback = callback;
278 			NotificationTable[i].context = context;
279 			diva_os_leave_spin_lock(&didd_spin, &irql, "didd_nfy_add");
280 			DBG_TRC(("Register adapter notification[%d]=%08x", i + 1, callback))
281 				return (i + 1);
282 		}
283 		diva_os_leave_spin_lock(&didd_spin, &irql, "didd_nfy_add");
284 	}
285 	DBG_ERR(("Can't register adapter notification, overflow"))
286 		return (0);
287 }
288 /* --------------------------------------------------------------------------
289    IDI client does register his notification function
290    -------------------------------------------------------------------------- */
diva_remove_adapter_callback(dword handle)291 static void diva_remove_adapter_callback(dword handle) {
292 	diva_os_spin_lock_magic_t irql;
293 	if (handle && ((--handle) < DIVA_DIDD_MAX_NOTIFICATIONS)) {
294 		diva_os_enter_spin_lock(&didd_spin, &irql, "didd_nfy_rm");
295 		NotificationTable[handle].callback = NULL;
296 		NotificationTable[handle].context  = NULL;
297 		diva_os_leave_spin_lock(&didd_spin, &irql, "didd_nfy_rm");
298 		DBG_TRC(("Remove adapter notification[%d]", (int)(handle + 1)))
299 			return;
300 	}
301 	DBG_ERR(("Can't remove adapter notification, handle=%d", handle))
302 		}
303 /* --------------------------------------------------------------------------
304    Notify all client about adapter array change
305    Does suppose following behavior in the client side:
306    Step 1: Redister Notification
307    Step 2: Read Adapter Array
308    -------------------------------------------------------------------------- */
diva_notify_adapter_change(DESCRIPTOR * d,int removal)309 static void diva_notify_adapter_change(DESCRIPTOR *d, int removal) {
310 	int i, do_notify;
311 	didd_adapter_change_notification_t nfy;
312 	diva_os_spin_lock_magic_t irql;
313 	for (i = 0; i < DIVA_DIDD_MAX_NOTIFICATIONS; i++) {
314 		do_notify = 0;
315 		diva_os_enter_spin_lock(&didd_spin, &irql, "didd_nfy");
316 		if (NotificationTable[i].callback) {
317 			memcpy(&nfy, &NotificationTable[i], sizeof(nfy));
318 			do_notify = 1;
319 		}
320 		diva_os_leave_spin_lock(&didd_spin, &irql, "didd_nfy");
321 		if (do_notify) {
322 			(*(nfy.callback))(nfy.context, d, removal);
323 		}
324 	}
325 }
326 /* --------------------------------------------------------------------------
327    For all systems, that are linked by Kernel Mode Linker this is ONLY one
328    function thet should be exported by this device driver
329    IDI clients should look for IDI_DADAPTER, and use request function
330    of this adapter (sync request) in order to receive appropriate services:
331    - add new adapter
332    - remove existing adapter
333    - add adapter array notification
334    - remove adapter array notification
335    (read adapter is redundant in this case)
336    INPUT:
337    buffer - pointer to buffer that will receive adapter array
338    length  - length (in bytes) of space in buffer
339    OUTPUT:
340    Adapter array will be written to memory described by 'buffer'
341    If the last adapter seen in the returned adapter array is
342    IDI_DADAPTER or if last adapter in array does have type '0', then
343    it was enougth space in buffer to accommodate all available
344    adapter descriptors
345    *NOTE 1 (debug interface):
346    The IDI adapter of type 'IDI_DIMAINT' does register as 'request'
347    famous 'dprintf' function (of type DI_PRINTF, please look
348    include/debuglib.c and include/debuglib.h) for details.
349    So dprintf is not exported from module debug module directly,
350    instead of this IDI_DIMAINT is registered.
351    Module load order will receive in this case:
352    1. DIDD (this file)
353    2. DIMAINT does load and register 'IDI_DIMAINT', at this step
354    DIDD should be able to get 'dprintf', save it, and
355    register with DIDD by means of 'dprintf' function.
356    3. any other driver is loaded and is able to access adapter array
357    and debug interface
358    This approach does allow to load/unload debug interface on demand,
359    and save memory, it it is necessary.
360    -------------------------------------------------------------------------- */
DIVA_DIDD_Read(void IDI_CALL_ENTITY_T * buffer,int length)361 void IDI_CALL_LINK_T DIVA_DIDD_Read(void IDI_CALL_ENTITY_T *buffer,
362 				    int length) {
363 	diva_didd_read_adapter_array(buffer, length);
364 }
365