Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S262777AbUKXSjs (ORCPT ); Wed, 24 Nov 2004 13:39:48 -0500 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S262787AbUKXSjn (ORCPT ); Wed, 24 Nov 2004 13:39:43 -0500 Received: from pop5-1.us4.outblaze.com ([205.158.62.125]:4530 "HELO pop5-1.us4.outblaze.com") by vger.kernel.org with SMTP id S262772AbUKXScR (ORCPT ); Wed, 24 Nov 2004 13:32:17 -0500 Subject: Suspend 2 merge: 44/51: Text UI plugin. From: Nigel Cunningham Reply-To: ncunningham@linuxmail.org To: Linux Kernel Mailing List In-Reply-To: <1101292194.5805.180.camel@desktop.cunninghams> References: <1101292194.5805.180.camel@desktop.cunninghams> Content-Type: text/plain Message-Id: <1101299939.5805.374.camel@desktop.cunninghams> Mime-Version: 1.0 X-Mailer: Ximian Evolution 1.4.6-1mdk Date: Thu, 25 Nov 2004 00:01:51 +1100 Content-Transfer-Encoding: 7bit Sender: linux-kernel-owner@vger.kernel.org X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 17118 Lines: 644 Here's our plugin that is used to display text output on the console. When the console loglevel is 0 or 1, the user gets a nice progress bar. Higher levels display increasing levels of debugging output. diff -ruN 850-text-ui-old/kernel/power/suspend_text.c 850-text-ui-new/kernel/power/suspend_text.c --- 850-text-ui-old/kernel/power/suspend_text.c 1970-01-01 10:00:00.000000000 +1000 +++ 850-text-ui-new/kernel/power/suspend_text.c 2004-11-15 09:41:00.000000000 +1100 @@ -0,0 +1,629 @@ +/* + * kernel/power/suspend2_text_display.c + * + * Copyright (C) 2002-2004 Nigel Cunningham + * + * This file is released under the GPLv2. + * + * Routines for Software Suspend's user interface. + * + * The user interface includes support for a text mode 'nice display'. + * + * The 'nice display' is text based and implements a progress bar and + * (optional) textual progress, as well as an overall description of + * the current action and the display of a header and the code version. + * + * It uses /dev/console, and thus also works on a serial console. + */ +#define SUSPEND_TEXT_MODE_C + +//#define __KERNEL_SYSCALLS__ + +#include +#include +#include +#include +#include + +#include "plugins.h" +#include "proc.h" +#include "suspend.h" + +/* + * The original macros use currcons, which we don't have access to. + */ +#undef video_num_columns +#define video_num_columns (vc_cons[fg_console].d->vc_cols) +#undef video_num_lines +#define video_num_lines (vc_cons[fg_console].d->vc_rows) + +static int barwidth = 0, barposn = -1, newbarposn = 0; +static int draw_progress_bar = 1; +static char print_buf[1024]; /* Same as printk - should be safe */ + +/* We remember the last header that was (or could have been) displayed for + * use during log level switches */ +static char lastheader[512]; +static int lastheader_message_len = 0; + +static void hide_cursor(void); +static void unblank_screen_via_file(void); + +static int suspend_console_fd = -1; +static struct termios termios; +static int lastloglevel = -1; + +#ifdef CONFIG_DEVFS_FS +static int mounted_devfs = 0; +#endif + +#define cond_console_print(chars) \ + if (suspend_console_fd > -1) { \ + int count = strlen(chars); \ + sys_write(suspend_console_fd, chars, count); \ + hide_cursor(); \ + unblank_screen_via_file(); \ + } + +static void move_cursor_to(unsigned char * xy) +{ + char buf[10]; + + snprintf(buf, 10, "\233%d;%dH", xy[1], xy[0]); + cond_console_print(buf); +} + +static void clear_display(void) +{ + char buf[4] = "\2332J"; + unsigned char home[2] = { 0, 0 }; + + cond_console_print(buf); + move_cursor_to(home); +} + +static void hide_cursor(void) +{ + char buf[6] = "\033[?1c"; + if (suspend_console_fd > -1) + sys_write(suspend_console_fd, buf, 5); +} + +static void restore_cursor(void) +{ + char buf[6] = "\033[?0c"; + if (suspend_console_fd > -1) + sys_write(suspend_console_fd, buf, 5); +} + +static void unblank_screen_via_file(void) +{ + char buf[6] = "\033[13]"; + if (suspend_console_fd > -1) + sys_write(suspend_console_fd, buf, 5); +} + +/* prepare_status + * Description: Prepare the 'nice display', drawing the header and version, + * along with the current action and perhaps also resetting the + * progress bar. + * Arguments: int printalways: Whether to print the action when debugging + * is on. + * int clearbar: Whether to reset the progress bar. + * const char *fmt, ...: The action to be displayed. + */ +static void text_prepare_status(int printalways, int clearbar, const char *fmt, va_list args) +{ + unsigned char posn[2]; + + if (fmt) + lastheader_message_len = vsnprintf(lastheader, 512, fmt, args); + + if (console_loglevel >= SUSPEND_ERROR) { + + if (printalways) + printk("\n** %s\n", lastheader); + return; + } + + barwidth = (video_num_columns - 2 * (video_num_columns / 4) - 2); + + /* Print version */ + posn[0] = (unsigned char) (0); + posn[1] = (unsigned char) (video_num_lines); + move_cursor_to(posn); + cond_console_print(SUSPEND_CORE_VERSION); + + /* Print header */ + posn[0] = (unsigned char) ((video_num_columns - 31) / 2); + posn[1] = (unsigned char) ((video_num_lines / 3) - 3); + move_cursor_to(posn); + + cond_console_print("S O F T W A R E S U S P E N D"); + + /* Print action */ + posn[1] = (unsigned char) (video_num_lines / 3); + posn[0] = (unsigned char) 0; + move_cursor_to(posn); + + /* Clear old message */ + for (barposn = 0; barposn < video_num_columns; barposn++) + cond_console_print(" "); + + posn[0] = (unsigned char) + ((video_num_columns - lastheader_message_len) / 2); + move_cursor_to(posn); + cond_console_print(lastheader); + + if (draw_progress_bar) { + /* Draw left bracket of progress bar. */ + posn[0] = (unsigned char) (video_num_columns / 4); + posn[1]++; + move_cursor_to(posn); + cond_console_print("["); + + /* Draw right bracket of progress bar. */ + posn[0] = (unsigned char) + (video_num_columns - (video_num_columns / 4) - 1); + move_cursor_to(posn); + cond_console_print("]"); + + if (clearbar) { + /* Position at start of progress */ + posn[0] = (unsigned char) (video_num_columns / 4 + 1); + move_cursor_to(posn); + + /* Clear bar */ + for (barposn = 0; barposn < barwidth; barposn++) + cond_console_print(" "); + move_cursor_to(posn); + } + } + + hide_cursor(); + + barposn = 0; +} + +/* text_loglevel_change + * + * Description: Update the display when the user changes the log level. + * Returns: Boolean indicating whether the level was changed. + */ + +static void text_loglevel_change(void) +{ + /* Calculate progress bar width. Note that whether the + * splash screen is on might have changed (this might be + * the first call in a new cycle), so we can't take it + * for granted that the width is the same as last time + * we came in here */ + barwidth = (video_num_columns - 2 * (video_num_columns / 4) - 2); + barposn = 0; + + /* Only reset the display if we're switching between nice display + * and displaying debugging output */ + + if (console_loglevel >= SUSPEND_ERROR) { + char message[35]; + if (lastloglevel < SUSPEND_ERROR) + clear_display(); + + snprintf(message, 35, + "Switched to console loglevel %d.\n", + console_loglevel); + cond_console_print(message); + + if (lastloglevel < SUSPEND_ERROR) { + cond_console_print(lastheader); + cond_console_print("\n"); + } + + } else if (lastloglevel >= SUSPEND_ERROR) { + clear_display(); + + /* Get the nice display or last action [re]drawn */ + text_prepare_status(1, 0, NULL, NULL); + } + + lastloglevel = console_loglevel; +} +/* text_update_progress + * + * Description: Update the progress bar and (if on) in-bar message. + * Arguments: UL value, maximum: Current progress percentage (value/max). + * const char *fmt, ...: Message to be displayed in the middle + * of the progress bar. + * Note that a NULL message does not mean that any previous + * message is erased! For that, you need prepare_status with + * clearbar on. + * Returns: Unsigned long: The next value where status needs to be updated. + * This is to reduce unnecessary calls to text_update_progress. + */ +unsigned long text_update_progress(unsigned long value, unsigned long maximum, + const char *fmt, va_list args) +{ + unsigned long next_update = 0; + int bitshift = generic_fls(maximum) - 16; + unsigned char posn[2]; + int message_len = 0; + + if (!barwidth) + barwidth = (video_num_columns - 2 * (video_num_columns / 4) - 2); + + if (!maximum) + return maximum; + + if (value < 0) + value = 0; + + if (value > maximum) + value = maximum; + + /* Try to avoid math problems - we can't do 64 bit math here + * (and shouldn't need it - anyone got screen resolution + * of 65536 pixels or more?) */ + if (bitshift > 0) { + unsigned long temp_maximum = maximum >> bitshift; + unsigned long temp_value = value >> bitshift; + newbarposn = (int) (temp_value * barwidth / temp_maximum); + } else + newbarposn = (int) (value * barwidth / maximum); + + if (newbarposn < barposn) + barposn = 0; + + next_update = ((newbarposn + 1) * maximum / barwidth) + 1; + + if ((console_loglevel >= SUSPEND_ERROR) || (!draw_progress_bar)) + return next_update; + + /* Update bar */ + if (draw_progress_bar) { + posn[1] = (unsigned char) ((video_num_lines / 3) + 1); + + /* Clear bar if at start */ + if (!barposn) { + posn[0] = (unsigned char) (video_num_columns / 4 + 1); + move_cursor_to(posn); + for (; barposn < barwidth; barposn++) + cond_console_print(" "); + barposn = 0; + } + posn[0] = (unsigned char) (video_num_columns / 4 + 1 + barposn); + move_cursor_to(posn); + + for (; barposn < newbarposn; barposn++) + cond_console_print("-"); + } + + /* Print string in progress bar on loglevel 1 */ + if ((fmt) && (console_loglevel)) { + message_len = vsnprintf(print_buf, sizeof(print_buf), " ", NULL); + message_len += vsnprintf(print_buf + message_len, + sizeof(print_buf) - message_len, fmt, args); + message_len += vsnprintf(print_buf + message_len, + sizeof(print_buf) - message_len, " ", NULL); + + if (message_len) { + posn[0] = (unsigned char) + ((video_num_columns - message_len) / 2); + posn[1] = (unsigned char) + ((video_num_lines / 3) + 1); + move_cursor_to(posn); + cond_console_print(print_buf); + } + } + + barposn = newbarposn; + hide_cursor(); + + return next_update; +} + +extern asmlinkage long sys_ioctl(unsigned int fd, unsigned int cmd, + unsigned long arg); + +static void text_message(unsigned long section, unsigned long level, + int normally_logged, + const char *fmt, va_list args) +{ + int printed_len = 0; + + if ((section) && (!TEST_DEBUG_STATE(section))) + return; + + if (level == SUSPEND_STATUS) { + text_prepare_status(1, 0, fmt, args); + return; + } + + if (level > console_loglevel) + return; + + printed_len = vsnprintf(print_buf + printed_len, + sizeof(print_buf) - printed_len, fmt, args); + + + if ((TEST_ACTION_STATE(SUSPEND_LOGALL)) || + (normally_logged)) { + /* If we didn't print anything, don't do the \n anyway! */ + if (!printed_len) + return; + printk(print_buf); + } else + cond_console_print(print_buf); +} +/* + * + */ + +static void suspend_get_dev_console(void) +{ + if (suspend_console_fd > -1) + return; + + suspend_console_fd = sys_open("/dev/console", O_RDWR | O_NONBLOCK, 0); + if (suspend_console_fd < 0) { + sys_mkdir("/dev", 0700); +#ifdef CONFIG_DEVFS_FS + sys_mount("devfs", "/dev", "devfs", 0, NULL); + mounted_devfs = 1; +#endif + suspend_console_fd = sys_open("/dev/console", O_RDWR | O_NONBLOCK, 0); + } + if (suspend_console_fd < 0) { + printk("Can't open /dev/console. Error value was %d.\n", + suspend_console_fd); + suspend_console_fd = -1; + return; + } + + sys_ioctl(suspend_console_fd, TCGETS, (long)&termios); + termios.c_lflag &= ~ICANON; + sys_ioctl(suspend_console_fd, TCSETSF, (long)&termios); +} + +/* prepare_console + * + */ +static void text_prepare_console(void) +{ + suspend_get_dev_console(); + + if (console_loglevel < 2) + clear_display(); + + lastloglevel = console_loglevel; +} + +/* cleanup_console + * + * Description: Close our handle on /dev/console. Must be done + * earlier than pm_restore_console to avoid problems with other + * processes trying to grab it when thawed. + */ + +static void cleanup_console(void) +{ + if (console_loglevel < 2) + clear_display(); + restore_cursor(); + termios.c_lflag |= ICANON; + sys_ioctl(suspend_console_fd, TCSETSF, (long)&termios); + sys_close(suspend_console_fd); + suspend_console_fd = -1; + +#ifdef CONFIG_DEVFS_FS + if (mounted_devfs) + sys_umount("/dev", 0); +#endif + + lastloglevel = -1; + return; +} + +static void text_redraw(void) +{ + sys_ioctl(suspend_console_fd, TIOCL_BLANKSCREEN, (long)&termios); + sys_ioctl(suspend_console_fd, TIOCL_UNBLANKSCREEN, (long)&termios); +} + +static int text_keypress(unsigned int key) +{ + switch (key) { + case 48: + console_loglevel = 0; + break; + case 49: + console_loglevel = 1; + break; +#ifdef CONFIG_SOFTWARE_SUSPEND_DEBUG + case 122: + /* `: Toggle slow */ + suspend_action ^= (1 << SUSPEND_SLOW); + suspend2_core_ops->schedule_message(7); + break; + case 1: + /* F1: Toggle any section debugging. */ + suspend_debug_state ^= (1 << SUSPEND_ANY_SECTION); + suspend2_core_ops->schedule_message(20); + break; + case 2: + /* F2: Freeze. */ + suspend_debug_state ^= (1 << SUSPEND_FREEZER); + suspend2_core_ops->schedule_message(21); + break; + case 3: + /* F3: Eat Memory */ + suspend_debug_state ^= (1 << SUSPEND_EAT_MEMORY); + suspend2_core_ops->schedule_message(22); + break; + case 4: + /* F4: Pagesets. */ + suspend_debug_state ^= (1 << SUSPEND_PAGESETS); + suspend2_core_ops->schedule_message(23); + break; + case 5: + /* F5: IO. */ + suspend_debug_state ^= (1 << SUSPEND_IO); + suspend2_core_ops->schedule_message(24); + break; + case 6: + /* F6: Bmapping of pages */ + suspend_debug_state ^= (1 << SUSPEND_BMAP); + suspend2_core_ops->schedule_message(25); + break; + case 7: + /* F7: Writer */ + suspend_debug_state ^= (1 << SUSPEND_WRITER); + suspend2_core_ops->schedule_message(26); + break; + case 8: + /* F8: Memory */ + suspend_debug_state ^= (1 << SUSPEND_MEMORY); + suspend2_core_ops->schedule_message(27); + break; + case 9: + /* F9: Ranges */ + suspend_debug_state ^= (1 << SUSPEND_RANGES); + suspend2_core_ops->schedule_message(28); + break; + case 10: + /* F10: Memory Pool */ + suspend_debug_state ^= (1 << SUSPEND_MEM_POOL); + suspend2_core_ops->schedule_message(29); + break; + case 11: + /* F11: Nosave */ + suspend_debug_state ^= (1 << SUSPEND_NOSAVE); + suspend2_core_ops->schedule_message(30); + break; + case 12: + /* F12: Integrity */ + suspend_debug_state ^= (1 << SUSPEND_INTEGRITY); + suspend2_core_ops->schedule_message(31); + break; + case 112: + /* During suspend, toggle pausing with P */ + suspend_action ^= (1 << SUSPEND_PAUSE); + suspend2_core_ops->schedule_message(1); + break; + case 115: + /* Otherwise, if S pressed, toggle single step */ + suspend_action ^= (1 << SUSPEND_SINGLESTEP); + suspend2_core_ops->schedule_message(3); + break; + case 108: + /* Otherwise, if L pressed, toggle logging everything */ + suspend_action ^= (1 << SUSPEND_LOGALL); + suspend2_core_ops->schedule_message(4); + break; + case 116: + /* T: Toggle freezing timers */ + clear_suspend_state(SUSPEND_TIMER_FREEZER_ON); + suspend2_core_ops->schedule_message(99); + break; + case 50: + case 51: + case 52: + case 53: + case 54: + case 55: + case 56: + case 57: + console_loglevel = ((key - 48)); + break; +#endif + default: + return 0; + } + return 1; +} + +/* + * User interface specific /proc/suspend entries. + */ + +static struct suspend_plugin_ops text_mode_ops; + +static struct suspend_proc_data proc_params[] = { + { .filename = "text_mode_progress_bar", + .permissions = PROC_RW, + .type = SUSPEND_PROC_DATA_INTEGER, + .data = { + .integer = { + .variable = &draw_progress_bar, + .minimum = 0, + .maximum = 1, + + } + } + }, + + { .filename = "disable_textmode_support", + .permissions = PROC_RW, + .type = SUSPEND_PROC_DATA_INTEGER, + .data = { + .integer = { + .variable = &text_mode_ops.disabled, + .minimum = 0, + .maximum = 1, + } + } + } +}; + +static struct suspend_plugin_ops text_mode_ops = { + .type = UI_PLUGIN, + .name = "Text Mode Support", + .ops = { + .ui = { + .prepare = text_prepare_console, + .log_level_change = text_loglevel_change, + .message = text_message, + .update_progress = text_update_progress, + .cleanup = cleanup_console, + .keypress = text_keypress, + .post_kernel_restore_redraw = + text_redraw, + } + } +}; + +/* ---- Registration ---- */ + +static __init int text_mode_load(void) +{ + int i, numfiles = sizeof(proc_params) / sizeof(struct suspend_proc_data); + int result; + + if (!(result = suspend_register_plugin(&text_mode_ops))) { + printk("Software Suspend text mode support loaded.\n"); + for (i=0; i< numfiles; i++) + suspend_register_procfile(&proc_params[i]); + } + return result; +} + +#ifdef MODULE +static __exit void text_mode_unload(void) +{ + int i, numfiles = sizeof(proc_params) / sizeof(struct suspend_proc_data); + + printk("Software Suspend text mode support unloading.\n"); + + for (i=0; i< numfiles; i++) + suspend_unregister_procfile(&proc_params[i]); + + suspend_unregister_plugin(&text_mode_ops); +} + +module_init(text_mode_load); +module_exit(text_mode_unload); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Nigel Cunningham"); +MODULE_DESCRIPTION("Suspend2 Text Mode support"); +#else +late_initcall(text_mode_load); +#endif - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/