/* clock.c * Version 0.0 * * Userland program utilizing /dev/pit. * * This code's under the GNU General Public License. * * Written by Andy Goth and Zorica Majdov, 2003. * * Assignment for CSE 3442-001 lab 5. * Dr. Roger Walker, University of Texas at Arlington */ #include #include #include #include #include #include #include #include #include #include #include #include "../kernel/pit.h" #define BACKSPACE 127 #define CLEAR 27 #define RETURN 10 /* --- Function prototypes. --- */ int main(int argc, char** argv); static void display(char* format, ...); static void display_time(void); static void handle_timer(void); static void handle_keyboard(void); static void timer_init(void); static void timer_cleanup(void); static void timer_stop(void); static void timer_start(void); static void cleanup(void); /* --- Global variables. --- */ /* The current time. */ static volatile int hours, minutes, seconds, tenths; /* If nonzero, the timer's tickin'. */ static int timer_running = 1; /* The terminal settings prior to running this program. */ static struct termios old_mode; /* File descriptor for /dev/pit. */ static int pit_fd = -1; /* Filename of /dev/pit. */ static char* devpit_fname; static const char* default_devpit_fname = "/dev/pit"; /* The divisor value to send to the 8253. */ /* static pit_divisor_t divisor = {100, 100, 10}; */ static pit_divisor_t divisor = {2, 2, 2}; /* --- Function definitions. --- */ /* Where it's at. */ int main(int argc, char** argv) { struct termios mode; struct pollfd ufds[2]; /* Parse command line arguments. */ if (argc == 1) { devpit_fname = strdup(default_devpit_fname); } else if (argc == 2) { devpit_fname = strdup(argv[1]); } else if (argc > 2) { printf("Usage: %s [device]\n", argv[0]); return EXIT_FAILURE; } /* Disable echo. Enable raw mode. */ if (isatty(0)) { tcgetattr(0, &mode); old_mode = mode; mode.c_lflag &= ~ECHO; mode.c_lflag &= ~ICANON; tcsetattr(0, TCSANOW, &mode); } /* Register exit handler. */ atexit(cleanup); /* Start the timer. */ timer_init(); timer_running = 1; /* Prepare for poll, below. */ ufds[0].fd = 0; ufds[0].events = POLLIN; ufds[1].fd = pit_fd; ufds[1].events = POLLIN; /* Update the screen. */ display_time(); /* Event loop. */ while (1) { /* Wait for keyboard or timer. */ int ret = poll(ufds, 2, -1); /* Error? */ if (ret == -1) { if (errno == EINTR) { /* Interrupted by signal... try again. */ continue; } else { /* Something Bad. */ perror("poll"); exit(EXIT_FAILURE); } } /* Nothing? */ if (ret == 0) continue; /* Keyboard junk. */ if (ufds[0].revents & POLLIN) { handle_keyboard(); if (--ret == 0) continue; } /* Timer tick. */ if (ufds[1].revents & POLLIN) { handle_timer(); if (--ret == 0) continue; } /* Shouldn't happen. */ fprintf(stderr, "Hmm...???\n"); exit(EXIT_FAILURE); } /* Should never get here... */ return EXIT_SUCCESS; } /* Prints a line of text to the screen. */ static void display(char* format, ...) { va_list ap; va_start(ap, format); printf("\r"); vprintf(format, ap); fflush(stdout); va_end(ap); } /* Prints the current time. */ static void display_time(void) { display("%s: %02d:%02d:%02d.%01d", timer_running ? "running" : "paused ", hours, minutes, seconds, tenths); } /* Usage: * * clock [device] * * device Overrides the 8253 device filename. (optional) * * Keys: * * C Pause or resume timer. * Z Set timer to zero. * S Set timer to a user-supplied value. * Q Terminate program. */ /* User pressed a key. Or something. */ static void handle_keyboard(void) { int ret, running; int caret = 0; char buf[] = "------"; /* Process keyboard input. */ switch (toupper(getchar())) { case 'C': /* Pause or resume timer. */ if (timer_running) { timer_running = 0; timer_stop(); } else { timer_running = 1; timer_start(); } display_time(); break; case 'Z': /* Reset timer to zero. */ hours = minutes = seconds = tenths = 0; display_time(); break; case 'S': /* Set the timer to a user-supplied value. */ running = timer_running; timer_running = 0; timer_stop(); while (1) { display("input : %.2s:%.2s:%.2s.0\e[%dG", buf + 0, buf + 2, buf + 4, caret + caret / 2 + 10); again: ret = getchar(); if (ret == BACKSPACE) { if (caret > 0) { caret--; buf[caret] = '-'; } } else if (ret == CLEAR) { caret = 0; strcpy(buf, "------"); } else if (ret == RETURN) { if (caret == 6) break; } else if (ret >= '0' && ret <= '9') { if (caret == 6) goto again; if (caret == 2 || caret == 4) { if (ret >= '6') goto again; } buf[caret] = ret; caret++; } } hours = (buf[0] - '0') * 10 + (buf[1] - '0') * 1; minutes = (buf[2] - '0') * 10 + (buf[3] - '0') * 1; seconds = (buf[4] - '0') * 10 + (buf[5] - '0') * 1; tenths = 0; timer_running = running; if (timer_running) timer_start(); break; case 'Q': /* Quit. */ printf("\n"); exit(EXIT_SUCCESS); } } /* Updates the clock. */ static void handle_timer(void) { unsigned long ticks; /* See how many ticks happened. */ if (read(pit_fd, &ticks, sizeof(ticks)) < 0) { perror("read"); exit(EXIT_FAILURE); } /* Process the ticks. */ for (; ticks != 0; --ticks) { if (++tenths < 10) {continue;} else {tenths = 0;} if (++seconds < 60) {continue;} else {seconds = 0;} if (++minutes < 60) {continue;} else {minutes = 0;} if (++hours < 100) {continue;} else {hours = 0;} } display_time(); } /* Initialize and start the timer. */ static void timer_init(void) { if ((pit_fd = open(devpit_fname, O_RDONLY)) < 0) { perror("open"); fprintf(stderr, "Error opening %s.\n", devpit_fname); exit(EXIT_FAILURE); } if (ioctl(pit_fd, PIT_SET_DIVISOR, &divisor) != 0) goto fail; if (ioctl(pit_fd, PIT_ENABLE_TICKS) != 0) goto fail; return; fail: perror("ioctl"); exit(EXIT_FAILURE); } /* Timer shutdown. */ static void timer_cleanup(void) { if (pit_fd != -1 && close(pit_fd) != 0) { perror("close"); fprintf(stderr, "Error closing %s.\n", devpit_fname); } } /* Pause the timer. */ static void timer_stop(void) { if (ioctl(pit_fd, PIT_IGNORE_TICKS) != 0) { perror("ioctl"); exit(EXIT_FAILURE); } } /* Start or resume the timer. */ static void timer_start(void) { if (ioctl(pit_fd, PIT_ENABLE_TICKS) != 0) { perror("ioctl"); exit(EXIT_FAILURE); } } /* Called before program quits. */ static void cleanup(void) { timer_cleanup(); tcsetattr(0, TCSANOW, &old_mode); if (devpit_fname != NULL) free(devpit_fname); } /* vim: set ts=4 sts=4 sw=4 tw=80 et: */ /* EOF */