1/*********************************************************************
2 *
3 * Filename:      ircomm_tty_ioctl.c
4 * Version:
5 * Description:
6 * Status:        Experimental.
7 * Author:        Dag Brattli <dagb@cs.uit.no>
8 * Created at:    Thu Jun 10 14:39:09 1999
9 * Modified at:   Wed Jan  5 14:45:43 2000
10 * Modified by:   Dag Brattli <dagb@cs.uit.no>
11 *
12 *     Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved.
13 *
14 *     This program is free software; you can redistribute it and/or
15 *     modify it under the terms of the GNU General Public License as
16 *     published by the Free Software Foundation; either version 2 of
17 *     the License, or (at your option) any later version.
18 *
19 *     This program is distributed in the hope that it will be useful,
20 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
21 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 *     GNU General Public License for more details.
23 *
24 *     You should have received a copy of the GNU General Public License
25 *     along with this program; if not, write to the Free Software
26 *     Foundation, Inc., 59 Temple Place, Suite 330, Boston,
27 *     MA 02111-1307 USA
28 *
29 ********************************************************************/
30
31#include <linux/init.h>
32#include <linux/fs.h>
33#include <linux/termios.h>
34#include <linux/tty.h>
35#include <linux/serial.h>
36
37#include <asm/uaccess.h>
38
39#include <net/irda/irda.h>
40#include <net/irda/irmod.h>
41
42#include <net/irda/ircomm_core.h>
43#include <net/irda/ircomm_param.h>
44#include <net/irda/ircomm_tty_attach.h>
45#include <net/irda/ircomm_tty.h>
46
47#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
48
49/*
50 * Function ircomm_tty_change_speed (driver)
51 *
52 *    Change speed of the driver. If the remote device is a DCE, then this
53 *    should make it change the speed of its serial port
54 */
55static void ircomm_tty_change_speed(struct ircomm_tty_cb *self)
56{
57	unsigned cflag, cval;
58	int baud;
59
60	IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
61
62	if (!self->tty || !self->tty->termios || !self->ircomm)
63		return;
64
65	cflag = self->tty->termios->c_cflag;
66
67	/*  byte size and parity */
68	switch (cflag & CSIZE) {
69	case CS5: cval = IRCOMM_WSIZE_5; break;
70	case CS6: cval = IRCOMM_WSIZE_6; break;
71	case CS7: cval = IRCOMM_WSIZE_7; break;
72	case CS8: cval = IRCOMM_WSIZE_8; break;
73	default:  cval = IRCOMM_WSIZE_5; break;
74	}
75	if (cflag & CSTOPB)
76		cval |= IRCOMM_2_STOP_BIT;
77
78	if (cflag & PARENB)
79		cval |= IRCOMM_PARITY_ENABLE;
80	if (!(cflag & PARODD))
81		cval |= IRCOMM_PARITY_EVEN;
82
83	/* Determine divisor based on baud rate */
84	baud = tty_get_baud_rate(self->tty);
85	if (!baud)
86		baud = 9600;	/* B0 transition handled in rs_set_termios */
87
88	self->settings.data_rate = baud;
89	ircomm_param_request(self, IRCOMM_DATA_RATE, FALSE);
90
91	/* CTS flow control flag and modem status interrupts */
92	if (cflag & CRTSCTS) {
93		self->flags |= ASYNC_CTS_FLOW;
94		self->settings.flow_control |= IRCOMM_RTS_CTS_IN;
95		/* This got me. Bummer. Jean II */
96		if (self->service_type == IRCOMM_3_WIRE_RAW)
97			IRDA_WARNING("%s(), enabling RTS/CTS on link that doesn't support it (3-wire-raw)\n", __FUNCTION__);
98	} else {
99		self->flags &= ~ASYNC_CTS_FLOW;
100		self->settings.flow_control &= ~IRCOMM_RTS_CTS_IN;
101	}
102	if (cflag & CLOCAL)
103		self->flags &= ~ASYNC_CHECK_CD;
104	else
105		self->flags |= ASYNC_CHECK_CD;
106	self->settings.data_format = cval;
107
108	ircomm_param_request(self, IRCOMM_DATA_FORMAT, FALSE);
109	ircomm_param_request(self, IRCOMM_FLOW_CONTROL, TRUE);
110}
111
112/*
113 * Function ircomm_tty_set_termios (tty, old_termios)
114 *
115 *    This routine allows the tty driver to be notified when device's
116 *    termios settings have changed.  Note that a well-designed tty driver
117 *    should be prepared to accept the case where old == NULL, and try to
118 *    do something rational.
119 */
120void ircomm_tty_set_termios(struct tty_struct *tty,
121			    struct ktermios *old_termios)
122{
123	struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
124	unsigned int cflag = tty->termios->c_cflag;
125
126	IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
127
128	if ((cflag == old_termios->c_cflag) &&
129	    (RELEVANT_IFLAG(tty->termios->c_iflag) ==
130	     RELEVANT_IFLAG(old_termios->c_iflag)))
131	{
132		return;
133	}
134
135	ircomm_tty_change_speed(self);
136
137	/* Handle transition to B0 status */
138	if ((old_termios->c_cflag & CBAUD) &&
139	    !(cflag & CBAUD)) {
140		self->settings.dte &= ~(IRCOMM_DTR|IRCOMM_RTS);
141		ircomm_param_request(self, IRCOMM_DTE, TRUE);
142	}
143
144	/* Handle transition away from B0 status */
145	if (!(old_termios->c_cflag & CBAUD) &&
146	    (cflag & CBAUD)) {
147		self->settings.dte |= IRCOMM_DTR;
148		if (!(tty->termios->c_cflag & CRTSCTS) ||
149		    !test_bit(TTY_THROTTLED, &tty->flags)) {
150			self->settings.dte |= IRCOMM_RTS;
151		}
152		ircomm_param_request(self, IRCOMM_DTE, TRUE);
153	}
154
155	/* Handle turning off CRTSCTS */
156	if ((old_termios->c_cflag & CRTSCTS) &&
157	    !(tty->termios->c_cflag & CRTSCTS))
158	{
159		tty->hw_stopped = 0;
160		ircomm_tty_start(tty);
161	}
162}
163
164/*
165 * Function ircomm_tty_tiocmget (tty, file)
166 *
167 *
168 *
169 */
170int ircomm_tty_tiocmget(struct tty_struct *tty, struct file *file)
171{
172	struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
173	unsigned int result;
174
175	IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
176
177	if (tty->flags & (1 << TTY_IO_ERROR))
178		return -EIO;
179
180	result =  ((self->settings.dte & IRCOMM_RTS) ? TIOCM_RTS : 0)
181		| ((self->settings.dte & IRCOMM_DTR) ? TIOCM_DTR : 0)
182		| ((self->settings.dce & IRCOMM_CD)  ? TIOCM_CAR : 0)
183		| ((self->settings.dce & IRCOMM_RI)  ? TIOCM_RNG : 0)
184		| ((self->settings.dce & IRCOMM_DSR) ? TIOCM_DSR : 0)
185		| ((self->settings.dce & IRCOMM_CTS) ? TIOCM_CTS : 0);
186	return result;
187}
188
189/*
190 * Function ircomm_tty_tiocmset (tty, file, set, clear)
191 *
192 *
193 *
194 */
195int ircomm_tty_tiocmset(struct tty_struct *tty, struct file *file,
196			unsigned int set, unsigned int clear)
197{
198	struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
199
200	IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
201
202	if (tty->flags & (1 << TTY_IO_ERROR))
203		return -EIO;
204
205	IRDA_ASSERT(self != NULL, return -1;);
206	IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
207
208	if (set & TIOCM_RTS)
209		self->settings.dte |= IRCOMM_RTS;
210	if (set & TIOCM_DTR)
211		self->settings.dte |= IRCOMM_DTR;
212
213	if (clear & TIOCM_RTS)
214		self->settings.dte &= ~IRCOMM_RTS;
215	if (clear & TIOCM_DTR)
216		self->settings.dte &= ~IRCOMM_DTR;
217
218	if ((set|clear) & TIOCM_RTS)
219		self->settings.dte |= IRCOMM_DELTA_RTS;
220	if ((set|clear) & TIOCM_DTR)
221		self->settings.dte |= IRCOMM_DELTA_DTR;
222
223	ircomm_param_request(self, IRCOMM_DTE, TRUE);
224
225	return 0;
226}
227
228/*
229 * Function get_serial_info (driver, retinfo)
230 *
231 *
232 *
233 */
234static int ircomm_tty_get_serial_info(struct ircomm_tty_cb *self,
235				      struct serial_struct __user *retinfo)
236{
237	struct serial_struct info;
238
239	if (!retinfo)
240		return -EFAULT;
241
242	IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
243
244	memset(&info, 0, sizeof(info));
245	info.line = self->line;
246	info.flags = self->flags;
247	info.baud_base = self->settings.data_rate;
248	info.close_delay = self->close_delay;
249	info.closing_wait = self->closing_wait;
250
251	/* For compatibility  */
252	info.type = PORT_16550A;
253	info.port = 0;
254	info.irq = 0;
255	info.xmit_fifo_size = 0;
256	info.hub6 = 0;
257	info.custom_divisor = 0;
258
259	if (copy_to_user(retinfo, &info, sizeof(*retinfo)))
260		return -EFAULT;
261
262	return 0;
263}
264
265/*
266 * Function set_serial_info (driver, new_info)
267 *
268 *
269 *
270 */
271static int ircomm_tty_set_serial_info(struct ircomm_tty_cb *self,
272				      struct serial_struct __user *new_info)
273{
274	return 0;
275}
276
277/*
278 * Function ircomm_tty_ioctl (tty, file, cmd, arg)
279 *
280 *
281 *
282 */
283int ircomm_tty_ioctl(struct tty_struct *tty, struct file *file,
284		     unsigned int cmd, unsigned long arg)
285{
286	struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
287	int ret = 0;
288
289	IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
290
291	if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
292	    (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) &&
293	    (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) {
294		if (tty->flags & (1 << TTY_IO_ERROR))
295		    return -EIO;
296	}
297
298	switch (cmd) {
299	case TIOCGSERIAL:
300		ret = ircomm_tty_get_serial_info(self, (struct serial_struct __user *) arg);
301		break;
302	case TIOCSSERIAL:
303		ret = ircomm_tty_set_serial_info(self, (struct serial_struct __user *) arg);
304		break;
305	case TIOCMIWAIT:
306		IRDA_DEBUG(0, "(), TIOCMIWAIT, not impl!\n");
307		break;
308
309	case TIOCGICOUNT:
310		IRDA_DEBUG(0, "%s(), TIOCGICOUNT not impl!\n", __FUNCTION__ );
311		return 0;
312	default:
313		ret = -ENOIOCTLCMD;  /* ioctls which we must ignore */
314	}
315	return ret;
316}
317