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