/*===========================================================================* * do_open * *===========================================================================*/ static int do_open(devminor_t minor, int access, endpoint_t user_endpt) { /* A tty line has been opened. Make it the callers controlling tty if * CDEV_NOCTTY is *not* set and it is not the log device. CDEV_CTTY is returned * if the tty is made the controlling tty, otherwise OK or an error code. */ tty_t *tp; int r = OK; if ((tp = line2tty(minor)) == NULL) return ENXIO; if (minor == LOG_MINOR && isconsole(tp)) { /* The log device is a write-only diagnostics device. */ if (access & CDEV_R_BIT) return EACCES; } else { if (!(access & CDEV_NOCTTY)) { tp->tty_pgrp = user_endpt; r = CDEV_CTTY; } tp->tty_openct++; if (tp->tty_openct == 1) { /* Tell the device that the tty is opened */ (*tp->tty_open)(tp, 0); } } return r; }
/*===========================================================================* * do_close * *===========================================================================*/ static int do_close(devminor_t minor) { /* A tty line has been closed. Clean up the line if it is the last close. */ tty_t *tp; if ((tp = line2tty(minor)) == NULL) return ENXIO; if ((minor != LOG_MINOR || !isconsole(tp)) && --tp->tty_openct == 0) { tp->tty_pgrp = 0; tty_icancel(tp); (*tp->tty_ocancel)(tp, 0); (*tp->tty_close)(tp, 0); tp->tty_termios = termios_defaults; tp->tty_winsize = winsize_defaults; setattr(tp); } return OK; }
/*===========================================================================* * do_int * *===========================================================================*/ PRIVATE void do_int() { /* The TTY task can generate two kinds of interrupts: * - a character has been received from the console or an RS232 line. * - an RS232 line has completed a write request (on behalf of a user). * The interrupt handler may delay the interrupt message at its discretion * to avoid swamping the TTY task. Messages may be overwritten when the * lines are fast or when there are races between different lines, input * and output, because MINIX only provides single buffering for interrupt * messages (in proc.c). This is handled by explicitly checking each line * for fresh input and completed output on each interrupt. Input is given * priority so signal characters are not delayed by lots of small output * requests. This does not signifigantly delay the detection of output * completions, since TTY will be scheduled to handle the output before * any new user can request input. * * If a reply is sent (to FS), further input/output must not be processed * for fear of sending a second message to FS, which would be lost under * certain race conditions. E.g. when FS is now ready and is about to * sendrec() to TTY (usually from rw_dev()). FS handles the deadlock * resulting from the _first_ send() from TTY clashing with the sendrec() * from FS. But then the scheduling causes the retried sendrec() to get * through, so the second send() fails with an E_LOCKED error. This might * be avoided by preempting tasks like TTY over servers like FS, or giving * preference to senders over receivers. In practice, TTY relies on being * woken at a later clock tick. */ char *buf; static struct tty_struct *last_tp = FIRST_TTY; /* round-robin service */ unsigned char odone; register char *rbuf; unsigned remaining; register struct tty_struct *tp; unsigned wrapcount; tp = last_tp; do { if (++tp >= END_TTY) tp = FIRST_TTY; /* Transfer any fresh input to TTY's buffer, and test output done. */ remaining = (*tp->tty_devread)(tp->tty_line, &buf, &odone); if (remaining == 0) goto check_output; /* avoid even uglier indentation */ rbuf = buf; if (!isconsole(tp) && tp->tty_mode & RAW) { /* Avoid grotesquely inefficient in_char(), except for console * which needs further translation. * Line feeds need not be counted. */ /* If queue becomes too full, ask external device to stop. */ if (tp->tty_incount < tp->tty_ihighwater && tp->tty_incount + remaining >= tp->tty_ihighwater) rs_istop(tp->tty_line); if (remaining > tp->tty_insize - tp->tty_incount) /* not all fit, discard */ remaining = tp->tty_insize - tp->tty_incount; wrapcount = tp->tty_inbufend - tp->tty_inhead; if (wrapcount < remaining) { memcpy(tp->tty_inhead, rbuf, wrapcount); tp->tty_inhead = tp->tty_inbuf; rbuf += wrapcount; tp->tty_incount += wrapcount; remaining -= wrapcount; } memcpy(tp->tty_inhead, rbuf, remaining); tp->tty_inhead += remaining; tp->tty_incount += remaining; } else { do in_char(tp, *rbuf++); while (--remaining != 0); } /* Possibly restart output (in case there were xoffs or echoes). */ (*tp->tty_devstart)(tp); /* See if a previously blocked reader can now be satisfied. */ if (tp->tty_inleft != 0 && tp->tty_incount != 0 && (tp->tty_mode & (RAW | CBREAK) || tp->tty_lfct != 0)) { /* Tell hanging reader that chars have arrived. */ tty_reply(REVIVE, (int) tp->tty_incaller, (int) tp->tty_inproc, rd_chars(tp)); } check_output: /* maybe continue output */ if (isconsole(tp) && tp->tty_outleft > 0) (*tp->tty_devstart)(tp); /* Finish off any completed block of output. */ if (odone) { if (tp->tty_rwords > 0) { /* not echo */ tp->tty_phys += tp->tty_rwords; tp->tty_cum += tp->tty_rwords; tp->tty_outleft -= tp->tty_rwords; if (tp->tty_outleft == 0) { finish(tp, tp->tty_cum); continue; } } tp->tty_rwords = 0; rs_ocancel(tp->tty_line); /* tty_ocancel does too much*/ (*tp->tty_devstart)(tp); /* maybe continue output */ } } while (tp != last_tp || tty_events >= EVENT_THRESHOLD); tty_awake = FALSE; last_tp = tp; }
/*===========================================================================* * do_ioctl * *===========================================================================*/ static int do_ioctl(devminor_t minor, unsigned long request, endpoint_t endpt, cp_grant_id_t grant, int flags, endpoint_t user_endpt, cdev_id_t id) { /* Perform an IOCTL on this terminal. POSIX termios calls are handled * by the IOCTL system call. */ kio_bell_t bell; clock_t ticks; tty_t *tp; int i, r; if ((tp = line2tty(minor)) == NULL) return ENXIO; r = OK; switch (request) { case TIOCGETA: /* Get the termios attributes. */ r = sys_safecopyto(endpt, grant, 0, (vir_bytes) &tp->tty_termios, sizeof(struct termios)); break; case TIOCSETAW: case TIOCSETAF: case TIOCDRAIN: if (tp->tty_outleft > 0) { if (flags & CDEV_NONBLOCK) return EAGAIN; /* Wait for all ongoing output processing to finish. */ tp->tty_iocaller = endpt; tp->tty_ioid = id; tp->tty_ioreq = request; tp->tty_iogrant = grant; return EDONTREPLY; /* suspend the caller */ } if (request == TIOCDRAIN) break; if (request == TIOCSETAF) tty_icancel(tp); /*FALL THROUGH*/ case TIOCSETA: /* Set the termios attributes. */ r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &tp->tty_termios, sizeof(struct termios)); if (r != OK) break; setattr(tp); break; case TIOCFLUSH: r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &i, sizeof(i)); if (r != OK) break; if(i & FREAD) { tty_icancel(tp); } if(i & FWRITE) { (*tp->tty_ocancel)(tp, 0); } break; case TIOCSTART: tp->tty_inhibited = 0; tp->tty_events = 1; break; case TIOCSTOP: tp->tty_inhibited = 1; tp->tty_events = 1; break; case TIOCSBRK: /* tcsendbreak - turn break on */ if (tp->tty_break_on != NULL) (*tp->tty_break_on)(tp,0); break; case TIOCCBRK: /* tcsendbreak - turn break off */ if (tp->tty_break_off != NULL) (*tp->tty_break_off)(tp,0); break; case TIOCGWINSZ: r = sys_safecopyto(endpt, grant, 0, (vir_bytes) &tp->tty_winsize, sizeof(struct winsize)); break; case TIOCSWINSZ: r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &tp->tty_winsize, sizeof(struct winsize)); sigchar(tp, SIGWINCH, 0); break; case KIOCBELL: /* Sound bell (only /dev/console). */ if (!isconsole(tp)) break; r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &bell, sizeof(bell)); if (r != OK) break; ticks = bell.kb_duration.tv_usec * system_hz / 1000000; ticks += bell.kb_duration.tv_sec * system_hz; if (!ticks) ticks++; beep_x(bell.kb_pitch, ticks); break; case TIOCGETD: /* get line discipline */ i = TTYDISC; r = sys_safecopyto(endpt, grant, 0, (vir_bytes) &i, sizeof(i)); break; case TIOCSETD: /* set line discipline */ printf("TTY: TIOCSETD: can't set any other line discipline.\n"); r = ENOTTY; break; case TIOCGLINED: /* get line discipline as string */ r = sys_safecopyto(endpt, grant, 0, (vir_bytes) lined, sizeof(lined)); break; case TIOCGQSIZE: /* get input/output queue sizes */ i = TTY_IN_BYTES; /* best we can do.. */ r = sys_safecopyto(endpt, grant, 0, (vir_bytes) &i, sizeof(i)); break; case KIOCSMAP: /* Load a new keymap (only /dev/console). */ if (isconsole(tp)) r = kbd_loadmap(endpt, grant); break; case TIOCSFON: /* Load a font into an EGA or VGA card ([email protected]) */ if (isconsole(tp)) r = con_loadfont(endpt, grant); break; case TIOCSCTTY: /* Process sets this tty as its controlling tty */ tp->tty_pgrp = user_endpt; break; /* These Posix functions are allowed to fail if _POSIX_JOB_CONTROL is * not defined. */ case TIOCGPGRP: case TIOCSPGRP: default: r = ENOTTY; } return r; }
/* * Find out the _real_ console. Assume that stdin is connected to * the console device (/dev/console). */ int consolename(char *res, int rlen) { #ifdef TIOCGDEV unsigned int kdev; #endif struct stat st, st2; char buf[256]; char *p; int didmount = 0; int n, r; int fd; fstat(0, &st); if (major(st.st_rdev) != 5 || minor(st.st_rdev) != 1) { /* * Old kernel, can find real device easily. */ int r = findtty(res, "/dev", rlen, st.st_rdev); if (0 != r) fprintf(stderr, "bootlogd: cannot find console device " "%d:%d under /dev\n", major(st.st_rdev), minor(st.st_rdev)); return r; } #ifdef TIOCGDEV # ifndef ENOIOCTLCMD # define ENOIOCTLCMD 515 # endif if (ioctl(0, TIOCGDEV, &kdev) == 0) { int r = findtty(res, "/dev", rlen, (dev_t)kdev); if (0 != r) fprintf(stderr, "bootlogd: cannot find console device " "%d:%d under /dev\n", major(kdev), minor(kdev)); return r; } if (errno != ENOIOCTLCMD) return -1; #endif #ifdef __linux__ /* * Read /proc/cmdline. */ stat("/", &st); if (stat("/proc", &st2) < 0) { perror("bootlogd: /proc"); return -1; } if (st.st_dev == st2.st_dev) { if (mount("proc", "/proc", "proc", 0, NULL) < 0) { perror("bootlogd: mount /proc"); return -1; } didmount = 1; } n = 0; r = -1; if ((fd = open("/proc/cmdline", O_RDONLY)) < 0) { perror("bootlogd: /proc/cmdline"); } else { buf[0] = 0; if ((n = read(fd, buf, sizeof(buf) - 1)) >= 0) r = 0; else perror("bootlogd: /proc/cmdline"); close(fd); } if (didmount) umount("/proc"); if (r < 0) return r; /* * OK, so find console= in /proc/cmdline. * Parse in reverse, opening as we go. */ p = buf + n; *p-- = 0; r = -1; while (p >= buf) { if (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { *p-- = 0; continue; } if (strncmp(p, "console=", 8) == 0 && isconsole(p + 8, res, rlen)) { r = 0; break; } p--; } if (r == 0) return r; #endif /* * Okay, no console on the command line - * guess the default console. */ for (n = 0; defcons[n]; n++) if (isconsole(defcons[n], res, rlen)) return 0; fprintf(stderr, "bootlogd: cannot deduce real console device\n"); return -1; }