linux/drivers/isdn/hardware/eicon/divasi.c
<<
>>
Prefs
   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 <linux/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        setup_timer(&p_os->diva_timer_id, (void *)diva_um_timer_function,
 304                    (unsigned long)p_os);
 305        p_os->aborted = 0;
 306        p_os->adapter_nr = adapter_nr;
 307        return (1);
 308}
 309
 310static ssize_t
 311um_idi_write(struct file *file, const char __user *buf, size_t count,
 312             loff_t *offset)
 313{
 314        diva_um_idi_os_context_t *p_os;
 315        int ret = -EINVAL;
 316        void *data;
 317        int adapter_nr = 0;
 318
 319        if (!file->private_data) {
 320                /* the first write() selects the adapter_nr */
 321                if (count == sizeof(int)) {
 322                        if (copy_from_user
 323                            ((void *) &adapter_nr, buf,
 324                             count)) return (-EFAULT);
 325                        if (!(um_idi_open_adapter(file, adapter_nr)))
 326                                return (-ENODEV);
 327                        return (count);
 328                } else
 329                        return (-ENODEV);
 330        }
 331
 332        if (!(p_os =
 333              (diva_um_idi_os_context_t *) diva_um_id_get_os_context(file->
 334                                                                     private_data)))
 335        {
 336                return (-ENODEV);
 337        }
 338        if (p_os->aborted) {
 339                return (-ENODEV);
 340        }
 341
 342        if (!(data = diva_os_malloc(0, count))) {
 343                return (-ENOMEM);
 344        }
 345
 346        if (copy_from_user(data, buf, count)) {
 347                ret = -EFAULT;
 348        } else {
 349                ret = diva_um_idi_write(file->private_data,
 350                                        file, data, count,
 351                                        divas_um_idi_copy_from_user);
 352                switch (ret) {
 353                case 0: /* no space available */
 354                        ret = (-EAGAIN);
 355                        break;
 356                case (-1):      /* adapter was removed */
 357                        ret = (-ENODEV);
 358                        break;
 359                case (-2):      /* length of user buffer > max message_length */
 360                        ret = (-EFAULT);
 361                        break;
 362                }
 363        }
 364        diva_os_free(0, data);
 365        DBG_TRC(("write: ret %d", ret));
 366        return (ret);
 367}
 368
 369static unsigned int um_idi_poll(struct file *file, poll_table *wait)
 370{
 371        diva_um_idi_os_context_t *p_os;
 372
 373        if (!file->private_data) {
 374                return (POLLERR);
 375        }
 376
 377        if ((!(p_os =
 378               (diva_um_idi_os_context_t *)
 379               diva_um_id_get_os_context(file->private_data)))
 380            || p_os->aborted) {
 381                return (POLLERR);
 382        }
 383
 384        poll_wait(file, &p_os->read_wait, wait);
 385
 386        if (p_os->aborted) {
 387                return (POLLERR);
 388        }
 389
 390        switch (diva_user_mode_idi_ind_ready(file->private_data, file)) {
 391        case (-1):
 392                return (POLLERR);
 393
 394        case 0:
 395                return (0);
 396        }
 397
 398        return (POLLIN | POLLRDNORM);
 399}
 400
 401static int um_idi_open(struct inode *inode, struct file *file)
 402{
 403        return (0);
 404}
 405
 406
 407static int um_idi_release(struct inode *inode, struct file *file)
 408{
 409        diva_um_idi_os_context_t *p_os;
 410        unsigned int adapter_nr;
 411        int ret = 0;
 412
 413        if (!(file->private_data)) {
 414                ret = -ENODEV;
 415                goto out;
 416        }
 417
 418        if (!(p_os =
 419              (diva_um_idi_os_context_t *) diva_um_id_get_os_context(file->private_data))) {
 420                ret = -ENODEV;
 421                goto out;
 422        }
 423
 424        adapter_nr = p_os->adapter_nr;
 425
 426        if ((ret = remove_entity(file->private_data))) {
 427                goto out;
 428        }
 429
 430        if (divas_um_idi_delete_entity
 431            ((int) adapter_nr, file->private_data)) {
 432                ret = -ENODEV;
 433                goto out;
 434        }
 435
 436out:
 437        return (ret);
 438}
 439
 440int diva_os_get_context_size(void)
 441{
 442        return (sizeof(diva_um_idi_os_context_t));
 443}
 444
 445void diva_os_wakeup_read(void *os_context)
 446{
 447        diva_um_idi_os_context_t *p_os =
 448                (diva_um_idi_os_context_t *) os_context;
 449        wake_up_interruptible(&p_os->read_wait);
 450}
 451
 452void diva_os_wakeup_close(void *os_context)
 453{
 454        diva_um_idi_os_context_t *p_os =
 455                (diva_um_idi_os_context_t *) os_context;
 456        wake_up_interruptible(&p_os->close_wait);
 457}
 458
 459static
 460void diva_um_timer_function(unsigned long data)
 461{
 462        diva_um_idi_os_context_t *p_os = (diva_um_idi_os_context_t *) data;
 463
 464        p_os->aborted = 1;
 465        wake_up_interruptible(&p_os->read_wait);
 466        wake_up_interruptible(&p_os->close_wait);
 467        DBG_ERR(("entity removal watchdog"))
 468                }
 469
 470/*
 471**  If application exits without entity removal this function will remove
 472**  entity and block until removal is complete
 473*/
 474static int remove_entity(void *entity)
 475{
 476        struct task_struct *curtask = current;
 477        diva_um_idi_os_context_t *p_os;
 478
 479        diva_um_idi_stop_wdog(entity);
 480
 481        if (!entity) {
 482                DBG_FTL(("Zero entity on remove"))
 483                        return (0);
 484        }
 485
 486        if (!(p_os =
 487              (diva_um_idi_os_context_t *)
 488              diva_um_id_get_os_context(entity))) {
 489                DBG_FTL(("Zero entity os context on remove"))
 490                        return (0);
 491        }
 492
 493        if (!divas_um_idi_entity_assigned(entity) || p_os->aborted) {
 494                /*
 495                  Entity is not assigned, also can be removed
 496                */
 497                return (0);
 498        }
 499
 500        DBG_TRC(("E(%08x) check remove", entity))
 501
 502                /*
 503                  If adapter not answers on remove request inside of
 504                  10 Sec, then adapter is dead
 505                */
 506                diva_um_idi_start_wdog(entity);
 507
 508        {
 509                DECLARE_WAITQUEUE(wait, curtask);
 510
 511                add_wait_queue(&p_os->close_wait, &wait);
 512                for (;;) {
 513                        set_current_state(TASK_INTERRUPTIBLE);
 514                        if (!divas_um_idi_entity_start_remove(entity)
 515                            || p_os->aborted) {
 516                                break;
 517                        }
 518                        schedule();
 519                }
 520                set_current_state(TASK_RUNNING);
 521                remove_wait_queue(&p_os->close_wait, &wait);
 522        }
 523
 524        DBG_TRC(("E(%08x) start remove", entity))
 525        {
 526                DECLARE_WAITQUEUE(wait, curtask);
 527
 528                add_wait_queue(&p_os->close_wait, &wait);
 529                for (;;) {
 530                        set_current_state(TASK_INTERRUPTIBLE);
 531                        if (!divas_um_idi_entity_assigned(entity)
 532                            || p_os->aborted) {
 533                                break;
 534                        }
 535                        schedule();
 536                }
 537                set_current_state(TASK_RUNNING);
 538                remove_wait_queue(&p_os->close_wait, &wait);
 539        }
 540
 541        DBG_TRC(("E(%08x) remove complete, aborted:%d", entity,
 542                 p_os->aborted))
 543
 544                diva_um_idi_stop_wdog(entity);
 545
 546        p_os->aborted = 0;
 547
 548        return (0);
 549}
 550
 551/*
 552 * timer watchdog
 553 */
 554void diva_um_idi_start_wdog(void *entity)
 555{
 556        diva_um_idi_os_context_t *p_os;
 557
 558        if (entity &&
 559            ((p_os =
 560              (diva_um_idi_os_context_t *)
 561              diva_um_id_get_os_context(entity)))) {
 562                mod_timer(&p_os->diva_timer_id, jiffies + 10 * HZ);
 563        }
 564}
 565
 566void diva_um_idi_stop_wdog(void *entity)
 567{
 568        diva_um_idi_os_context_t *p_os;
 569
 570        if (entity &&
 571            ((p_os =
 572              (diva_um_idi_os_context_t *)
 573              diva_um_id_get_os_context(entity)))) {
 574                del_timer(&p_os->diva_timer_id);
 575        }
 576}
 577