#include "keyboard.h"
#define TTY3270_CHAR_BUF_SIZE 256
-#define TTY3270_OUTPUT_BUFFER_SIZE 1024
+#define TTY3270_OUTPUT_BUFFER_SIZE 4096
#define TTY3270_STRING_PAGES 5
#define TTY3270_SCREEN_PAGES 8 /* has to be power-of-two */
/* Output stuff. */
struct list_head lines; /* List of lines. */
- struct list_head update; /* List of lines to update. */
unsigned char wcc; /* Write control character. */
int nr_lines; /* # lines in list. */
int nr_up; /* # lines up in history. */
struct raw3270_request *write; /* Single write request. */
struct timer_list timer; /* Output delay timer. */
char *converted_line; /* RAW 3270 data stream */
+ unsigned int line_view_start; /* Start of visible area */
+ unsigned int line_write_start; /* current write position */
/* Current tty screen. */
unsigned int cx, cy; /* Current output position. */
/* tty3270->update_flags. See tty3270_update for details. */
#define TTY_UPDATE_ERASE 1 /* Use EWRITEA instead of WRITE. */
-#define TTY_UPDATE_LIST 2 /* Update lines in tty3270->update. */
#define TTY_UPDATE_INPUT 4 /* Update input line. */
#define TTY_UPDATE_STATUS 8 /* Update status line. */
#define TTY_UPDATE_ALL 16 /* Recreate screen. */
#define TTY3270_INPUT_AREA_ROWS 2
-static void tty3270_update(struct timer_list *);
/*
* Setup timeout for a device. On timeout trigger an update.
*/
return cp;
}
+static int tty3270_line_increment(struct tty3270 *tp, unsigned int line, unsigned int incr)
+{
+ return (line + incr) & (tp->allocated_lines - 1);
+}
+
+static struct tty3270_line *tty3270_get_write_line(struct tty3270 *tp, unsigned int num)
+{
+ return tp->screen + tty3270_line_increment(tp, tp->line_write_start, num);
+}
+
+static struct tty3270_line *tty3270_get_view_line(struct tty3270 *tp, unsigned int num)
+{
+ return tp->screen + tty3270_line_increment(tp, tp->line_view_start, num - tp->nr_up);
+}
+
/*
* The input line are the two last lines of the screen.
*/
* The status line is the last line of the screen. It shows the string
* "Running"/"Holding" in the lower right corner of the screen.
*/
-static void tty3270_update_status(struct tty3270 *tp)
-{
- tp->update_flags |= TTY_UPDATE_STATUS;
- tty3270_set_timer(tp, 1);
-}
-
-/*
- * The status line is the last line of the screen. It shows the string
- * "Running"/"Holding" in the lower right corner of the screen.
- */
static int tty3270_add_status(struct tty3270 *tp)
{
char *cp = tp->converted_line;
* Set output offsets to 3270 datastream fragment of a tty string.
* (TO_SBA offset at the start and TO_RA offset at the end of the string)
*/
-static void tty3270_update_string(struct tty3270 *tp, struct string *line, int nr)
+static void tty3270_update_string(struct tty3270 *tp, char *line, int len, int nr)
{
unsigned char *cp;
-
- raw3270_buffer_address(tp->view.dev, line->string + 1, 0, nr);
- cp = line->string + line->len - 4;
+ raw3270_buffer_address(tp->view.dev, line + 1, 0, nr);
+ cp = line + len - 4;
if (*cp == TO_RA)
raw3270_buffer_address(tp->view.dev, cp + 1, 0, nr + 1);
}
/*
- * Rebuild update list to print all lines.
- */
-static void tty3270_rebuild_update(struct tty3270 *tp)
-{
- struct string *s, *n;
- int line, nr_up;
-
- /*
- * Throw away update list and create a new one,
- * containing all lines that will fit on the screen.
- */
- list_for_each_entry_safe(s, n, &tp->update, update)
- list_del_init(&s->update);
- line = tty3270_tty_rows(tp) - 1;
- nr_up = tp->nr_up;
- list_for_each_entry_reverse(s, &tp->lines, list) {
- if (nr_up > 0) {
- nr_up--;
- continue;
- }
- tty3270_update_string(tp, s, line);
- list_add(&s->update, &tp->update);
- if (--line < 0)
- break;
- }
- tp->update_flags |= TTY_UPDATE_LIST;
-}
-
-/*
* Alloc string for size bytes. If there is not enough room in
* freemem, free strings until there is room.
*/
if (s)
return s;
list_for_each_entry_safe(s, n, &tp->lines, list) {
- BUG_ON(tp->nr_lines <= tty3270_tty_rows(tp));
list_del(&s->list);
- if (!list_empty(&s->update))
- list_del(&s->update);
- tp->nr_lines--;
if (free_string(&tp->freemem, s) >= size)
break;
}
- s = alloc_string(&tp->freemem, size);
- BUG_ON(!s);
- if (tp->nr_up != 0 &&
- tp->nr_up + tty3270_tty_rows(tp) >= tp->nr_lines) {
- tp->nr_up = tp->nr_lines - tp->view.rows + TTY3270_INPUT_AREA_ROWS;
- tty3270_rebuild_update(tp);
- tty3270_update_status(tp);
- }
- return s;
-}
-
-/*
- * Add an empty line to the list.
- */
-static void tty3270_blank_line(struct tty3270 *tp)
-{
- static const unsigned char blueprint[] = {
- TO_SBA, 0, 0, TO_SA, TAT_EXTHI, TAX_RESET,
- TO_SA, TAT_FGCOLOR, TAC_RESET, TO_RA, 0, 0, 0,
- };
- struct string *s;
-
- s = tty3270_alloc_string(tp, sizeof(blueprint));
- memcpy(s->string, blueprint, sizeof(blueprint));
- s->len = sizeof(blueprint);
- list_add_tail(&s->list, &tp->lines);
- tp->nr_lines++;
- if (tp->nr_up != 0)
- tp->nr_up++;
+ return alloc_string(&tp->freemem, size);
}
-/*
- * Create a blank screen and remove all lines from the history.
- */
static void tty3270_blank_screen(struct tty3270 *tp)
{
- struct string *s, *n;
+ struct tty3270_line *line;
int i;
- for (i = 0; i < tty3270_tty_rows(tp); i++)
- tp->screen[i].len = 0;
- tp->nr_up = 0;
- list_for_each_entry_safe(s, n, &tp->lines, list) {
- list_del(&s->list);
- if (!list_empty(&s->update))
- list_del(&s->update);
- tp->nr_lines--;
- free_string(&tp->freemem, s);
+ for (i = 0; i < tty3270_tty_rows(tp); i++) {
+ line = tty3270_get_write_line(tp, i);
+ line->len = 0;
+ line->dirty = 1;
}
+ tp->nr_up = 0;
}
/*
xchg(&tp->write, rq);
}
-static int tty3270_required_length(struct tty3270 *tp, int line_nr)
+static int tty3270_required_length(struct tty3270 *tp, struct tty3270_line *line)
{
unsigned char f_color, b_color, highlight;
- struct tty3270_line *line;
struct tty3270_cell *cell;
int i, flen = 3; /* Prefix (TO_SBA). */
- line = tp->screen + line_nr;
flen += line->len;
highlight = TAX_RESET;
f_color = TAC_RESET;
attr->b_color = TAC_RESET;
}
-static struct string *tty3270_resize_line(struct tty3270 *tp, struct string *s, int newlen)
-{
- struct string *n = tty3270_alloc_string(tp, newlen);
-
- list_add(&n->list, &s->list);
- list_del_init(&s->list);
- if (!list_empty(&s->update))
- list_del_init(&s->update);
- free_string(&tp->freemem, s);
- return n;
-}
-
/*
* Convert a tty3270_line to a 3270 data fragment usable for output.
*/
-static void tty3270_convert_line(struct tty3270 *tp, int line_nr)
+static unsigned int tty3270_convert_line(struct tty3270 *tp, struct tty3270_line *line)
{
- struct tty3270_line *line = tp->screen + line_nr;
struct tty3270_attribute attr;
- struct string *s;
- int flen, i;
+ int flen;
char *cp;
/* Determine how long the fragment will be. */
- flen = tty3270_required_length(tp, line_nr);
- /* Find the line in the list. */
- i = tty3270_tty_rows(tp) - line_nr;
- list_for_each_entry_reverse(s, &tp->lines, list)
- if (--i <= 0)
- break;
- /*
- * Check if the line needs to get reallocated.
- */
- if (s->len != flen)
- s = tty3270_resize_line(tp, s, flen);
-
+ flen = tty3270_required_length(tp, line);
+ if (flen > PAGE_SIZE)
+ return 0;
/* Write 3270 data fragment. */
tty3270_reset_attributes(&attr);
- cp = tty3270_add_attributes(tp, line, &attr, s->string);
+ cp = tty3270_add_attributes(tp, line, &attr, tp->converted_line);
cp = tty3270_add_reset_attributes(tp, line, cp, &attr);
- if (tp->nr_up + line_nr < tty3270_tty_rows(tp)) {
- /* Line is currently visible on screen. */
- tty3270_update_string(tp, s, line_nr);
- /* Add line to update list. */
- if (list_empty(&s->update)) {
- list_add_tail(&s->update, &tp->update);
- tp->update_flags |= TTY_UPDATE_LIST;
- }
- }
+ return cp - (char *)tp->converted_line;
}
/*
static void tty3270_update(struct timer_list *t)
{
struct tty3270 *tp = from_timer(tp, t, timer);
- static char invalid_sba[2] = { 0xff, 0xff };
struct raw3270_request *wrq;
+ struct tty3270_line *line;
unsigned long updated;
- struct string *s, *n;
- char *sba, *str;
- int rc, len;
+ int i, rc, len;
wrq = xchg(&tp->write, 0);
if (!wrq) {
spin_lock_irq(&tp->view.lock);
updated = 0;
if (tp->update_flags & TTY_UPDATE_ALL) {
- tty3270_rebuild_update(tp);
- tp->update_flags = TTY_UPDATE_ERASE | TTY_UPDATE_LIST |
+ tp->update_flags = TTY_UPDATE_ERASE |
TTY_UPDATE_INPUT | TTY_UPDATE_STATUS;
}
if (tp->update_flags & TTY_UPDATE_ERASE) {
tp->prompt->len) == 0)
updated |= TTY_UPDATE_INPUT;
- sba = invalid_sba;
-
- if (tp->update_flags & TTY_UPDATE_LIST) {
- /* Write strings in the update list to the screen. */
- list_for_each_entry_safe(s, n, &tp->update, update) {
- str = s->string;
- len = s->len;
- /*
- * Skip TO_SBA at the start of the string if the
- * last output position matches the start address
- * of this line.
- */
- if (s->string[1] == sba[0] && s->string[2] == sba[1]) {
- str += 3;
- len -= 3;
- }
- if (raw3270_request_add_data(wrq, str, len) != 0)
- break;
- list_del_init(&s->update);
- if (s->string[s->len - 4] == TO_RA)
- sba = s->string + s->len - 3;
- else
- sba = invalid_sba;
- }
- if (list_empty(&tp->update))
- updated |= TTY_UPDATE_LIST;
+ for (i = 0; i < tty3270_tty_rows(tp); i++) {
+ line = tty3270_get_view_line(tp, i);
+ if (!line->dirty)
+ continue;
+ len = tty3270_convert_line(tp, line);
+ tty3270_update_string(tp, tp->converted_line, len, i);
+ if (raw3270_request_add_data(wrq, tp->converted_line, len))
+ break;
+ line->dirty = 0;
}
+
+ if (i < tty3270_tty_rows(tp) - 1)
+ tty3270_set_timer(tp, 1);
+
wrq->callback = tty3270_write_callback;
rc = raw3270_start(&tp->view, wrq);
if (rc == 0) {
tp->rcl_nr--;
}
s = tty3270_alloc_string(tp, len);
+ if (!s)
+ return;
memcpy(s->string, input, len);
list_add_tail(&s->list, &tp->rcl_lines);
tp->rcl_nr++;
raw3270_deactivate_view(&tp->view);
}
+static void tty3270_redraw(struct tty3270 *tp)
+{
+ int i;
+
+ for (i = 0; i < tty3270_tty_rows(tp); i++)
+ tty3270_get_view_line(tp, i)->dirty = 1;
+ tp->update_flags = TTY_UPDATE_ALL;
+ tty3270_set_timer(tp, 1);
+}
/*
* Scroll forward in history.
*/
static void tty3270_scroll_forward(struct kbd_data *kbd)
{
struct tty3270 *tp = container_of(kbd->port, struct tty3270, port);
- int nr_up;
spin_lock_irq(&tp->view.lock);
- nr_up = tp->nr_up - tp->view.rows + TTY3270_INPUT_AREA_ROWS;
- if (nr_up < 0)
- nr_up = 0;
- if (nr_up != tp->nr_up) {
- tp->nr_up = nr_up;
- tty3270_rebuild_update(tp);
- tp->update_flags |= TTY_UPDATE_STATUS;
- tty3270_set_timer(tp, 1);
- }
+
+ if (tp->nr_up >= tty3270_tty_rows(tp))
+ tp->nr_up -= tty3270_tty_rows(tp) / 2;
+ else
+ tp->nr_up = 0;
+ tty3270_redraw(tp);
spin_unlock_irq(&tp->view.lock);
}
static void tty3270_scroll_backward(struct kbd_data *kbd)
{
struct tty3270 *tp = container_of(kbd->port, struct tty3270, port);
- int nr_up;
spin_lock_irq(&tp->view.lock);
- nr_up = tp->nr_up + tty3270_tty_rows(tp);
- if (nr_up + tty3270_tty_rows(tp) > tp->nr_lines)
- nr_up = tp->nr_lines - tp->view.rows + TTY3270_INPUT_AREA_ROWS;
- if (nr_up != tp->nr_up) {
- tp->nr_up = nr_up;
- tty3270_rebuild_update(tp);
- tty3270_update_status(tp);
- }
+ tp->nr_up += tty3270_tty_rows(tp) / 2;
+ if (tp->nr_up > tp->allocated_lines - tty3270_tty_rows(tp))
+ tp->nr_up = tp->allocated_lines - tty3270_tty_rows(tp);
+ tty3270_redraw(tp);
spin_unlock_irq(&tp->view.lock);
}
len = tp->input->len - 6 - rrq->rescnt;
if (tp->inattr != TF_INPUTN)
tty3270_rcl_add(tp, input, len);
- if (tp->nr_up > 0) {
+ if (tp->nr_up > 0)
tp->nr_up = 0;
- tty3270_rebuild_update(tp);
- tty3270_update_status(tp);
- }
/* Clear input area. */
tty3270_update_prompt(tp, NULL, 0);
tty3270_set_timer(tp, 1);
goto out_tp;
INIT_LIST_HEAD(&tp->freemem);
INIT_LIST_HEAD(&tp->lines);
- INIT_LIST_HEAD(&tp->update);
INIT_LIST_HEAD(&tp->rcl_lines);
tp->rcl_max = 20;
for (pages = 0; pages < TTY3270_STRING_PAGES; pages++)
free_pages((unsigned long) tp->freemem_pages[pages], 0);
kfree(tp->freemem_pages);
+ free_page((unsigned long)tp->converted_line);
tty_port_destroy(&tp->port);
kfree(tp);
}
old_cols == new_cols &&
old_rows == new_rows) {
spin_lock_irq(&tp->view.lock);
- tp->update_flags = TTY_UPDATE_ALL;
- tty3270_set_timer(tp, 1);
+ tty3270_redraw(tp);
spin_unlock_irq(&tp->view.lock);
return;
}
free_string(&tp->freemem, tp->prompt);
tty3270_create_prompt(tp);
- while (tp->nr_lines < tty3270_tty_rows(tp))
- tty3270_blank_line(tp);
tp->update_flags = TTY_UPDATE_ALL;
spin_unlock_irq(&tp->view.lock);
tty3270_free_screen(oscreen, old_allocated);
tty3270_create_view(int index, struct tty3270 **newtp)
{
struct tty3270 *tp;
- int i, rc;
+ int rc;
if (tty3270_max_index < index + 1)
tty3270_max_index = index + 1;
tty3270_create_prompt(tp);
/* Create blank line for every line in the tty output area. */
- for (i = 0; i < tty3270_tty_rows(tp); i++)
- tty3270_blank_line(tp);
+ tty3270_blank_screen(tp);
tp->kbd->port = &tp->port;
tp->kbd->fn_handler[KVAL(K_INCRCONSOLE)] = tty3270_exit_tty;
struct tty3270_line *line;
struct tty3270_cell *cell;
- line = tp->screen + tp->cy;
+ line = tty3270_get_write_line(tp, tp->cy);
if (line->len <= tp->cx) {
while (line->len < tp->cx) {
cell = line->cells + line->len;
cell = line->cells + tp->cx;
cell->character = ch;
cell->attributes = tp->attributes;
+ line->dirty = 1;
}
/*
*/
static void tty3270_lf(struct tty3270 *tp)
{
- struct tty3270_line temp;
+ struct tty3270_line *line;
int i;
- tty3270_convert_line(tp, tp->cy);
if (tp->cy < tty3270_tty_rows(tp) - 1) {
tp->cy++;
- return;
+ } else {
+ tp->line_view_start = tty3270_line_increment(tp, tp->line_view_start, 1);
+ tp->line_write_start = tty3270_line_increment(tp, tp->line_write_start, 1);
+ for (i = 0; i < tty3270_tty_rows(tp); i++)
+ tty3270_get_view_line(tp, i)->dirty = 1;
}
- /* Last line just filled up. Add new, blank line. */
- tty3270_blank_line(tp);
- temp = tp->screen[0];
- temp.len = 0;
- for (i = 0; i < tty3270_tty_rows(tp) - 1; i++)
- tp->screen[i] = tp->screen[i+1];
- tp->screen[tty3270_tty_rows(tp) - 1] = temp;
- tty3270_rebuild_update(tp);
+
+ line = tty3270_get_write_line(tp, tp->cy);
+ line->len = 0;
+ line->dirty = 1;
}
static void tty3270_ri(struct tty3270 *tp)
{
- if (tp->cy > 0) {
- tty3270_convert_line(tp, tp->cy);
- tp->cy--;
- }
+ if (tp->cy > 0)
+ tp->cy--;
}
static void tty3270_reset_cell(struct tty3270 *tp, struct tty3270_cell *cell)
struct tty3270_line *line;
int k;
- line = tp->screen + tp->cy;
+ line = tty3270_get_write_line(tp, tp->cy);
while (line->len < tp->cx)
tty3270_reset_cell(tp, &line->cells[line->len++]);
if (n > tp->view.cols - tp->cx)
struct tty3270_line *line;
int i;
- line = tp->screen + tp->cy;
+ line = tty3270_get_write_line(tp, tp->cy);
if (line->len <= tp->cx)
return;
if (line->len - tp->cx <= n) {
struct tty3270_line *line;
struct tty3270_cell *cell;
- line = tp->screen + tp->cy;
+ line = tty3270_get_write_line(tp, tp->cy);
while (line->len > tp->cx && n-- > 0) {
cell = line->cells + tp->cx++;
tty3270_reset_cell(tp, cell);
struct tty3270_cell *cell;
int i, start, end;
- line = tp->screen + tp->cy;
+ line = tty3270_get_write_line(tp, tp->cy);
switch (mode) {
case 0:
if (line->len <= end)
line->len = end;
-
- tty3270_convert_line(tp, tp->cy);
}
/*
*/
static void tty3270_erase_display(struct tty3270 *tp, int mode)
{
+ struct tty3270_line *line;
int i, start, end;
switch (mode) {
return;
}
for (i = start; i < end; i++) {
- tp->screen[i].len = 0;
- tty3270_convert_line(tp, i);
+ line = tty3270_get_write_line(tp, i);
+ line->len = 0;
+ line->dirty = 1;
}
- tty3270_rebuild_update(tp);
}
/*
int max_cy = max(0, cy);
tp->cx = min_t(int, tp->view.cols - 1, max_cx);
- line = tp->screen + tp->cy;
+ line = tty3270_get_write_line(tp, tp->cy);
while (line->len < tp->cx) {
cell = line->cells + line->len;
cell->character = ' ';
cell->attributes = tp->attributes;
line->len++;
}
-
- cy = min_t(int, tty3270_tty_rows(tp) - 1, max_cy);
- if (cy != tp->cy) {
- tty3270_convert_line(tp, tp->cy);
- tp->cy = cy;
- }
+ tp->cy = min_t(int, tty3270_tty_rows(tp) - 1, max_cy);
}
/*
tp->saved_attributes = tp->attributes;
break;
case '8': /* Restore cursor position. */
- tty3270_convert_line(tp, tp->cy);
tty3270_goto_xy(tp, tp->saved_cx, tp->saved_cy);
tp->attributes = tp->saved_attributes;
break;
tp->saved_attributes = tp->attributes;
break;
case 'u': /* Restore cursor position. */
- tty3270_convert_line(tp, tp->cy);
tty3270_goto_xy(tp, tp->saved_cx, tp->saved_cy);
tp->attributes = tp->saved_attributes;
break;
break;
}
}
- /* Convert current line to 3270 data fragment. */
- tty3270_convert_line(tp, tp->cy);
-
/* Setup timer to update display after 1/10 second */
if (!timer_pending(&tp->timer))
tty3270_set_timer(tp, HZ/10);
tty3270_reset_attributes(&tp->attributes);
tty3270_reset_attributes(&tp->saved_attributes);
tty3270_blank_screen(tp);
- while (tp->nr_lines < tty3270_tty_rows(tp))
- tty3270_blank_line(tp);
tp->update_flags = TTY_UPDATE_ALL;
spin_unlock_irq(&tp->view.lock);
tty3270_set_timer(tp, 1);