linux/drivers/isdn/gigaset/interface.c
<<
>>
Prefs
   1/*
   2 * interface to user space for the gigaset driver
   3 *
   4 * Copyright (c) 2004 by Hansjoerg Lipp <hjlipp@web.de>
   5 *
   6 * =====================================================================
   7 *    This program is free software; you can redistribute it and/or
   8 *    modify it under the terms of the GNU General Public License as
   9 *    published by the Free Software Foundation; either version 2 of
  10 *    the License, or (at your option) any later version.
  11 * =====================================================================
  12 */
  13
  14#include "gigaset.h"
  15#include <linux/gigaset_dev.h>
  16#include <linux/tty_flip.h>
  17#include <linux/module.h>
  18
  19/*** our ioctls ***/
  20
  21static int if_lock(struct cardstate *cs, int *arg)
  22{
  23        int cmd = *arg;
  24
  25        gig_dbg(DEBUG_IF, "%u: if_lock (%d)", cs->minor_index, cmd);
  26
  27        if (cmd > 1)
  28                return -EINVAL;
  29
  30        if (cmd < 0) {
  31                *arg = cs->mstate == MS_LOCKED;
  32                return 0;
  33        }
  34
  35        if (!cmd && cs->mstate == MS_LOCKED && cs->connected) {
  36                cs->ops->set_modem_ctrl(cs, 0, TIOCM_DTR | TIOCM_RTS);
  37                cs->ops->baud_rate(cs, B115200);
  38                cs->ops->set_line_ctrl(cs, CS8);
  39                cs->control_state = TIOCM_DTR | TIOCM_RTS;
  40        }
  41
  42        cs->waiting = 1;
  43        if (!gigaset_add_event(cs, &cs->at_state, EV_IF_LOCK,
  44                               NULL, cmd, NULL)) {
  45                cs->waiting = 0;
  46                return -ENOMEM;
  47        }
  48        gigaset_schedule_event(cs);
  49
  50        wait_event(cs->waitqueue, !cs->waiting);
  51
  52        if (cs->cmd_result >= 0) {
  53                *arg = cs->cmd_result;
  54                return 0;
  55        }
  56
  57        return cs->cmd_result;
  58}
  59
  60static int if_version(struct cardstate *cs, unsigned arg[4])
  61{
  62        static const unsigned version[4] = GIG_VERSION;
  63        static const unsigned compat[4] = GIG_COMPAT;
  64        unsigned cmd = arg[0];
  65
  66        gig_dbg(DEBUG_IF, "%u: if_version (%d)", cs->minor_index, cmd);
  67
  68        switch (cmd) {
  69        case GIGVER_DRIVER:
  70                memcpy(arg, version, sizeof version);
  71                return 0;
  72        case GIGVER_COMPAT:
  73                memcpy(arg, compat, sizeof compat);
  74                return 0;
  75        case GIGVER_FWBASE:
  76                cs->waiting = 1;
  77                if (!gigaset_add_event(cs, &cs->at_state, EV_IF_VER,
  78                                       NULL, 0, arg)) {
  79                        cs->waiting = 0;
  80                        return -ENOMEM;
  81                }
  82                gigaset_schedule_event(cs);
  83
  84                wait_event(cs->waitqueue, !cs->waiting);
  85
  86                if (cs->cmd_result >= 0)
  87                        return 0;
  88
  89                return cs->cmd_result;
  90        default:
  91                return -EINVAL;
  92        }
  93}
  94
  95static int if_config(struct cardstate *cs, int *arg)
  96{
  97        gig_dbg(DEBUG_IF, "%u: if_config (%d)", cs->minor_index, *arg);
  98
  99        if (*arg != 1)
 100                return -EINVAL;
 101
 102        if (cs->mstate != MS_LOCKED)
 103                return -EBUSY;
 104
 105        if (!cs->connected) {
 106                pr_err("%s: not connected\n", __func__);
 107                return -ENODEV;
 108        }
 109
 110        *arg = 0;
 111        return gigaset_enterconfigmode(cs);
 112}
 113
 114/*** the terminal driver ***/
 115
 116static int if_open(struct tty_struct *tty, struct file *filp)
 117{
 118        struct cardstate *cs;
 119
 120        gig_dbg(DEBUG_IF, "%d+%d: %s()",
 121                tty->driver->minor_start, tty->index, __func__);
 122
 123        cs = gigaset_get_cs_by_tty(tty);
 124        if (!cs || !try_module_get(cs->driver->owner))
 125                return -ENODEV;
 126
 127        if (mutex_lock_interruptible(&cs->mutex)) {
 128                module_put(cs->driver->owner);
 129                return -ERESTARTSYS;
 130        }
 131        tty->driver_data = cs;
 132
 133        ++cs->port.count;
 134
 135        if (cs->port.count == 1) {
 136                tty_port_tty_set(&cs->port, tty);
 137                cs->port.low_latency = 1;
 138        }
 139
 140        mutex_unlock(&cs->mutex);
 141        return 0;
 142}
 143
 144static void if_close(struct tty_struct *tty, struct file *filp)
 145{
 146        struct cardstate *cs = tty->driver_data;
 147
 148        if (!cs) { /* happens if we didn't find cs in open */
 149                gig_dbg(DEBUG_IF, "%s: no cardstate", __func__);
 150                return;
 151        }
 152
 153        gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
 154
 155        mutex_lock(&cs->mutex);
 156
 157        if (!cs->connected)
 158                gig_dbg(DEBUG_IF, "not connected");     /* nothing to do */
 159        else if (!cs->port.count)
 160                dev_warn(cs->dev, "%s: device not opened\n", __func__);
 161        else if (!--cs->port.count)
 162                tty_port_tty_set(&cs->port, NULL);
 163
 164        mutex_unlock(&cs->mutex);
 165
 166        module_put(cs->driver->owner);
 167}
 168
 169static int if_ioctl(struct tty_struct *tty,
 170                    unsigned int cmd, unsigned long arg)
 171{
 172        struct cardstate *cs = tty->driver_data;
 173        int retval = -ENODEV;
 174        int int_arg;
 175        unsigned char buf[6];
 176        unsigned version[4];
 177
 178        gig_dbg(DEBUG_IF, "%u: %s(0x%x)", cs->minor_index, __func__, cmd);
 179
 180        if (mutex_lock_interruptible(&cs->mutex))
 181                return -ERESTARTSYS;
 182
 183        if (!cs->connected) {
 184                gig_dbg(DEBUG_IF, "not connected");
 185                retval = -ENODEV;
 186        } else {
 187                retval = 0;
 188                switch (cmd) {
 189                case GIGASET_REDIR:
 190                        retval = get_user(int_arg, (int __user *) arg);
 191                        if (retval >= 0)
 192                                retval = if_lock(cs, &int_arg);
 193                        if (retval >= 0)
 194                                retval = put_user(int_arg, (int __user *) arg);
 195                        break;
 196                case GIGASET_CONFIG:
 197                        retval = get_user(int_arg, (int __user *) arg);
 198                        if (retval >= 0)
 199                                retval = if_config(cs, &int_arg);
 200                        if (retval >= 0)
 201                                retval = put_user(int_arg, (int __user *) arg);
 202                        break;
 203                case GIGASET_BRKCHARS:
 204                        retval = copy_from_user(&buf,
 205                                                (const unsigned char __user *) arg, 6)
 206                                ? -EFAULT : 0;
 207                        if (retval >= 0) {
 208                                gigaset_dbg_buffer(DEBUG_IF, "GIGASET_BRKCHARS",
 209                                                   6, (const unsigned char *) arg);
 210                                retval = cs->ops->brkchars(cs, buf);
 211                        }
 212                        break;
 213                case GIGASET_VERSION:
 214                        retval = copy_from_user(version,
 215                                                (unsigned __user *) arg, sizeof version)
 216                                ? -EFAULT : 0;
 217                        if (retval >= 0)
 218                                retval = if_version(cs, version);
 219                        if (retval >= 0)
 220                                retval = copy_to_user((unsigned __user *) arg,
 221                                                      version, sizeof version)
 222                                        ? -EFAULT : 0;
 223                        break;
 224                default:
 225                        gig_dbg(DEBUG_IF, "%s: arg not supported - 0x%04x",
 226                                __func__, cmd);
 227                        retval = -ENOIOCTLCMD;
 228                }
 229        }
 230
 231        mutex_unlock(&cs->mutex);
 232
 233        return retval;
 234}
 235
 236static int if_tiocmget(struct tty_struct *tty)
 237{
 238        struct cardstate *cs = tty->driver_data;
 239        int retval;
 240
 241        gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
 242
 243        if (mutex_lock_interruptible(&cs->mutex))
 244                return -ERESTARTSYS;
 245
 246        retval = cs->control_state & (TIOCM_RTS | TIOCM_DTR);
 247
 248        mutex_unlock(&cs->mutex);
 249
 250        return retval;
 251}
 252
 253static int if_tiocmset(struct tty_struct *tty,
 254                       unsigned int set, unsigned int clear)
 255{
 256        struct cardstate *cs = tty->driver_data;
 257        int retval;
 258        unsigned mc;
 259
 260        gig_dbg(DEBUG_IF, "%u: %s(0x%x, 0x%x)",
 261                cs->minor_index, __func__, set, clear);
 262
 263        if (mutex_lock_interruptible(&cs->mutex))
 264                return -ERESTARTSYS;
 265
 266        if (!cs->connected) {
 267                gig_dbg(DEBUG_IF, "not connected");
 268                retval = -ENODEV;
 269        } else {
 270                mc = (cs->control_state | set) & ~clear & (TIOCM_RTS | TIOCM_DTR);
 271                retval = cs->ops->set_modem_ctrl(cs, cs->control_state, mc);
 272                cs->control_state = mc;
 273        }
 274
 275        mutex_unlock(&cs->mutex);
 276
 277        return retval;
 278}
 279
 280static int if_write(struct tty_struct *tty, const unsigned char *buf, int count)
 281{
 282        struct cardstate *cs = tty->driver_data;
 283        struct cmdbuf_t *cb;
 284        int retval;
 285
 286        gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
 287
 288        if (mutex_lock_interruptible(&cs->mutex))
 289                return -ERESTARTSYS;
 290
 291        if (!cs->connected) {
 292                gig_dbg(DEBUG_IF, "not connected");
 293                retval = -ENODEV;
 294                goto done;
 295        }
 296        if (cs->mstate != MS_LOCKED) {
 297                dev_warn(cs->dev, "can't write to unlocked device\n");
 298                retval = -EBUSY;
 299                goto done;
 300        }
 301        if (count <= 0) {
 302                /* nothing to do */
 303                retval = 0;
 304                goto done;
 305        }
 306
 307        cb = kmalloc(sizeof(struct cmdbuf_t) + count, GFP_KERNEL);
 308        if (!cb) {
 309                dev_err(cs->dev, "%s: out of memory\n", __func__);
 310                retval = -ENOMEM;
 311                goto done;
 312        }
 313
 314        memcpy(cb->buf, buf, count);
 315        cb->len = count;
 316        cb->offset = 0;
 317        cb->next = NULL;
 318        cb->wake_tasklet = &cs->if_wake_tasklet;
 319        retval = cs->ops->write_cmd(cs, cb);
 320done:
 321        mutex_unlock(&cs->mutex);
 322        return retval;
 323}
 324
 325static int if_write_room(struct tty_struct *tty)
 326{
 327        struct cardstate *cs = tty->driver_data;
 328        int retval;
 329
 330        gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
 331
 332        if (mutex_lock_interruptible(&cs->mutex))
 333                return -ERESTARTSYS;
 334
 335        if (!cs->connected) {
 336                gig_dbg(DEBUG_IF, "not connected");
 337                retval = -ENODEV;
 338        } else if (cs->mstate != MS_LOCKED) {
 339                dev_warn(cs->dev, "can't write to unlocked device\n");
 340                retval = -EBUSY;
 341        } else
 342                retval = cs->ops->write_room(cs);
 343
 344        mutex_unlock(&cs->mutex);
 345
 346        return retval;
 347}
 348
 349static int if_chars_in_buffer(struct tty_struct *tty)
 350{
 351        struct cardstate *cs = tty->driver_data;
 352        int retval = 0;
 353
 354        gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
 355
 356        mutex_lock(&cs->mutex);
 357
 358        if (!cs->connected)
 359                gig_dbg(DEBUG_IF, "not connected");
 360        else if (cs->mstate != MS_LOCKED)
 361                dev_warn(cs->dev, "can't write to unlocked device\n");
 362        else
 363                retval = cs->ops->chars_in_buffer(cs);
 364
 365        mutex_unlock(&cs->mutex);
 366
 367        return retval;
 368}
 369
 370static void if_throttle(struct tty_struct *tty)
 371{
 372        struct cardstate *cs = tty->driver_data;
 373
 374        gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
 375
 376        mutex_lock(&cs->mutex);
 377
 378        if (!cs->connected)
 379                gig_dbg(DEBUG_IF, "not connected");     /* nothing to do */
 380        else
 381                gig_dbg(DEBUG_IF, "%s: not implemented\n", __func__);
 382
 383        mutex_unlock(&cs->mutex);
 384}
 385
 386static void if_unthrottle(struct tty_struct *tty)
 387{
 388        struct cardstate *cs = tty->driver_data;
 389
 390        gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
 391
 392        mutex_lock(&cs->mutex);
 393
 394        if (!cs->connected)
 395                gig_dbg(DEBUG_IF, "not connected");     /* nothing to do */
 396        else
 397                gig_dbg(DEBUG_IF, "%s: not implemented\n", __func__);
 398
 399        mutex_unlock(&cs->mutex);
 400}
 401
 402static void if_set_termios(struct tty_struct *tty, struct ktermios *old)
 403{
 404        struct cardstate *cs = tty->driver_data;
 405        unsigned int iflag;
 406        unsigned int cflag;
 407        unsigned int old_cflag;
 408        unsigned int control_state, new_state;
 409
 410        gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
 411
 412        mutex_lock(&cs->mutex);
 413
 414        if (!cs->connected) {
 415                gig_dbg(DEBUG_IF, "not connected");
 416                goto out;
 417        }
 418
 419        iflag = tty->termios.c_iflag;
 420        cflag = tty->termios.c_cflag;
 421        old_cflag = old ? old->c_cflag : cflag;
 422        gig_dbg(DEBUG_IF, "%u: iflag %x cflag %x old %x",
 423                cs->minor_index, iflag, cflag, old_cflag);
 424
 425        /* get a local copy of the current port settings */
 426        control_state = cs->control_state;
 427
 428        /*
 429         * Update baud rate.
 430         * Do not attempt to cache old rates and skip settings,
 431         * disconnects screw such tricks up completely.
 432         * Premature optimization is the root of all evil.
 433         */
 434
 435        /* reassert DTR and (maybe) RTS on transition from B0 */
 436        if ((old_cflag & CBAUD) == B0) {
 437                new_state = control_state | TIOCM_DTR;
 438                /* don't set RTS if using hardware flow control */
 439                if (!(old_cflag & CRTSCTS))
 440                        new_state |= TIOCM_RTS;
 441                gig_dbg(DEBUG_IF, "%u: from B0 - set DTR%s",
 442                        cs->minor_index,
 443                        (new_state & TIOCM_RTS) ? " only" : "/RTS");
 444                cs->ops->set_modem_ctrl(cs, control_state, new_state);
 445                control_state = new_state;
 446        }
 447
 448        cs->ops->baud_rate(cs, cflag & CBAUD);
 449
 450        if ((cflag & CBAUD) == B0) {
 451                /* Drop RTS and DTR */
 452                gig_dbg(DEBUG_IF, "%u: to B0 - drop DTR/RTS", cs->minor_index);
 453                new_state = control_state & ~(TIOCM_DTR | TIOCM_RTS);
 454                cs->ops->set_modem_ctrl(cs, control_state, new_state);
 455                control_state = new_state;
 456        }
 457
 458        /*
 459         * Update line control register (LCR)
 460         */
 461
 462        cs->ops->set_line_ctrl(cs, cflag);
 463
 464        /* save off the modified port settings */
 465        cs->control_state = control_state;
 466
 467out:
 468        mutex_unlock(&cs->mutex);
 469}
 470
 471static const struct tty_operations if_ops = {
 472        .open =                 if_open,
 473        .close =                if_close,
 474        .ioctl =                if_ioctl,
 475        .write =                if_write,
 476        .write_room =           if_write_room,
 477        .chars_in_buffer =      if_chars_in_buffer,
 478        .set_termios =          if_set_termios,
 479        .throttle =             if_throttle,
 480        .unthrottle =           if_unthrottle,
 481        .tiocmget =             if_tiocmget,
 482        .tiocmset =             if_tiocmset,
 483};
 484
 485
 486/* wakeup tasklet for the write operation */
 487static void if_wake(unsigned long data)
 488{
 489        struct cardstate *cs = (struct cardstate *)data;
 490
 491        tty_port_tty_wakeup(&cs->port);
 492}
 493
 494/*** interface to common ***/
 495
 496void gigaset_if_init(struct cardstate *cs)
 497{
 498        struct gigaset_driver *drv;
 499
 500        drv = cs->driver;
 501        if (!drv->have_tty)
 502                return;
 503
 504        tasklet_init(&cs->if_wake_tasklet, if_wake, (unsigned long) cs);
 505
 506        mutex_lock(&cs->mutex);
 507        cs->tty_dev = tty_port_register_device(&cs->port, drv->tty,
 508                        cs->minor_index, NULL);
 509
 510        if (!IS_ERR(cs->tty_dev))
 511                dev_set_drvdata(cs->tty_dev, cs);
 512        else {
 513                pr_warning("could not register device to the tty subsystem\n");
 514                cs->tty_dev = NULL;
 515        }
 516        mutex_unlock(&cs->mutex);
 517}
 518
 519void gigaset_if_free(struct cardstate *cs)
 520{
 521        struct gigaset_driver *drv;
 522
 523        drv = cs->driver;
 524        if (!drv->have_tty)
 525                return;
 526
 527        tasklet_disable(&cs->if_wake_tasklet);
 528        tasklet_kill(&cs->if_wake_tasklet);
 529        cs->tty_dev = NULL;
 530        tty_unregister_device(drv->tty, cs->minor_index);
 531}
 532
 533/**
 534 * gigaset_if_receive() - pass a received block of data to the tty device
 535 * @cs:         device descriptor structure.
 536 * @buffer:     received data.
 537 * @len:        number of bytes received.
 538 *
 539 * Called by asyncdata/isocdata if a block of data received from the
 540 * device must be sent to userspace through the ttyG* device.
 541 */
 542void gigaset_if_receive(struct cardstate *cs,
 543                        unsigned char *buffer, size_t len)
 544{
 545        tty_insert_flip_string(&cs->port, buffer, len);
 546        tty_flip_buffer_push(&cs->port);
 547}
 548EXPORT_SYMBOL_GPL(gigaset_if_receive);
 549
 550/* gigaset_if_initdriver
 551 * Initialize tty interface.
 552 * parameters:
 553 *      drv             Driver
 554 *      procname        Name of the driver (e.g. for /proc/tty/drivers)
 555 *      devname         Name of the device files (prefix without minor number)
 556 */
 557void gigaset_if_initdriver(struct gigaset_driver *drv, const char *procname,
 558                           const char *devname)
 559{
 560        int ret;
 561        struct tty_driver *tty;
 562
 563        drv->have_tty = 0;
 564
 565        drv->tty = tty = alloc_tty_driver(drv->minors);
 566        if (tty == NULL)
 567                goto enomem;
 568
 569        tty->type =             TTY_DRIVER_TYPE_SERIAL;
 570        tty->subtype =          SERIAL_TYPE_NORMAL;
 571        tty->flags =            TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
 572
 573        tty->driver_name =      procname;
 574        tty->name =             devname;
 575        tty->minor_start =      drv->minor;
 576
 577        tty->init_termios          = tty_std_termios;
 578        tty->init_termios.c_cflag  = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
 579        tty_set_operations(tty, &if_ops);
 580
 581        ret = tty_register_driver(tty);
 582        if (ret < 0) {
 583                pr_err("error %d registering tty driver\n", ret);
 584                goto error;
 585        }
 586        gig_dbg(DEBUG_IF, "tty driver initialized");
 587        drv->have_tty = 1;
 588        return;
 589
 590enomem:
 591        pr_err("out of memory\n");
 592error:
 593        if (drv->tty)
 594                put_tty_driver(drv->tty);
 595}
 596
 597void gigaset_if_freedriver(struct gigaset_driver *drv)
 598{
 599        if (!drv->have_tty)
 600                return;
 601
 602        drv->have_tty = 0;
 603        tty_unregister_driver(drv->tty);
 604        put_tty_driver(drv->tty);
 605}
 606