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 
67 int
linux_ioctl_termios(struct lwp * l,const struct linux_sys_ioctl_args * uap,register_t * retval)68 linux_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);
382 out:
383           fd_putfile(SCARG(uap, fd));
384           return error;
385 }
386