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