1/* $Id: divasi.c,v 1.25.6.2 2005/01/31 12:22: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 <linux/module.h> 14#include <linux/init.h> 15#include <linux/kernel.h> 16#include <linux/sched.h> 17#include <linux/poll.h> 18#include <linux/proc_fs.h> 19#include <linux/skbuff.h> 20#include <linux/seq_file.h> 21#include <asm/uaccess.h> 22 23#include "platform.h" 24#include "di_defs.h" 25#include "divasync.h" 26#include "um_xdi.h" 27#include "um_idi.h" 28 29static char *main_revision = "$Revision: 1.25.6.2 $"; 30 31static int major; 32 33MODULE_DESCRIPTION("User IDI Interface for Eicon ISDN cards"); 34MODULE_AUTHOR("Cytronics & Melware, Eicon Networks"); 35MODULE_SUPPORTED_DEVICE("DIVA card driver"); 36MODULE_LICENSE("GPL"); 37 38typedef struct _diva_um_idi_os_context { 39 wait_queue_head_t read_wait; 40 wait_queue_head_t close_wait; 41 struct timer_list diva_timer_id; 42 int aborted; 43 int adapter_nr; 44} diva_um_idi_os_context_t; 45 46static char *DRIVERNAME = "Eicon DIVA - User IDI (http://www.melware.net)"; 47static char *DRIVERLNAME = "diva_idi"; 48static char *DEVNAME = "DivasIDI"; 49char *DRIVERRELEASE_IDI = "2.0"; 50 51extern int idifunc_init(void); 52extern void idifunc_finit(void); 53 54/* 55 * helper functions 56 */ 57static char *getrev(const char *revision) 58{ 59 char *rev; 60 char *p; 61 if ((p = strchr(revision, ':'))) { 62 rev = p + 2; 63 p = strchr(rev, '$'); 64 *--p = 0; 65 } else 66 rev = "1.0"; 67 return rev; 68} 69 70/* 71 * LOCALS 72 */ 73static ssize_t um_idi_read(struct file *file, char __user *buf, size_t count, 74 loff_t *offset); 75static ssize_t um_idi_write(struct file *file, const char __user *buf, 76 size_t count, loff_t *offset); 77static unsigned int um_idi_poll(struct file *file, poll_table *wait); 78static int um_idi_open(struct inode *inode, struct file *file); 79static int um_idi_release(struct inode *inode, struct file *file); 80static int remove_entity(void *entity); 81static void diva_um_timer_function(unsigned long data); 82 83/* 84 * proc entry 85 */ 86extern struct proc_dir_entry *proc_net_eicon; 87static struct proc_dir_entry *um_idi_proc_entry = NULL; 88 89static int um_idi_proc_show(struct seq_file *m, void *v) 90{ 91 char tmprev[32]; 92 93 seq_printf(m, "%s\n", DRIVERNAME); 94 seq_printf(m, "name : %s\n", DRIVERLNAME); 95 seq_printf(m, "release : %s\n", DRIVERRELEASE_IDI); 96 strcpy(tmprev, main_revision); 97 seq_printf(m, "revision : %s\n", getrev(tmprev)); 98 seq_printf(m, "build : %s\n", DIVA_BUILD); 99 seq_printf(m, "major : %d\n", major); 100 101 return 0; 102} 103 104static int um_idi_proc_open(struct inode *inode, struct file *file) 105{ 106 return single_open(file, um_idi_proc_show, NULL); 107} 108 109static const struct file_operations um_idi_proc_fops = { 110 .owner = THIS_MODULE, 111 .open = um_idi_proc_open, 112 .read = seq_read, 113 .llseek = seq_lseek, 114 .release = single_release, 115}; 116 117static int __init create_um_idi_proc(void) 118{ 119 um_idi_proc_entry = proc_create(DRIVERLNAME, S_IRUGO, proc_net_eicon, 120 &um_idi_proc_fops); 121 if (!um_idi_proc_entry) 122 return (0); 123 return (1); 124} 125 126static void remove_um_idi_proc(void) 127{ 128 if (um_idi_proc_entry) { 129 remove_proc_entry(DRIVERLNAME, proc_net_eicon); 130 um_idi_proc_entry = NULL; 131 } 132} 133 134static const struct file_operations divas_idi_fops = { 135 .owner = THIS_MODULE, 136 .llseek = no_llseek, 137 .read = um_idi_read, 138 .write = um_idi_write, 139 .poll = um_idi_poll, 140 .open = um_idi_open, 141 .release = um_idi_release 142}; 143 144static void divas_idi_unregister_chrdev(void) 145{ 146 unregister_chrdev(major, DEVNAME); 147} 148 149static int __init divas_idi_register_chrdev(void) 150{ 151 if ((major = register_chrdev(0, DEVNAME, &divas_idi_fops)) < 0) 152 { 153 printk(KERN_ERR "%s: failed to create /dev entry.\n", 154 DRIVERLNAME); 155 return (0); 156 } 157 158 return (1); 159} 160 161/* 162** Driver Load 163*/ 164static int __init divasi_init(void) 165{ 166 char tmprev[50]; 167 int ret = 0; 168 169 printk(KERN_INFO "%s\n", DRIVERNAME); 170 printk(KERN_INFO "%s: Rel:%s Rev:", DRIVERLNAME, DRIVERRELEASE_IDI); 171 strcpy(tmprev, main_revision); 172 printk("%s Build: %s\n", getrev(tmprev), DIVA_BUILD); 173 174 if (!divas_idi_register_chrdev()) { 175 ret = -EIO; 176 goto out; 177 } 178 179 if (!create_um_idi_proc()) { 180 divas_idi_unregister_chrdev(); 181 printk(KERN_ERR "%s: failed to create proc entry.\n", 182 DRIVERLNAME); 183 ret = -EIO; 184 goto out; 185 } 186 187 if (!(idifunc_init())) { 188 remove_um_idi_proc(); 189 divas_idi_unregister_chrdev(); 190 printk(KERN_ERR "%s: failed to connect to DIDD.\n", 191 DRIVERLNAME); 192 ret = -EIO; 193 goto out; 194 } 195 printk(KERN_INFO "%s: started with major %d\n", DRIVERLNAME, major); 196 197out: 198 return (ret); 199} 200 201 202/* 203** Driver Unload 204*/ 205static void __exit divasi_exit(void) 206{ 207 idifunc_finit(); 208 remove_um_idi_proc(); 209 divas_idi_unregister_chrdev(); 210 211 printk(KERN_INFO "%s: module unloaded.\n", DRIVERLNAME); 212} 213 214module_init(divasi_init); 215module_exit(divasi_exit); 216 217 218/* 219 * FILE OPERATIONS 220 */ 221 222static int 223divas_um_idi_copy_to_user(void *os_handle, void *dst, const void *src, 224 int length) 225{ 226 memcpy(dst, src, length); 227 return (length); 228} 229 230static ssize_t 231um_idi_read(struct file *file, char __user *buf, size_t count, loff_t *offset) 232{ 233 diva_um_idi_os_context_t *p_os; 234 int ret = -EINVAL; 235 void *data; 236 237 if (!file->private_data) { 238 return (-ENODEV); 239 } 240 241 if (! 242 (p_os = 243 (diva_um_idi_os_context_t *) diva_um_id_get_os_context(file-> 244 private_data))) 245 { 246 return (-ENODEV); 247 } 248 if (p_os->aborted) { 249 return (-ENODEV); 250 } 251 252 if (!(data = diva_os_malloc(0, count))) { 253 return (-ENOMEM); 254 } 255 256 ret = diva_um_idi_read(file->private_data, 257 file, data, count, 258 divas_um_idi_copy_to_user); 259 switch (ret) { 260 case 0: /* no message available */ 261 ret = (-EAGAIN); 262 break; 263 case (-1): /* adapter was removed */ 264 ret = (-ENODEV); 265 break; 266 case (-2): /* message_length > length of user buffer */ 267 ret = (-EFAULT); 268 break; 269 } 270 271 if (ret > 0) { 272 if (copy_to_user(buf, data, ret)) { 273 ret = (-EFAULT); 274 } 275 } 276 277 diva_os_free(0, data); 278 DBG_TRC(("read: ret %d", ret)); 279 return (ret); 280} 281 282 283static int 284divas_um_idi_copy_from_user(void *os_handle, void *dst, const void *src, 285 int length) 286{ 287 memcpy(dst, src, length); 288 return (length); 289} 290 291static int um_idi_open_adapter(struct file *file, int adapter_nr) 292{ 293 diva_um_idi_os_context_t *p_os; 294 void *e = 295 divas_um_idi_create_entity((dword) adapter_nr, (void *) file); 296 297 if (!(file->private_data = e)) { 298 return (0); 299 } 300 p_os = (diva_um_idi_os_context_t *) diva_um_id_get_os_context(e); 301 init_waitqueue_head(&p_os->read_wait); 302 init_waitqueue_head(&p_os->close_wait); 303 init_timer(&p_os->diva_timer_id); 304 p_os->diva_timer_id.function = (void *) diva_um_timer_function; 305 p_os->diva_timer_id.data = (unsigned long) p_os; 306 p_os->aborted = 0; 307 p_os->adapter_nr = adapter_nr; 308 return (1); 309} 310 311static ssize_t 312um_idi_write(struct file *file, const char __user *buf, size_t count, 313 loff_t *offset) 314{ 315 diva_um_idi_os_context_t *p_os; 316 int ret = -EINVAL; 317 void *data; 318 int adapter_nr = 0; 319 320 if (!file->private_data) { 321 /* the first write() selects the adapter_nr */ 322 if (count == sizeof(int)) { 323 if (copy_from_user 324 ((void *) &adapter_nr, buf, 325 count)) return (-EFAULT); 326 if (!(um_idi_open_adapter(file, adapter_nr))) 327 return (-ENODEV); 328 return (count); 329 } else 330 return (-ENODEV); 331 } 332 333 if (!(p_os = 334 (diva_um_idi_os_context_t *) diva_um_id_get_os_context(file-> 335 private_data))) 336 { 337 return (-ENODEV); 338 } 339 if (p_os->aborted) { 340 return (-ENODEV); 341 } 342 343 if (!(data = diva_os_malloc(0, count))) { 344 return (-ENOMEM); 345 } 346 347 if (copy_from_user(data, buf, count)) { 348 ret = -EFAULT; 349 } else { 350 ret = diva_um_idi_write(file->private_data, 351 file, data, count, 352 divas_um_idi_copy_from_user); 353 switch (ret) { 354 case 0: /* no space available */ 355 ret = (-EAGAIN); 356 break; 357 case (-1): /* adapter was removed */ 358 ret = (-ENODEV); 359 break; 360 case (-2): /* length of user buffer > max message_length */ 361 ret = (-EFAULT); 362 break; 363 } 364 } 365 diva_os_free(0, data); 366 DBG_TRC(("write: ret %d", ret)); 367 return (ret); 368} 369 370static unsigned int um_idi_poll(struct file *file, poll_table *wait) 371{ 372 diva_um_idi_os_context_t *p_os; 373 374 if (!file->private_data) { 375 return (POLLERR); 376 } 377 378 if ((!(p_os = 379 (diva_um_idi_os_context_t *) 380 diva_um_id_get_os_context(file->private_data))) 381 || p_os->aborted) { 382 return (POLLERR); 383 } 384 385 poll_wait(file, &p_os->read_wait, wait); 386 387 if (p_os->aborted) { 388 return (POLLERR); 389 } 390 391 switch (diva_user_mode_idi_ind_ready(file->private_data, file)) { 392 case (-1): 393 return (POLLERR); 394 395 case 0: 396 return (0); 397 } 398 399 return (POLLIN | POLLRDNORM); 400} 401 402static int um_idi_open(struct inode *inode, struct file *file) 403{ 404 return (0); 405} 406 407 408static int um_idi_release(struct inode *inode, struct file *file) 409{ 410 diva_um_idi_os_context_t *p_os; 411 unsigned int adapter_nr; 412 int ret = 0; 413 414 if (!(file->private_data)) { 415 ret = -ENODEV; 416 goto out; 417 } 418 419 if (!(p_os = 420 (diva_um_idi_os_context_t *) diva_um_id_get_os_context(file->private_data))) { 421 ret = -ENODEV; 422 goto out; 423 } 424 425 adapter_nr = p_os->adapter_nr; 426 427 if ((ret = remove_entity(file->private_data))) { 428 goto out; 429 } 430 431 if (divas_um_idi_delete_entity 432 ((int) adapter_nr, file->private_data)) { 433 ret = -ENODEV; 434 goto out; 435 } 436 437out: 438 return (ret); 439} 440 441int diva_os_get_context_size(void) 442{ 443 return (sizeof(diva_um_idi_os_context_t)); 444} 445 446void diva_os_wakeup_read(void *os_context) 447{ 448 diva_um_idi_os_context_t *p_os = 449 (diva_um_idi_os_context_t *) os_context; 450 wake_up_interruptible(&p_os->read_wait); 451} 452 453void diva_os_wakeup_close(void *os_context) 454{ 455 diva_um_idi_os_context_t *p_os = 456 (diva_um_idi_os_context_t *) os_context; 457 wake_up_interruptible(&p_os->close_wait); 458} 459 460static 461void diva_um_timer_function(unsigned long data) 462{ 463 diva_um_idi_os_context_t *p_os = (diva_um_idi_os_context_t *) data; 464 465 p_os->aborted = 1; 466 wake_up_interruptible(&p_os->read_wait); 467 wake_up_interruptible(&p_os->close_wait); 468 DBG_ERR(("entity removal watchdog")) 469 } 470 471/* 472** If application exits without entity removal this function will remove 473** entity and block until removal is complete 474*/ 475static int remove_entity(void *entity) 476{ 477 struct task_struct *curtask = current; 478 diva_um_idi_os_context_t *p_os; 479 480 diva_um_idi_stop_wdog(entity); 481 482 if (!entity) { 483 DBG_FTL(("Zero entity on remove")) 484 return (0); 485 } 486 487 if (!(p_os = 488 (diva_um_idi_os_context_t *) 489 diva_um_id_get_os_context(entity))) { 490 DBG_FTL(("Zero entity os context on remove")) 491 return (0); 492 } 493 494 if (!divas_um_idi_entity_assigned(entity) || p_os->aborted) { 495 /* 496 Entity is not assigned, also can be removed 497 */ 498 return (0); 499 } 500 501 DBG_TRC(("E(%08x) check remove", entity)) 502 503 /* 504 If adapter not answers on remove request inside of 505 10 Sec, then adapter is dead 506 */ 507 diva_um_idi_start_wdog(entity); 508 509 { 510 DECLARE_WAITQUEUE(wait, curtask); 511 512 add_wait_queue(&p_os->close_wait, &wait); 513 for (;;) { 514 set_current_state(TASK_INTERRUPTIBLE); 515 if (!divas_um_idi_entity_start_remove(entity) 516 || p_os->aborted) { 517 break; 518 } 519 schedule(); 520 } 521 set_current_state(TASK_RUNNING); 522 remove_wait_queue(&p_os->close_wait, &wait); 523 } 524 525 DBG_TRC(("E(%08x) start remove", entity)) 526 { 527 DECLARE_WAITQUEUE(wait, curtask); 528 529 add_wait_queue(&p_os->close_wait, &wait); 530 for (;;) { 531 set_current_state(TASK_INTERRUPTIBLE); 532 if (!divas_um_idi_entity_assigned(entity) 533 || p_os->aborted) { 534 break; 535 } 536 schedule(); 537 } 538 set_current_state(TASK_RUNNING); 539 remove_wait_queue(&p_os->close_wait, &wait); 540 } 541 542 DBG_TRC(("E(%08x) remove complete, aborted:%d", entity, 543 p_os->aborted)) 544 545 diva_um_idi_stop_wdog(entity); 546 547 p_os->aborted = 0; 548 549 return (0); 550} 551 552/* 553 * timer watchdog 554 */ 555void diva_um_idi_start_wdog(void *entity) 556{ 557 diva_um_idi_os_context_t *p_os; 558 559 if (entity && 560 ((p_os = 561 (diva_um_idi_os_context_t *) 562 diva_um_id_get_os_context(entity)))) { 563 mod_timer(&p_os->diva_timer_id, jiffies + 10 * HZ); 564 } 565} 566 567void diva_um_idi_stop_wdog(void *entity) 568{ 569 diva_um_idi_os_context_t *p_os; 570 571 if (entity && 572 ((p_os = 573 (diva_um_idi_os_context_t *) 574 diva_um_id_get_os_context(entity)))) { 575 del_timer(&p_os->diva_timer_id); 576 } 577} 578