interface.c revision 48a7466f4dd0104d87a6d8dd0f25027be89c8453
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 printk(KERN_DEBUG "%s: no cardstate\n", __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