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/* stolen from usbserial and some other tty drivers */
 116
 117static int  if_open(struct tty_struct *tty, struct file *filp);
 118static void if_close(struct tty_struct *tty, struct file *filp);
 119static int  if_ioctl(struct tty_struct *tty,
 120                     unsigned int cmd, unsigned long arg);
 121static int  if_write_room(struct tty_struct *tty);
 122static int  if_chars_in_buffer(struct tty_struct *tty);
 123static void if_throttle(struct tty_struct *tty);
 124static void if_unthrottle(struct tty_struct *tty);
 125static void if_set_termios(struct tty_struct *tty, struct ktermios *old);
 126static int  if_tiocmget(struct tty_struct *tty);
 127static int  if_tiocmset(struct tty_struct *tty,
 128                        unsigned int set, unsigned int clear);
 129static int  if_write(struct tty_struct *tty,
 130                     const unsigned char *buf, int count);
 131
 132static const struct tty_operations if_ops = {
 133        .open =                 if_open,
 134        .close =                if_close,
 135        .ioctl =                if_ioctl,
 136        .write =                if_write,
 137        .write_room =           if_write_room,
 138        .chars_in_buffer =      if_chars_in_buffer,
 139        .set_termios =          if_set_termios,
 140        .throttle =             if_throttle,
 141        .unthrottle =           if_unthrottle,
 142        .tiocmget =             if_tiocmget,
 143        .tiocmset =             if_tiocmset,
 144};
 145
 146static int if_open(struct tty_struct *tty, struct file *filp)
 147{
 148        struct cardstate *cs;
 149
 150        gig_dbg(DEBUG_IF, "%d+%d: %s()",
 151                tty->driver->minor_start, tty->index, __func__);
 152
 153        cs = gigaset_get_cs_by_tty(tty);
 154        if (!cs || !try_module_get(cs->driver->owner))
 155                return -ENODEV;
 156
 157        if (mutex_lock_interruptible(&cs->mutex)) {
 158                module_put(cs->driver->owner);
 159                return -ERESTARTSYS;
 160        }
 161        tty->driver_data = cs;
 162
 163        ++cs->port.count;
 164
 165        if (cs->port.count == 1) {
 166                tty_port_tty_set(&cs->port, tty);
 167                tty->low_latency = 1;
 168        }
 169
 170        mutex_unlock(&cs->mutex);
 171        return 0;
 172}
 173
 174static void if_close(struct tty_struct *tty, struct file *filp)
 175{
 176        struct cardstate *cs = tty->driver_data;
 177
 178        if (!cs) { /* happens if we didn't find cs in open */
 179                gig_dbg(DEBUG_IF, "%s: no cardstate", __func__);
 180                return;
 181        }
 182
 183        gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
 184
 185        mutex_lock(&cs->mutex);
 186
 187        if (!cs->connected)
 188                gig_dbg(DEBUG_IF, "not connected");     /* nothing to do */
 189        else if (!cs->port.count)
 190                dev_warn(cs->dev, "%s: device not opened\n", __func__);
 191        else if (!--cs->port.count)
 192                tty_port_tty_set(&cs->port, NULL);
 193
 194        mutex_unlock(&cs->mutex);
 195
 196        module_put(cs->driver->owner);
 197}
 198
 199static int if_ioctl(struct tty_struct *tty,
 200                    unsigned int cmd, unsigned long arg)
 201{
 202        struct cardstate *cs = tty->driver_data;
 203        int retval = -ENODEV;
 204        int int_arg;
 205        unsigned char buf[6];
 206        unsigned version[4];
 207
 208        gig_dbg(DEBUG_IF, "%u: %s(0x%x)", cs->minor_index, __func__, cmd);
 209
 210        if (mutex_lock_interruptible(&cs->mutex))
 211                return -ERESTARTSYS;
 212
 213        if (!cs->connected) {
 214                gig_dbg(DEBUG_IF, "not connected");
 215                retval = -ENODEV;
 216        } else {
 217                retval = 0;
 218                switch (cmd) {
 219                case GIGASET_REDIR:
 220                        retval = get_user(int_arg, (int __user *) arg);
 221                        if (retval >= 0)
 222                                retval = if_lock(cs, &int_arg);
 223                        if (retval >= 0)
 224                                retval = put_user(int_arg, (int __user *) arg);
 225                        break;
 226                case GIGASET_CONFIG:
 227                        retval = get_user(int_arg, (int __user *) arg);
 228                        if (retval >= 0)
 229                                retval = if_config(cs, &int_arg);
 230                        if (retval >= 0)
 231                                retval = put_user(int_arg, (int __user *) arg);
 232                        break;
 233                case GIGASET_BRKCHARS:
 234                        retval = copy_from_user(&buf,
 235                                                (const unsigned char __user *) arg, 6)
 236                                ? -EFAULT : 0;
 237                        if (retval >= 0) {
 238                                gigaset_dbg_buffer(DEBUG_IF, "GIGASET_BRKCHARS",
 239                                                   6, (const unsigned char *) arg);
 240                                retval = cs->ops->brkchars(cs, buf);
 241                        }
 242                        break;
 243                case GIGASET_VERSION:
 244                        retval = copy_from_user(version,
 245                                                (unsigned __user *) arg, sizeof version)
 246                                ? -EFAULT : 0;
 247                        if (retval >= 0)
 248                                retval = if_version(cs, version);
 249                        if (retval >= 0)
 250                                retval = copy_to_user((unsigned __user *) arg,
 251                                                      version, sizeof version)
 252                                        ? -EFAULT : 0;
 253                        break;
 254                default:
 255                        gig_dbg(DEBUG_IF, "%s: arg not supported - 0x%04x",
 256                                __func__, cmd);
 257                        retval = -ENOIOCTLCMD;
 258                }
 259        }
 260
 261        mutex_unlock(&cs->mutex);
 262
 263        return retval;
 264}
 265
 266static int if_tiocmget(struct tty_struct *tty)
 267{
 268        struct cardstate *cs = tty->driver_data;
 269        int retval;
 270
 271        gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
 272
 273        if (mutex_lock_interruptible(&cs->mutex))
 274                return -ERESTARTSYS;
 275
 276        retval = cs->control_state & (TIOCM_RTS | TIOCM_DTR);
 277
 278        mutex_unlock(&cs->mutex);
 279
 280        return retval;
 281}
 282
 283static int if_tiocmset(struct tty_struct *tty,
 284                       unsigned int set, unsigned int clear)
 285{
 286        struct cardstate *cs = tty->driver_data;
 287        int retval;
 288        unsigned mc;
 289
 290        gig_dbg(DEBUG_IF, "%u: %s(0x%x, 0x%x)",
 291                cs->minor_index, __func__, set, clear);
 292
 293        if (mutex_lock_interruptible(&cs->mutex))
 294                return -ERESTARTSYS;
 295
 296        if (!cs->connected) {
 297                gig_dbg(DEBUG_IF, "not connected");
 298                retval = -ENODEV;
 299        } else {
 300                mc = (cs->control_state | set) & ~clear & (TIOCM_RTS | TIOCM_DTR);
 301                retval = cs->ops->set_modem_ctrl(cs, cs->control_state, mc);
 302                cs->control_state = mc;
 303        }
 304
 305        mutex_unlock(&cs->mutex);
 306
 307        return retval;
 308}
 309
 310static int if_write(struct tty_struct *tty, const unsigned char *buf, int count)
 311{
 312        struct cardstate *cs = tty->driver_data;
 313        struct cmdbuf_t *cb;
 314        int retval;
 315
 316        gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
 317
 318        if (mutex_lock_interruptible(&cs->mutex))
 319                return -ERESTARTSYS;
 320
 321        if (!cs->connected) {
 322                gig_dbg(DEBUG_IF, "not connected");
 323                retval = -ENODEV;
 324                goto done;
 325        }
 326        if (cs->mstate != MS_LOCKED) {
 327                dev_warn(cs->dev, "can't write to unlocked device\n");
 328                retval = -EBUSY;
 329                goto done;
 330        }
 331        if (count <= 0) {
 332                /* nothing to do */
 333                retval = 0;
 334                goto done;
 335        }
 336
 337        cb = kmalloc(sizeof(struct cmdbuf_t) + count, GFP_KERNEL);
 338        if (!cb) {
 339                dev_err(cs->dev, "%s: out of memory\n", __func__);
 340                retval = -ENOMEM;
 341                goto done;
 342        }
 343
 344        memcpy(cb->buf, buf, count);
 345        cb->len = count;
 346        cb->offset = 0;
 347        cb->next = NULL;
 348        cb->wake_tasklet = &cs->if_wake_tasklet;
 349        retval = cs->ops->write_cmd(cs, cb);
 350done:
 351        mutex_unlock(&cs->mutex);
 352        return retval;
 353}
 354
 355static int if_write_room(struct tty_struct *tty)
 356{
 357        struct cardstate *cs = tty->driver_data;
 358        int retval = -ENODEV;
 359
 360        gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
 361
 362        if (mutex_lock_interruptible(&cs->mutex))
 363                return -ERESTARTSYS;
 364
 365        if (!cs->connected) {
 366                gig_dbg(DEBUG_IF, "not connected");
 367                retval = -ENODEV;
 368        } else if (cs->mstate != MS_LOCKED) {
 369                dev_warn(cs->dev, "can't write to unlocked device\n");
 370                retval = -EBUSY;
 371        } else
 372                retval = cs->ops->write_room(cs);
 373
 374        mutex_unlock(&cs->mutex);
 375
 376        return retval;
 377}
 378
 379static int if_chars_in_buffer(struct tty_struct *tty)
 380{
 381        struct cardstate *cs = tty->driver_data;
 382        int retval = 0;
 383
 384        gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
 385
 386        mutex_lock(&cs->mutex);
 387
 388        if (!cs->connected)
 389                gig_dbg(DEBUG_IF, "not connected");
 390        else if (cs->mstate != MS_LOCKED)
 391                dev_warn(cs->dev, "can't write to unlocked device\n");
 392        else
 393                retval = cs->ops->chars_in_buffer(cs);
 394
 395        mutex_unlock(&cs->mutex);
 396
 397        return retval;
 398}
 399
 400static void if_throttle(struct tty_struct *tty)
 401{
 402        struct cardstate *cs = tty->driver_data;
 403
 404        gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
 405
 406        mutex_lock(&cs->mutex);
 407
 408        if (!cs->connected)
 409                gig_dbg(DEBUG_IF, "not connected");     /* nothing to do */
 410        else
 411                gig_dbg(DEBUG_IF, "%s: not implemented\n", __func__);
 412
 413        mutex_unlock(&cs->mutex);
 414}
 415
 416static void if_unthrottle(struct tty_struct *tty)
 417{
 418        struct cardstate *cs = tty->driver_data;
 419
 420        gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
 421
 422        mutex_lock(&cs->mutex);
 423
 424        if (!cs->connected)
 425                gig_dbg(DEBUG_IF, "not connected");     /* nothing to do */
 426        else
 427                gig_dbg(DEBUG_IF, "%s: not implemented\n", __func__);
 428
 429        mutex_unlock(&cs->mutex);
 430}
 431
 432static void if_set_termios(struct tty_struct *tty, struct ktermios *old)
 433{
 434        struct cardstate *cs = tty->driver_data;
 435        unsigned int iflag;
 436        unsigned int cflag;
 437        unsigned int old_cflag;
 438        unsigned int control_state, new_state;
 439
 440        gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
 441
 442        mutex_lock(&cs->mutex);
 443
 444        if (!cs->connected) {
 445                gig_dbg(DEBUG_IF, "not connected");
 446                goto out;
 447        }
 448
 449        iflag = tty->termios->c_iflag;
 450        cflag = tty->termios->c_cflag;
 451        old_cflag = old ? old->c_cflag : cflag;
 452        gig_dbg(DEBUG_IF, "%u: iflag %x cflag %x old %x",
 453                cs->minor_index, iflag, cflag, old_cflag);
 454
 455        /* get a local copy of the current port settings */
 456        control_state = cs->control_state;
 457
 458        /*
 459         * Update baud rate.
 460         * Do not attempt to cache old rates and skip settings,
 461         * disconnects screw such tricks up completely.
 462         * Premature optimization is the root of all evil.
 463         */
 464
 465        /* reassert DTR and (maybe) RTS on transition from B0 */
 466        if ((old_cflag & CBAUD) == B0) {
 467                new_state = control_state | TIOCM_DTR;
 468                /* don't set RTS if using hardware flow control */
 469                if (!(old_cflag & CRTSCTS))
 470                        new_state |= TIOCM_RTS;
 471                gig_dbg(DEBUG_IF, "%u: from B0 - set DTR%s",
 472                        cs->minor_index,
 473                        (new_state & TIOCM_RTS) ? " only" : "/RTS");
 474                cs->ops->set_modem_ctrl(cs, control_state, new_state);
 475                control_state = new_state;
 476        }
 477
 478        cs->ops->baud_rate(cs, cflag & CBAUD);
 479
 480        if ((cflag & CBAUD) == B0) {
 481                /* Drop RTS and DTR */
 482                gig_dbg(DEBUG_IF, "%u: to B0 - drop DTR/RTS", cs->minor_index);
 483                new_state = control_state & ~(TIOCM_DTR | TIOCM_RTS);
 484                cs->ops->set_modem_ctrl(cs, control_state, new_state);
 485                control_state = new_state;
 486        }
 487
 488        /*
 489         * Update line control register (LCR)
 490         */
 491
 492        cs->ops->set_line_ctrl(cs, cflag);
 493
 494        /* save off the modified port settings */
 495        cs->control_state = control_state;
 496
 497out:
 498        mutex_unlock(&cs->mutex);
 499}
 500
 501
 502/* wakeup tasklet for the write operation */
 503static void if_wake(unsigned long data)
 504{
 505        struct cardstate *cs = (struct cardstate *)data;
 506        struct tty_struct *tty = tty_port_tty_get(&cs->port);
 507
 508        if (tty) {
 509                tty_wakeup(tty);
 510                tty_kref_put(tty);
 511        }
 512}
 513
 514/*** interface to common ***/
 515
 516void gigaset_if_init(struct cardstate *cs)
 517{
 518        struct gigaset_driver *drv;
 519
 520        drv = cs->driver;
 521        if (!drv->have_tty)
 522                return;
 523
 524        tasklet_init(&cs->if_wake_tasklet, if_wake, (unsigned long) cs);
 525
 526        mutex_lock(&cs->mutex);
 527        cs->tty_dev = tty_register_device(drv->tty, cs->minor_index, NULL);
 528
 529        if (!IS_ERR(cs->tty_dev))
 530                dev_set_drvdata(cs->tty_dev, cs);
 531        else {
 532                pr_warning("could not register device to the tty subsystem\n");
 533                cs->tty_dev = NULL;
 534        }
 535        mutex_unlock(&cs->mutex);
 536}
 537
 538void gigaset_if_free(struct cardstate *cs)
 539{
 540        struct gigaset_driver *drv;
 541
 542        drv = cs->driver;
 543        if (!drv->have_tty)
 544                return;
 545
 546        tasklet_disable(&cs->if_wake_tasklet);
 547        tasklet_kill(&cs->if_wake_tasklet);
 548        cs->tty_dev = NULL;
 549        tty_unregister_device(drv->tty, cs->minor_index);
 550}
 551
 552/**
 553 * gigaset_if_receive() - pass a received block of data to the tty device
 554 * @cs:         device descriptor structure.
 555 * @buffer:     received data.
 556 * @len:        number of bytes received.
 557 *
 558 * Called by asyncdata/isocdata if a block of data received from the
 559 * device must be sent to userspace through the ttyG* device.
 560 */
 561void gigaset_if_receive(struct cardstate *cs,
 562                        unsigned char *buffer, size_t len)
 563{
 564        struct tty_struct *tty = tty_port_tty_get(&cs->port);
 565
 566        if (tty == NULL) {
 567                gig_dbg(DEBUG_IF, "receive on closed device");
 568                return;
 569        }
 570
 571        tty_insert_flip_string(tty, buffer, len);
 572        tty_flip_buffer_push(tty);
 573        tty_kref_put(tty);
 574}
 575EXPORT_SYMBOL_GPL(gigaset_if_receive);
 576
 577/* gigaset_if_initdriver
 578 * Initialize tty interface.
 579 * parameters:
 580 *      drv             Driver
 581 *      procname        Name of the driver (e.g. for /proc/tty/drivers)
 582 *      devname         Name of the device files (prefix without minor number)
 583 */
 584void gigaset_if_initdriver(struct gigaset_driver *drv, const char *procname,
 585                           const char *devname)
 586{
 587        int ret;
 588        struct tty_driver *tty;
 589
 590        drv->have_tty = 0;
 591
 592        drv->tty = tty = alloc_tty_driver(drv->minors);
 593        if (tty == NULL)
 594                goto enomem;
 595
 596        tty->type =             TTY_DRIVER_TYPE_SERIAL;
 597        tty->subtype =          SERIAL_TYPE_NORMAL;
 598        tty->flags =            TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
 599
 600        tty->driver_name =      procname;
 601        tty->name =             devname;
 602        tty->minor_start =      drv->minor;
 603
 604        tty->init_termios          = tty_std_termios;
 605        tty->init_termios.c_cflag  = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
 606        tty_set_operations(tty, &if_ops);
 607
 608        ret = tty_register_driver(tty);
 609        if (ret < 0) {
 610                pr_err("error %d registering tty driver\n", ret);
 611                goto error;
 612        }
 613        gig_dbg(DEBUG_IF, "tty driver initialized");
 614        drv->have_tty = 1;
 615        return;
 616
 617enomem:
 618        pr_err("out of memory\n");
 619error:
 620        if (drv->tty)
 621                put_tty_driver(drv->tty);
 622}
 623
 624void gigaset_if_freedriver(struct gigaset_driver *drv)
 625{
 626        if (!drv->have_tty)
 627                return;
 628
 629        drv->have_tty = 0;
 630        tty_unregister_driver(drv->tty);
 631        put_tty_driver(drv->tty);
 632}
 633