1/*	$NetBSD: linux_termios.c,v 1.39 2021/11/23 17:54:08 pho Exp $	*/
2
3/*-
4 * Copyright (c) 1995, 1998, 2008 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Frank van der Linden and Eric Haszlakiewicz.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33__KERNEL_RCSID(0, "$NetBSD: linux_termios.c,v 1.39 2021/11/23 17:54:08 pho Exp $");
34
35#if defined(_KERNEL_OPT)
36#include "opt_ptm.h"
37#endif
38
39#include <sys/param.h>
40#include <sys/proc.h>
41#include <sys/systm.h>
42#include <sys/file.h>
43#include <sys/filedesc.h>
44#include <sys/ioctl.h>
45#include <sys/mount.h>
46#include <sys/termios.h>
47#include <sys/kernel.h>
48
49#include <sys/syscallargs.h>
50
51#include <compat/linux/common/linux_types.h>
52#include <compat/linux/common/linux_ioctl.h>
53#include <compat/linux/common/linux_signal.h>
54#include <compat/linux/common/linux_util.h>
55#include <compat/linux/common/linux_termios.h>
56#include <compat/linux/common/linux_ipc.h>
57#include <compat/linux/common/linux_sem.h>
58
59#include <compat/linux/linux_syscallargs.h>
60
61#ifdef DEBUG_LINUX
62#define DPRINTF(a)	uprintf a
63#else
64#define DPRINTF(a)
65#endif
66
67int
68linux_ioctl_termios(struct lwp *l, const struct linux_sys_ioctl_args *uap, register_t *retval)
69{
70	/* {
71		syscallarg(int) fd;
72		syscallarg(u_long) com;
73		syscallarg(void *) data;
74	} */
75	file_t *fp;
76	u_long com;
77	struct linux_termio tmplt;
78	struct linux_termios tmplts;
79	struct termios tmpbts;
80	int idat;
81	struct sys_ioctl_args ia;
82	int error;
83	char tioclinux;
84	int (*bsdioctl)(file_t *, u_long, void *);
85
86	if ((fp = fd_getfile(SCARG(uap, fd))) == NULL)
87		return (EBADF);
88
89	if ((fp->f_flag & (FREAD | FWRITE)) == 0) {
90		error = EBADF;
91		goto out;
92	}
93
94	bsdioctl = fp->f_ops->fo_ioctl;
95	com = SCARG(uap, com);
96	retval[0] = 0;
97
98	switch (com) {
99	case LINUX_TCGETS:
100		error = (*bsdioctl)(fp, TIOCGETA, &tmpbts);
101		if (error)
102			goto out;
103		bsd_termios_to_linux_termios(&tmpbts, &tmplts);
104		error = copyout(&tmplts, SCARG(uap, data), sizeof tmplts);
105		goto out;
106	case LINUX_TCSETS:
107	case LINUX_TCSETSW:
108	case LINUX_TCSETSF:
109		/*
110		 * First fill in all fields, so that we keep the current
111		 * values for fields that Linux doesn't know about.
112		 */
113		error = (*bsdioctl)(fp, TIOCGETA, &tmpbts);
114		if (error)
115			goto out;
116		error = copyin(SCARG(uap, data), &tmplts, sizeof tmplts);
117		if (error)
118			goto out;
119		linux_termios_to_bsd_termios(&tmplts, &tmpbts);
120		switch (com) {
121		case LINUX_TCSETS:
122			com = TIOCSETA;
123			break;
124		case LINUX_TCSETSW:
125			com = TIOCSETAW;
126			break;
127		case LINUX_TCSETSF:
128			com = TIOCSETAF;
129			break;
130		}
131		error = (*bsdioctl)(fp, com, &tmpbts);
132		goto out;
133	case LINUX_TCGETA:
134		error = (*bsdioctl)(fp, TIOCGETA, &tmpbts);
135		if (error)
136			goto out;
137		bsd_termios_to_linux_termio(&tmpbts, &tmplt);
138		error = copyout(&tmplt, SCARG(uap, data), sizeof tmplt);
139		goto out;
140	case LINUX_TCSETA:
141	case LINUX_TCSETAW:
142	case LINUX_TCSETAF:
143		/*
144		 * First fill in all fields, so that we keep the current
145		 * values for fields that Linux doesn't know about.
146		 */
147		error = (*bsdioctl)(fp, TIOCGETA, &tmpbts);
148		if (error)
149			goto out;
150		error = copyin(SCARG(uap, data), &tmplt, sizeof tmplt);
151		if (error)
152			goto out;
153		linux_termio_to_bsd_termios(&tmplt, &tmpbts);
154		switch (com) {
155		case LINUX_TCSETA:
156			com = TIOCSETA;
157			break;
158		case LINUX_TCSETAW:
159			com = TIOCSETAW;
160			break;
161		case LINUX_TCSETAF:
162			com = TIOCSETAF;
163			break;
164		}
165		error = (*bsdioctl)(fp, com, &tmpbts);
166		goto out;
167	case LINUX_TCFLSH:
168		switch((u_long)SCARG(uap, data)) {
169		case 0:
170			idat = FREAD;
171			break;
172		case 1:
173			idat = FWRITE;
174			break;
175		case 2:
176			idat = 0;
177			break;
178		default:
179			error = EINVAL;
180			goto out;
181		}
182		error = (*bsdioctl)(fp, TIOCFLUSH, &idat);
183		goto out;
184	case LINUX_TIOCGETD:
185		error = (*bsdioctl)(fp, TIOCGETD, &idat);
186		if (error)
187			goto out;
188		switch (idat) {
189		case TTYDISC:
190			idat = LINUX_N_TTY;
191			break;
192		case SLIPDISC:
193			idat = LINUX_N_SLIP;
194			break;
195		case PPPDISC:
196			idat = LINUX_N_PPP;
197			break;
198		case STRIPDISC:
199			idat = LINUX_N_STRIP;
200			break;
201		/*
202		 * Linux does not have the tablet line discipline.
203		 */
204		case TABLDISC:
205		default:
206			idat = -1;	/* XXX What should this be? */
207			break;
208		}
209		error = copyout(&idat, SCARG(uap, data), sizeof idat);
210		goto out;
211	case LINUX_TIOCSETD:
212		error = copyin(SCARG(uap, data), &idat, sizeof idat);
213		if (error)
214			goto out;
215		switch (idat) {
216		case LINUX_N_TTY:
217			idat = TTYDISC;
218			break;
219		case LINUX_N_SLIP:
220			idat = SLIPDISC;
221			break;
222		case LINUX_N_PPP:
223			idat = PPPDISC;
224			break;
225		case LINUX_N_STRIP:
226			idat = STRIPDISC;
227			break;
228		/*
229		 * We can't handle the mouse line discipline Linux has.
230		 */
231		case LINUX_N_MOUSE:
232		case LINUX_N_AX25:
233		case LINUX_N_X25:
234		case LINUX_N_6PACK:
235		default:
236			error = EINVAL;
237			goto out;
238		}
239		error = (*bsdioctl)(fp, TIOCSETD, &idat);
240		goto out;
241	case LINUX_TIOCLINUX:
242		error = copyin(SCARG(uap, data), &tioclinux, sizeof tioclinux);
243		if (error != 0)
244			goto out;
245		switch (tioclinux) {
246		case LINUX_TIOCLINUX_KERNMSG:
247			/*
248			 * XXX needed to not fail for some things. Could
249			 * try to use TIOCCONS, but the char argument
250			 * specifies the VT #, not an fd.
251			 */
252			error = 0;
253			goto out;
254		case LINUX_TIOCLINUX_COPY:
255		case LINUX_TIOCLINUX_PASTE:
256		case LINUX_TIOCLINUX_UNBLANK:
257		case LINUX_TIOCLINUX_LOADLUT:
258		case LINUX_TIOCLINUX_READSHIFT:
259		case LINUX_TIOCLINUX_READMOUSE:
260		case LINUX_TIOCLINUX_VESABLANK:
261		case LINUX_TIOCLINUX_CURCONS:	/* could use VT_GETACTIVE */
262		default:
263			error = EINVAL;
264			goto out;
265		}
266		break;
267	case LINUX_TIOCGWINSZ:
268		SCARG(&ia, com) = TIOCGWINSZ;
269		break;
270	case LINUX_TIOCSWINSZ:
271		SCARG(&ia, com) = TIOCSWINSZ;
272		break;
273	case LINUX_TIOCGPGRP:
274		SCARG(&ia, com) = TIOCGPGRP;
275		break;
276	case LINUX_TIOCSPGRP:
277		SCARG(&ia, com) = TIOCSPGRP;
278		break;
279	case LINUX_FIOCLEX:
280		SCARG(&ia, com) = FIOCLEX;
281		break;
282	case LINUX_FIONCLEX:
283		SCARG(&ia, com) = FIONCLEX;
284		break;
285	case LINUX_FIONREAD:
286		SCARG(&ia, com) = FIONREAD;
287		break;
288	case LINUX_FIONBIO:
289		SCARG(&ia, com) = FIONBIO;
290		break;
291	case LINUX_FIOASYNC:
292		SCARG(&ia, com) = FIOASYNC;
293		break;
294	case LINUX_TIOCEXCL:
295		SCARG(&ia, com) = TIOCEXCL;
296		break;
297	case LINUX_TIOCNXCL:
298		SCARG(&ia, com) = TIOCNXCL;
299		break;
300	case LINUX_TIOCCONS:
301		SCARG(&ia, com) = TIOCCONS;
302		break;
303	case LINUX_TIOCNOTTY:
304		SCARG(&ia, com) = TIOCNOTTY;
305		break;
306	case LINUX_TCSBRK:
307		idat = (u_long)SCARG(uap, data);
308		if (idat != 0)
309			SCARG(&ia, com) = TIOCDRAIN;
310		else {
311			if ((error = (*bsdioctl)(fp, TIOCSBRK, NULL)) != 0)
312				goto out;
313			error = tsleep(&idat, PZERO | PCATCH, "linux_tcsbrk", hz / 4);
314			if (error == EINTR || error == ERESTART) {
315				(void)(*bsdioctl)(fp, TIOCCBRK, NULL);
316				error = EINTR;
317			} else
318				error = (*bsdioctl)(fp, TIOCCBRK, NULL);
319			goto out;
320		}
321		break;
322	case LINUX_TIOCMGET:
323		SCARG(&ia, com) = TIOCMGET;
324		break;
325	case LINUX_TIOCMSET:
326		SCARG(&ia, com) = TIOCMSET;
327		break;
328	case LINUX_TIOCMBIC:
329		SCARG(&ia, com) = TIOCMBIC;
330		break;
331	case LINUX_TIOCMBIS:
332		SCARG(&ia, com) = TIOCMBIS;
333		break;
334#ifdef LINUX_TIOCGPTN
335	case LINUX_TIOCGPTN:
336#ifndef NO_DEV_PTM
337		{
338			struct ptmget ptm;
339
340			error = (*bsdioctl)(fp, TIOCPTSNAME, &ptm);
341			if (error != 0)
342				goto out;
343			error = copyout(&ptm.sfd, SCARG(uap, data),
344			    sizeof(ptm.sfd));
345			goto out;
346		}
347#endif /* NO_DEV_PTM */
348#endif /* LINUX_TIOCGPTN */
349#ifdef LINUX_TIOCSPTLCK
350	case LINUX_TIOCSPTLCK:
351			fd_putfile(SCARG(uap, fd));
352			error = copyin(SCARG(uap, data), &idat, sizeof(idat));
353			if (error)
354				return error;
355			DPRINTF(("TIOCSPTLCK %d\n", idat));
356			return 0;
357#endif
358	case LINUX_TCXONC:
359		idat = (u_long)SCARG(uap, data);
360		switch (idat) {
361		case LINUX_TCOOFF:
362			SCARG(&ia, com) = TIOCSTOP;
363			break;
364		case LINUX_TCOON:
365			SCARG(&ia, com) = TIOCSTART;
366			break;
367		case LINUX_TCIOFF:
368		case LINUX_TCION:
369		default:
370			error = EINVAL;
371			goto out;
372		}
373		break;
374	default:
375		error = EINVAL;
376		goto out;
377	}
378
379	SCARG(&ia, fd) = SCARG(uap, fd);
380	SCARG(&ia, data) = SCARG(uap, data);
381	error = sys_ioctl(curlwp, &ia, retval);
382out:
383	fd_putfile(SCARG(uap, fd));
384	return error;
385}
386