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