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
116static 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
144static 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
169static 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
236static 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
253static 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
280static 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);
320done:
321	mutex_unlock(&cs->mutex);
322	return retval;
323}
324
325static 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
349static 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
370static 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
386static 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
402static 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
467out:
468	mutex_unlock(&cs->mutex);
469}
470
471static 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 */
487static 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
496void 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
519void 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 */
542void 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}
548EXPORT_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 */
557void 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
590enomem:
591	pr_err("out of memory\n");
592error:
593	if (drv->tty)
594		put_tty_driver(drv->tty);
595}
596
597void 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