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