Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
interface.c
Go to the documentation of this file.
1 /*
2  * interface to user space for the gigaset driver
3  *
4  * Copyright (c) 2004 by Hansjoerg Lipp <[email protected]>
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 
21 static 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);
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 
60 static 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 
95 static 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 
117 static int if_open(struct tty_struct *tty, struct file *filp);
118 static void if_close(struct tty_struct *tty, struct file *filp);
119 static int if_ioctl(struct tty_struct *tty,
120  unsigned int cmd, unsigned long arg);
121 static int if_write_room(struct tty_struct *tty);
122 static int if_chars_in_buffer(struct tty_struct *tty);
123 static void if_throttle(struct tty_struct *tty);
124 static void if_unthrottle(struct tty_struct *tty);
125 static void if_set_termios(struct tty_struct *tty, struct ktermios *old);
126 static int if_tiocmget(struct tty_struct *tty);
127 static int if_tiocmset(struct tty_struct *tty,
128  unsigned int set, unsigned int clear);
129 static int if_write(struct tty_struct *tty,
130  const unsigned char *buf, int count);
131 
132 static 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 
146 static 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 
174 static 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 
199 static 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 
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 
266 static 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 
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 
283 static 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 
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 
310 static 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 
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);
350 done:
351  mutex_unlock(&cs->mutex);
352  return retval;
353 }
354 
355 static 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 
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 
379 static 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 
400 static 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 
416 static 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 
432 static 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 */
496 
497 out:
498  mutex_unlock(&cs->mutex);
499 }
500 
501 
502 /* wakeup tasklet for the write operation */
503 static 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 
516 void 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_port_register_device(&cs->port, drv->tty,
528  cs->minor_index, NULL);
529 
530  if (!IS_ERR(cs->tty_dev))
531  dev_set_drvdata(cs->tty_dev, cs);
532  else {
533  pr_warning("could not register device to the tty subsystem\n");
534  cs->tty_dev = NULL;
535  }
536  mutex_unlock(&cs->mutex);
537 }
538 
539 void gigaset_if_free(struct cardstate *cs)
540 {
541  struct gigaset_driver *drv;
542 
543  drv = cs->driver;
544  if (!drv->have_tty)
545  return;
546 
547  tasklet_disable(&cs->if_wake_tasklet);
549  cs->tty_dev = NULL;
551 }
552 
563  unsigned char *buffer, size_t len)
564 {
565  struct tty_struct *tty = tty_port_tty_get(&cs->port);
566 
567  if (tty == NULL) {
568  gig_dbg(DEBUG_IF, "receive on closed device");
569  return;
570  }
571 
572  tty_insert_flip_string(tty, buffer, len);
574  tty_kref_put(tty);
575 }
577 
578 /* gigaset_if_initdriver
579  * Initialize tty interface.
580  * parameters:
581  * drv Driver
582  * procname Name of the driver (e.g. for /proc/tty/drivers)
583  * devname Name of the device files (prefix without minor number)
584  */
585 void gigaset_if_initdriver(struct gigaset_driver *drv, const char *procname,
586  const char *devname)
587 {
588  int ret;
589  struct tty_driver *tty;
590 
591  drv->have_tty = 0;
592 
593  drv->tty = tty = alloc_tty_driver(drv->minors);
594  if (tty == NULL)
595  goto enomem;
596 
600 
601  tty->driver_name = procname;
602  tty->name = devname;
603  tty->minor_start = drv->minor;
604 
606  tty->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
607  tty_set_operations(tty, &if_ops);
608 
609  ret = tty_register_driver(tty);
610  if (ret < 0) {
611  pr_err("error %d registering tty driver\n", ret);
612  goto error;
613  }
614  gig_dbg(DEBUG_IF, "tty driver initialized");
615  drv->have_tty = 1;
616  return;
617 
618 enomem:
619  pr_err("out of memory\n");
620 error:
621  if (drv->tty)
622  put_tty_driver(drv->tty);
623 }
624 
626 {
627  if (!drv->have_tty)
628  return;
629 
630  drv->have_tty = 0;
632  put_tty_driver(drv->tty);
633 }