Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* $Id: mntfunc.c,v 1.19.6.4 2005/01/31 12:22:20 armin Exp $ |
| 2 | * |
| 3 | * Driver for Eicon DIVA Server ISDN cards. |
| 4 | * Maint module |
| 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 | |
| 14 | #include "platform.h" |
| 15 | #include "di_defs.h" |
| 16 | #include "divasync.h" |
| 17 | #include "debug_if.h" |
| 18 | |
| 19 | extern char *DRIVERRELEASE_MNT; |
| 20 | |
| 21 | #define DBG_MINIMUM (DL_LOG + DL_FTL + DL_ERR) |
| 22 | #define DBG_DEFAULT (DBG_MINIMUM + DL_XLOG + DL_REG) |
| 23 | |
| 24 | extern void DIVA_DIDD_Read(void *, int); |
| 25 | |
| 26 | static dword notify_handle; |
| 27 | static DESCRIPTOR DAdapter; |
| 28 | static DESCRIPTOR MAdapter; |
| 29 | static DESCRIPTOR MaintDescriptor = |
| 30 | { IDI_DIMAINT, 0, 0, (IDI_CALL) diva_maint_prtComp }; |
| 31 | |
| 32 | extern int diva_os_copy_to_user(void *os_handle, void __user *dst, |
| 33 | const void *src, int length); |
| 34 | extern int diva_os_copy_from_user(void *os_handle, void *dst, |
| 35 | const void __user *src, int length); |
| 36 | |
| 37 | static void no_printf(unsigned char *x, ...) |
| 38 | { |
| 39 | /* dummy debug function */ |
| 40 | } |
| 41 | |
| 42 | #include "debuglib.c" |
| 43 | |
| 44 | /* |
| 45 | * DIDD callback function |
| 46 | */ |
| 47 | static void *didd_callback(void *context, DESCRIPTOR * adapter, |
| 48 | int removal) |
| 49 | { |
| 50 | if (adapter->type == IDI_DADAPTER) { |
| 51 | DBG_ERR(("cb: Change in DAdapter ? Oops ?.")); |
| 52 | } else if (adapter->type == IDI_DIMAINT) { |
| 53 | if (removal) { |
| 54 | DbgDeregister(); |
| 55 | memset(&MAdapter, 0, sizeof(MAdapter)); |
| 56 | dprintf = no_printf; |
| 57 | } else { |
| 58 | memcpy(&MAdapter, adapter, sizeof(MAdapter)); |
| 59 | dprintf = (DIVA_DI_PRINTF) MAdapter.request; |
| 60 | DbgRegister("MAINT", DRIVERRELEASE_MNT, DBG_DEFAULT); |
| 61 | } |
| 62 | } else if ((adapter->type > 0) && (adapter->type < 16)) { |
| 63 | if (removal) { |
| 64 | diva_mnt_remove_xdi_adapter(adapter); |
| 65 | } else { |
| 66 | diva_mnt_add_xdi_adapter(adapter); |
| 67 | } |
| 68 | } |
| 69 | return (NULL); |
| 70 | } |
| 71 | |
| 72 | /* |
| 73 | * connect to didd |
| 74 | */ |
| 75 | static int DIVA_INIT_FUNCTION connect_didd(void) |
| 76 | { |
| 77 | int x = 0; |
| 78 | int dadapter = 0; |
| 79 | IDI_SYNC_REQ req; |
| 80 | DESCRIPTOR DIDD_Table[MAX_DESCRIPTORS]; |
| 81 | |
| 82 | DIVA_DIDD_Read(DIDD_Table, sizeof(DIDD_Table)); |
| 83 | |
| 84 | for (x = 0; x < MAX_DESCRIPTORS; x++) { |
| 85 | if (DIDD_Table[x].type == IDI_DADAPTER) { /* DADAPTER found */ |
| 86 | dadapter = 1; |
| 87 | memcpy(&DAdapter, &DIDD_Table[x], sizeof(DAdapter)); |
| 88 | req.didd_notify.e.Req = 0; |
| 89 | req.didd_notify.e.Rc = |
| 90 | IDI_SYNC_REQ_DIDD_REGISTER_ADAPTER_NOTIFY; |
| 91 | req.didd_notify.info.callback = (void *)didd_callback; |
| 92 | req.didd_notify.info.context = NULL; |
| 93 | DAdapter.request((ENTITY *) & req); |
| 94 | if (req.didd_notify.e.Rc != 0xff) |
| 95 | return (0); |
| 96 | notify_handle = req.didd_notify.info.handle; |
| 97 | /* Register MAINT (me) */ |
| 98 | req.didd_add_adapter.e.Req = 0; |
| 99 | req.didd_add_adapter.e.Rc = |
| 100 | IDI_SYNC_REQ_DIDD_ADD_ADAPTER; |
| 101 | req.didd_add_adapter.info.descriptor = |
| 102 | (void *) &MaintDescriptor; |
| 103 | DAdapter.request((ENTITY *) & req); |
| 104 | if (req.didd_add_adapter.e.Rc != 0xff) |
| 105 | return (0); |
| 106 | } else if ((DIDD_Table[x].type > 0) |
| 107 | && (DIDD_Table[x].type < 16)) { |
| 108 | diva_mnt_add_xdi_adapter(&DIDD_Table[x]); |
| 109 | } |
| 110 | } |
| 111 | return (dadapter); |
| 112 | } |
| 113 | |
| 114 | /* |
| 115 | * disconnect from didd |
| 116 | */ |
| 117 | static void DIVA_EXIT_FUNCTION disconnect_didd(void) |
| 118 | { |
| 119 | IDI_SYNC_REQ req; |
| 120 | |
| 121 | req.didd_notify.e.Req = 0; |
| 122 | req.didd_notify.e.Rc = IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER_NOTIFY; |
| 123 | req.didd_notify.info.handle = notify_handle; |
| 124 | DAdapter.request((ENTITY *) & req); |
| 125 | |
| 126 | req.didd_remove_adapter.e.Req = 0; |
| 127 | req.didd_remove_adapter.e.Rc = IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER; |
| 128 | req.didd_remove_adapter.info.p_request = |
| 129 | (IDI_CALL) MaintDescriptor.request; |
| 130 | DAdapter.request((ENTITY *) & req); |
| 131 | } |
| 132 | |
| 133 | /* |
| 134 | * read/write maint |
| 135 | */ |
| 136 | int maint_read_write(void __user *buf, int count) |
| 137 | { |
| 138 | byte data[128]; |
| 139 | dword cmd, id, mask; |
| 140 | int ret = 0; |
| 141 | |
| 142 | if (count < (3 * sizeof(dword))) |
| 143 | return (-EFAULT); |
| 144 | |
| 145 | if (diva_os_copy_from_user(NULL, (void *) &data[0], |
| 146 | buf, 3 * sizeof(dword))) { |
| 147 | return (-EFAULT); |
| 148 | } |
| 149 | |
| 150 | cmd = *(dword *) & data[0]; /* command */ |
| 151 | id = *(dword *) & data[4]; /* driver id */ |
| 152 | mask = *(dword *) & data[8]; /* mask or size */ |
| 153 | |
| 154 | switch (cmd) { |
| 155 | case DITRACE_CMD_GET_DRIVER_INFO: |
| 156 | if ((ret = diva_get_driver_info(id, data, sizeof(data))) > 0) { |
| 157 | if ((count < ret) || diva_os_copy_to_user |
| 158 | (NULL, buf, (void *) &data[0], ret)) |
| 159 | ret = -EFAULT; |
| 160 | } else { |
| 161 | ret = -EINVAL; |
| 162 | } |
| 163 | break; |
| 164 | |
| 165 | case DITRACE_READ_DRIVER_DBG_MASK: |
| 166 | if ((ret = diva_get_driver_dbg_mask(id, (byte *) data)) > 0) { |
| 167 | if ((count < ret) || diva_os_copy_to_user |
| 168 | (NULL, buf, (void *) &data[0], ret)) |
| 169 | ret = -EFAULT; |
| 170 | } else { |
| 171 | ret = -ENODEV; |
| 172 | } |
| 173 | break; |
| 174 | |
| 175 | case DITRACE_WRITE_DRIVER_DBG_MASK: |
| 176 | if ((ret = diva_set_driver_dbg_mask(id, mask)) <= 0) { |
| 177 | ret = -ENODEV; |
| 178 | } |
| 179 | break; |
| 180 | |
| 181 | /* |
| 182 | Filter commands will ignore the ID due to fact that filtering affects |
| 183 | the B- channel and Audio Tap trace levels only. Also MAINT driver will |
| 184 | select the right trace ID by itself |
| 185 | */ |
| 186 | case DITRACE_WRITE_SELECTIVE_TRACE_FILTER: |
| 187 | if (!mask) { |
| 188 | ret = diva_set_trace_filter (1, "*"); |
| 189 | } else if (mask < sizeof(data)) { |
| 190 | if (diva_os_copy_from_user(NULL, data, (char __user *)buf+12, mask)) { |
| 191 | ret = -EFAULT; |
| 192 | } else { |
| 193 | ret = diva_set_trace_filter ((int)mask, data); |
| 194 | } |
| 195 | } else { |
| 196 | ret = -EINVAL; |
| 197 | } |
| 198 | break; |
| 199 | |
| 200 | case DITRACE_READ_SELECTIVE_TRACE_FILTER: |
| 201 | if ((ret = diva_get_trace_filter (sizeof(data), data)) > 0) { |
| 202 | if (diva_os_copy_to_user (NULL, buf, data, ret)) |
| 203 | ret = -EFAULT; |
| 204 | } else { |
| 205 | ret = -ENODEV; |
| 206 | } |
| 207 | break; |
| 208 | |
| 209 | case DITRACE_READ_TRACE_ENTRY:{ |
| 210 | diva_os_spin_lock_magic_t old_irql; |
| 211 | word size; |
| 212 | diva_dbg_entry_head_t *pmsg; |
| 213 | byte *pbuf; |
| 214 | |
| 215 | if (!(pbuf = diva_os_malloc(0, mask))) { |
| 216 | return (-ENOMEM); |
| 217 | } |
| 218 | |
| 219 | for(;;) { |
| 220 | if (!(pmsg = |
| 221 | diva_maint_get_message(&size, &old_irql))) { |
| 222 | break; |
| 223 | } |
| 224 | if (size > mask) { |
| 225 | diva_maint_ack_message(0, &old_irql); |
| 226 | ret = -EINVAL; |
| 227 | break; |
| 228 | } |
| 229 | ret = size; |
| 230 | memcpy(pbuf, pmsg, size); |
| 231 | diva_maint_ack_message(1, &old_irql); |
| 232 | if ((count < size) || |
| 233 | diva_os_copy_to_user (NULL, buf, (void *) pbuf, size)) |
| 234 | ret = -EFAULT; |
| 235 | break; |
| 236 | } |
| 237 | diva_os_free(0, pbuf); |
| 238 | } |
| 239 | break; |
| 240 | |
| 241 | case DITRACE_READ_TRACE_ENTRYS:{ |
| 242 | diva_os_spin_lock_magic_t old_irql; |
| 243 | word size; |
| 244 | diva_dbg_entry_head_t *pmsg; |
| 245 | byte *pbuf = NULL; |
| 246 | int written = 0; |
| 247 | |
| 248 | if (mask < 4096) { |
| 249 | ret = -EINVAL; |
| 250 | break; |
| 251 | } |
| 252 | if (!(pbuf = diva_os_malloc(0, mask))) { |
| 253 | return (-ENOMEM); |
| 254 | } |
| 255 | |
| 256 | for (;;) { |
| 257 | if (!(pmsg = |
| 258 | diva_maint_get_message(&size, &old_irql))) { |
| 259 | break; |
| 260 | } |
| 261 | if ((size + 8) > mask) { |
| 262 | diva_maint_ack_message(0, &old_irql); |
| 263 | break; |
| 264 | } |
| 265 | /* |
| 266 | Write entry length |
| 267 | */ |
| 268 | pbuf[written++] = (byte) size; |
| 269 | pbuf[written++] = (byte) (size >> 8); |
| 270 | pbuf[written++] = 0; |
| 271 | pbuf[written++] = 0; |
| 272 | /* |
| 273 | Write message |
| 274 | */ |
| 275 | memcpy(&pbuf[written], pmsg, size); |
| 276 | diva_maint_ack_message(1, &old_irql); |
| 277 | written += size; |
| 278 | mask -= (size + 4); |
| 279 | } |
| 280 | pbuf[written++] = 0; |
| 281 | pbuf[written++] = 0; |
| 282 | pbuf[written++] = 0; |
| 283 | pbuf[written++] = 0; |
| 284 | |
| 285 | if ((count < written) || diva_os_copy_to_user(NULL, buf, (void *) pbuf, written)) { |
| 286 | ret = -EFAULT; |
| 287 | } else { |
| 288 | ret = written; |
| 289 | } |
| 290 | diva_os_free(0, pbuf); |
| 291 | } |
| 292 | break; |
| 293 | |
| 294 | default: |
| 295 | ret = -EINVAL; |
| 296 | } |
| 297 | return (ret); |
| 298 | } |
| 299 | |
| 300 | /* |
| 301 | * init |
| 302 | */ |
| 303 | int DIVA_INIT_FUNCTION mntfunc_init(int *buffer_length, void **buffer, |
| 304 | unsigned long diva_dbg_mem) |
| 305 | { |
| 306 | if (*buffer_length < 64) { |
| 307 | *buffer_length = 64; |
| 308 | } |
| 309 | if (*buffer_length > 512) { |
| 310 | *buffer_length = 512; |
| 311 | } |
| 312 | *buffer_length *= 1024; |
| 313 | |
| 314 | if (diva_dbg_mem) { |
| 315 | *buffer = (void *) diva_dbg_mem; |
| 316 | } else { |
| 317 | while ((*buffer_length >= (64 * 1024)) |
| 318 | && |
| 319 | (!(*buffer = diva_os_malloc (0, *buffer_length)))) { |
| 320 | *buffer_length -= 1024; |
| 321 | } |
| 322 | |
| 323 | if (!*buffer) { |
| 324 | DBG_ERR(("init: Can not alloc trace buffer")); |
| 325 | return (0); |
| 326 | } |
| 327 | } |
| 328 | |
| 329 | if (diva_maint_init(*buffer, *buffer_length, (diva_dbg_mem == 0))) { |
| 330 | if (!diva_dbg_mem) { |
| 331 | diva_os_free (0, *buffer); |
| 332 | } |
| 333 | DBG_ERR(("init: maint init failed")); |
| 334 | return (0); |
| 335 | } |
| 336 | |
| 337 | if (!connect_didd()) { |
| 338 | DBG_ERR(("init: failed to connect to DIDD.")); |
| 339 | diva_maint_finit(); |
| 340 | if (!diva_dbg_mem) { |
| 341 | diva_os_free (0, *buffer); |
| 342 | } |
| 343 | return (0); |
| 344 | } |
| 345 | return (1); |
| 346 | } |
| 347 | |
| 348 | /* |
| 349 | * exit |
| 350 | */ |
| 351 | void DIVA_EXIT_FUNCTION mntfunc_finit(void) |
| 352 | { |
| 353 | void *buffer; |
| 354 | int i = 100; |
| 355 | |
| 356 | DbgDeregister(); |
| 357 | |
| 358 | while (diva_mnt_shutdown_xdi_adapters() && i--) { |
| 359 | diva_os_sleep(10); |
| 360 | } |
| 361 | |
| 362 | disconnect_didd(); |
| 363 | |
| 364 | if ((buffer = diva_maint_finit())) { |
| 365 | diva_os_free (0, buffer); |
| 366 | } |
| 367 | |
| 368 | memset(&MAdapter, 0, sizeof(MAdapter)); |
| 369 | dprintf = no_printf; |
| 370 | } |