From: Martin Schwidefsky New 3270 device driver. --- /dev/null | 3854 -------------------------------- 25-akpm/Documentation/devices.txt | 12 25-akpm/arch/s390/defconfig | 3 25-akpm/drivers/s390/char/Makefile | 12 25-akpm/drivers/s390/char/con3270.c | 639 +++++ 25-akpm/drivers/s390/char/defkeymap.c | 156 + 25-akpm/drivers/s390/char/defkeymap.map | 191 + 25-akpm/drivers/s390/char/fs3270.c | 373 +++ 25-akpm/drivers/s390/char/keyboard.c | 516 ++++ 25-akpm/drivers/s390/char/keyboard.h | 57 25-akpm/drivers/s390/char/raw3270.c | 1256 ++++++++++ 25-akpm/drivers/s390/char/raw3270.h | 273 ++ 25-akpm/drivers/s390/char/tty3270.c | 1828 +++++++++++++++ 13 files changed, 5304 insertions(+), 3866 deletions(-) diff -puN arch/s390/defconfig~s390-08-new-3270-driver arch/s390/defconfig --- 25/arch/s390/defconfig~s390-08-new-3270-driver Thu Jan 8 14:11:35 2004 +++ 25-akpm/arch/s390/defconfig Thu Jan 8 14:11:35 2004 @@ -159,7 +159,8 @@ CONFIG_UNIX98_PTY_COUNT=2048 # # S/390 character device drivers # -# CONFIG_TN3270 is not set +CONFIG_TN3270=y +CONFIG_TN3270_CONSOLE=y CONFIG_TN3215=y CONFIG_TN3215_CONSOLE=y CONFIG_CCW_CONSOLE=y diff -puN Documentation/devices.txt~s390-08-new-3270-driver Documentation/devices.txt --- 25/Documentation/devices.txt~s390-08-new-3270-driver Thu Jan 8 14:11:35 2004 +++ 25-akpm/Documentation/devices.txt Thu Jan 8 14:11:35 2004 @@ -2526,17 +2526,17 @@ Your cooperation is appreciated. 1 = /dev/dri/card1 Second graphics card ... -227 char IBM 3270 terminal block-mode access +227 char IBM 3270 terminal Unix tty access + 1 = /dev/3270/tty1 First 3270 terminal + 2 = /dev/3270/tty2 Seconds 3270 terminal + ... + +228 char IBM 3270 terminal block-mode access 0 = /dev/3270/tub Controlling interface 1 = /dev/3270/tub1 First 3270 terminal 2 = /dev/3270/tub2 Second 3270 terminal ... -228 char IBM 3270 terminal Unix tty access - 1 = /dev/3270/tty1 First 3270 terminal - 2 = /dev/3270/tty2 Seconds 3270 terminal - ... - 229 char IBM iSeries virtual console 0 = /dev/iseries/vtty0 First console port 1 = /dev/iseries/vtty1 Second console port diff -puN /dev/null drivers/s390/char/con3270.c --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/drivers/s390/char/con3270.c Thu Jan 8 14:11:35 2004 @@ -0,0 +1,639 @@ +/* + * drivers/s390/char/con3270.c + * IBM/3270 Driver - console view. + * + * Author(s): + * Original 3270 Code for 2.4 written by Richard Hitt (UTS Global) + * Rewritten for 2.5 by Martin Schwidefsky + * -- Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "raw3270.h" +#include "ctrlchar.h" + +#define CON3270_OUTPUT_BUFFER_SIZE 1024 +#define CON3270_STRING_PAGES 4 + +static struct raw3270_fn con3270_fn; + +/* + * Main 3270 console view data structure. + */ +struct con3270 { + struct raw3270_view view; + spinlock_t lock; + struct list_head freemem; /* list of free memory for strings. */ + + /* Output stuff. */ + struct list_head lines; /* list of lines. */ + struct list_head update; /* list of lines to update. */ + int line_nr; /* line number for next update. */ + int nr_lines; /* # lines in list. */ + int nr_up; /* # lines up in history. */ + unsigned long update_flags; /* Update indication bits. */ + struct string *cline; /* current output line. */ + struct string *status; /* last line of display. */ + struct raw3270_request *write; /* single write request. */ + struct timer_list timer; + + /* Input stuff. */ + struct string *input; /* input string for read request. */ + struct raw3270_request *read; /* single read request. */ + struct raw3270_request *kreset; /* single keyboard reset request. */ + struct tasklet_struct readlet; /* tasklet to issue read request. */ +}; + +static struct con3270 *condev; + +/* con3270->update_flags. See con3270_update for details. */ +#define CON_UPDATE_ERASE 1 /* Use EWRITEA instead of WRITE. */ +#define CON_UPDATE_LIST 2 /* Update lines in tty3270->update. */ +#define CON_UPDATE_STATUS 4 /* Update status line. */ +#define CON_UPDATE_ALL 7 + +static void con3270_update(struct con3270 *); + +/* + * Setup timeout for a device. On timeout trigger an update. + */ +void +con3270_set_timer(struct con3270 *cp, int expires) +{ + if (expires == 0) { + if (timer_pending(&cp->timer)) + del_timer(&cp->timer); + return; + } + if (timer_pending(&cp->timer)) { + if (mod_timer(&cp->timer, jiffies + expires)) + return; + } + cp->timer.function = (void (*)(unsigned long)) con3270_update; + cp->timer.data = (unsigned long) cp; + cp->timer.expires = jiffies + expires; + add_timer(&cp->timer); +} + +/* + * The status line is the last line of the screen. It shows the string + * "console view" in the lower left corner and "Running"/"More..."/"Holding" + * in the lower right corner of the screen. + */ +static void +con3270_update_status(struct con3270 *cp) +{ + char *str; + + str = (cp->nr_up != 0) ? "History" : "Running"; + memcpy(cp->status->string + 24, str, 7); + codepage_convert(cp->view.ascebc, cp->status->string + 24, 7); + cp->update_flags |= CON_UPDATE_STATUS; +} + +static void +con3270_create_status(struct con3270 *cp) +{ + static const unsigned char blueprint[] = + { TO_SBA, 0, 0, TO_SF,TF_LOG,TO_SA,TAT_COLOR, TAC_GREEN, + 'c','o','n','s','o','l','e',' ','v','i','e','w', + TO_RA,0,0,0,'R','u','n','n','i','n','g',TO_SF,TF_LOG }; + + cp->status = alloc_string(&cp->freemem, sizeof(blueprint)); + /* Copy blueprint to status line */ + memcpy(cp->status->string, blueprint, sizeof(blueprint)); + /* Set TO_RA addresses. */ + raw3270_buffer_address(cp->view.dev, cp->status->string + 1, + cp->view.cols * (cp->view.rows - 1)); + raw3270_buffer_address(cp->view.dev, cp->status->string + 21, + cp->view.cols * cp->view.rows - 8); + /* Convert strings to ebcdic. */ + codepage_convert(cp->view.ascebc, cp->status->string + 8, 12); + codepage_convert(cp->view.ascebc, cp->status->string + 24, 7); +} + +/* + * Set output offsets to 3270 datastream fragment of a console string. + */ +static void +con3270_update_string(struct con3270 *cp, struct string *s, int nr) +{ + if (s->len >= cp->view.cols - 5) + return; + raw3270_buffer_address(cp->view.dev, s->string + s->len - 3, + cp->view.cols * (nr + 1)); +} + +/* + * Rebuild update list to print all lines. + */ +static void +con3270_rebuild_update(struct con3270 *cp) +{ + struct string *s, *n; + int nr; + + /* + * 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, &cp->update, update) + list_del_init(&s->update); + nr = cp->view.rows - 2 + cp->nr_up; + list_for_each_entry_reverse(s, &cp->lines, list) { + if (nr < cp->view.rows - 1) + list_add(&s->update, &cp->update); + if (--nr < 0) + break; + } + cp->line_nr = 0; + cp->update_flags |= CON_UPDATE_LIST; +} + +/* + * Alloc string for size bytes. Free strings from history if necessary. + */ +static struct string * +con3270_alloc_string(struct con3270 *cp, size_t size) +{ + struct string *s, *n; + + s = alloc_string(&cp->freemem, size); + if (s) + return s; + list_for_each_entry_safe(s, n, &cp->lines, list) { + list_del(&s->list); + if (!list_empty(&s->update)) + list_del(&s->update); + cp->nr_lines--; + if (free_string(&cp->freemem, s) >= size) + break; + } + s = alloc_string(&cp->freemem, size); + BUG_ON(!s); + if (cp->nr_up != 0 && cp->nr_up + cp->view.rows > cp->nr_lines) { + cp->nr_up = cp->nr_lines - cp->view.rows + 1; + con3270_rebuild_update(cp); + con3270_update_status(cp); + } + return s; +} + +/* + * Write completion callback. + */ +static void +con3270_write_callback(struct raw3270_request *rq, void *data) +{ + raw3270_request_reset(rq); + xchg(&((struct con3270 *) rq->view)->write, rq); +} + +/* + * Update console display. + */ +static void +con3270_update(struct con3270 *cp) +{ + struct raw3270_request *wrq; + char wcc, prolog[6]; + unsigned long flags; + unsigned long updated; + struct string *s, *n; + int rc; + + wrq = xchg(&cp->write, 0); + if (!wrq) { + con3270_set_timer(cp, 1); + return; + } + + spin_lock_irqsave(&cp->view.lock, flags); + updated = 0; + if (cp->update_flags & CON_UPDATE_ERASE) { + /* Use erase write alternate to initialize display. */ + raw3270_request_set_cmd(wrq, TC_EWRITEA); + updated |= CON_UPDATE_ERASE; + } else + raw3270_request_set_cmd(wrq, TC_WRITE); + + wcc = TW_NONE; + raw3270_request_add_data(wrq, &wcc, 1); + + /* + * Update status line. + */ + if (cp->update_flags & CON_UPDATE_STATUS) + if (raw3270_request_add_data(wrq, cp->status->string, + cp->status->len) == 0) + updated |= CON_UPDATE_STATUS; + + if (cp->update_flags & CON_UPDATE_LIST) { + prolog[0] = TO_SBA; + prolog[3] = TO_SA; + prolog[4] = TAT_COLOR; + prolog[5] = TAC_TURQ; + raw3270_buffer_address(cp->view.dev, prolog + 1, + cp->view.cols * cp->line_nr); + raw3270_request_add_data(wrq, prolog, 6); + /* Write strings in the update list to the screen. */ + list_for_each_entry_safe(s, n, &cp->update, update) { + if (s != cp->cline) + con3270_update_string(cp, s, cp->line_nr); + if (raw3270_request_add_data(wrq, s->string, + s->len) != 0) + break; + list_del_init(&s->update); + if (s != cp->cline) + cp->line_nr++; + } + if (list_empty(&cp->update)) + updated |= CON_UPDATE_LIST; + } + wrq->callback = con3270_write_callback; + rc = raw3270_start(&cp->view, wrq); + if (rc == 0) { + cp->update_flags &= ~updated; + if (cp->update_flags) + con3270_set_timer(cp, 1); + } else { + raw3270_request_reset(wrq); + xchg(&cp->write, wrq); + } + spin_unlock_irqrestore(&cp->view.lock, flags); +} + +/* + * Read tasklet. + */ +static void +con3270_read_tasklet(struct raw3270_request *rrq) +{ + static char kreset_data = TW_KR; + struct con3270 *cp; + unsigned long flags; + int nr_up, deactivate; + + cp = (struct con3270 *) rrq->view; + spin_lock_irqsave(&cp->view.lock, flags); + nr_up = cp->nr_up; + deactivate = 0; + /* Check aid byte. */ + switch (cp->input->string[0]) { + case 0x7d: /* enter: jump to bottom. */ + nr_up = 0; + break; + case 0xf3: /* PF3: deactivate the console view. */ + deactivate = 1; + break; + case 0x6d: /* clear: start from scratch. */ + con3270_rebuild_update(cp); + cp->update_flags = CON_UPDATE_ALL; + con3270_set_timer(cp, 1); + break; + case 0xf7: /* PF7: do a page up in the console log. */ + nr_up += cp->view.rows - 2; + if (nr_up + cp->view.rows - 1 > cp->nr_lines) { + nr_up = cp->nr_lines - cp->view.rows + 1; + if (nr_up < 0) + nr_up = 0; + } + break; + case 0xf8: /* PF8: do a page down in the console log. */ + nr_up -= cp->view.rows - 2; + if (nr_up < 0) + nr_up = 0; + break; + } + if (nr_up != cp->nr_up) { + cp->nr_up = nr_up; + con3270_rebuild_update(cp); + con3270_update_status(cp); + con3270_set_timer(cp, 1); + } + spin_unlock_irqrestore(&cp->view.lock, flags); + + /* Start keyboard reset command. */ + raw3270_request_reset(cp->kreset); + raw3270_request_set_cmd(cp->kreset, TC_WRITE); + raw3270_request_add_data(cp->kreset, &kreset_data, 1); + raw3270_start(&cp->view, cp->kreset); + + if (deactivate) + raw3270_deactivate_view(&cp->view); + + raw3270_request_reset(rrq); + xchg(&cp->read, rrq); + raw3270_put_view(&cp->view); +} + +/* + * Read request completion callback. + */ +static void +con3270_read_callback(struct raw3270_request *rq, void *data) +{ + raw3270_get_view(rq->view); + /* Schedule tasklet to pass input to tty. */ + tasklet_schedule(&((struct con3270 *) rq->view)->readlet); +} + +/* + * Issue a read request. Called only from interrupt function. + */ +static void +con3270_issue_read(struct con3270 *cp) +{ + struct raw3270_request *rrq; + int rc; + + rrq = xchg(&cp->read, 0); + if (!rrq) + /* Read already scheduled. */ + return; + rrq->callback = con3270_read_callback; + rrq->callback_data = cp; + raw3270_request_set_cmd(rrq, TC_READMOD); + raw3270_request_set_data(rrq, cp->input->string, cp->input->len); + /* Issue the read modified request. */ + rc = raw3270_start_irq(&cp->view, rrq); + if (rc) + raw3270_request_reset(rrq); +} + +/* + * Switch to the console view. + */ +static int +con3270_activate(struct raw3270_view *view) +{ + unsigned long flags; + struct con3270 *cp; + + cp = (struct con3270 *) view; + spin_lock_irqsave(&cp->view.lock, flags); + cp->nr_up = 0; + con3270_rebuild_update(cp); + con3270_update_status(cp); + cp->update_flags = CON_UPDATE_ALL; + con3270_set_timer(cp, 1); + spin_unlock_irqrestore(&cp->view.lock, flags); + return 0; +} + +static void +con3270_deactivate(struct raw3270_view *view) +{ + unsigned long flags; + struct con3270 *cp; + + cp = (struct con3270 *) view; + spin_lock_irqsave(&cp->view.lock, flags); + del_timer(&cp->timer); + spin_unlock_irqrestore(&cp->view.lock, flags); +} + +static int +con3270_irq(struct con3270 *cp, struct raw3270_request *rq, struct irb *irb) +{ + /* Handle ATTN. Schedule tasklet to read aid. */ + if (irb->scsw.dstat & DEV_STAT_ATTENTION) + con3270_issue_read(cp); + + if (rq) { + if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK) + rq->rc = -EIO; + else + /* Normal end. Copy residual count. */ + rq->rescnt = irb->scsw.count; + } + return RAW3270_IO_DONE; +} + +/* Console view to a 3270 device. */ +static struct raw3270_fn con3270_fn = { + .activate = con3270_activate, + .deactivate = con3270_deactivate, + .intv = (void *) con3270_irq +}; + +static inline void +con3270_cline_add(struct con3270 *cp) +{ + if (!list_empty(&cp->cline->list)) + /* Already added. */ + return; + list_add_tail(&cp->cline->list, &cp->lines); + cp->nr_lines++; + con3270_rebuild_update(cp); +} + +static inline void +con3270_cline_insert(struct con3270 *cp, unsigned char c) +{ + cp->cline->string[cp->cline->len++] = + cp->view.ascebc[(c < ' ') ? ' ' : c]; + if (list_empty(&cp->cline->update)) { + list_add_tail(&cp->cline->update, &cp->update); + cp->update_flags |= CON_UPDATE_LIST; + } +} + +static inline void +con3270_cline_end(struct con3270 *cp) +{ + struct string *s; + unsigned int size; + + /* Copy cline. */ + size = (cp->cline->len < cp->view.cols - 5) ? + cp->cline->len + 4 : cp->view.cols; + s = con3270_alloc_string(cp, size); + memcpy(s->string, cp->cline->string, cp->cline->len); + if (s->len < cp->view.cols - 5) { + s->string[s->len - 4] = TO_RA; + s->string[s->len - 1] = 0; + } else { + while (--size > cp->cline->len) + s->string[size] = cp->view.ascebc[' ']; + } + /* Replace cline with allocated line s and reset cline. */ + list_add(&s->list, &cp->cline->list); + list_del_init(&cp->cline->list); + if (!list_empty(&cp->cline->update)) { + list_add(&s->update, &cp->cline->update); + list_del_init(&cp->cline->update); + } + cp->cline->len = 0; +} + +/* + * Write a string to the 3270 console + */ +static void +con3270_write(struct console *co, const char *str, unsigned int count) +{ + struct con3270 *cp; + unsigned long flags; + unsigned char c; + + cp = condev; + if (cp->view.dev) + raw3270_activate_view(&cp->view); + spin_lock_irqsave(&cp->view.lock, flags); + while (count-- > 0) { + c = *str++; + if (cp->cline->len == 0) + con3270_cline_add(cp); + if (c != '\n') + con3270_cline_insert(cp, c); + if (c == '\n' || cp->cline->len >= cp->view.cols) + con3270_cline_end(cp); + } + /* Setup timer to output current console buffer after 1/10 second */ + if (cp->view.dev && !timer_pending(&cp->timer)) + con3270_set_timer(cp, HZ/10); + spin_unlock_irqrestore(&cp->view.lock,flags); +} + +extern struct tty_driver *tty3270_driver; + +static struct tty_driver * +con3270_device(struct console *c, int *index) +{ + *index = c->index; + return tty3270_driver; +} + +/* + * Wait for end of write request. + */ +static void +con3270_wait_write(struct con3270 *cp) +{ + while (!cp->write) { + raw3270_wait_cons_dev(cp->view.dev); + barrier(); + } +} + +/* + * panic() calls console_unblank before the system enters a + * disabled, endless loop. + */ +static void +con3270_unblank(void) +{ + struct con3270 *cp; + unsigned long flags; + + cp = condev; + if (!cp->view.dev) + return; + spin_lock_irqsave(&cp->view.lock, flags); + con3270_wait_write(cp); + cp->nr_up = 0; + con3270_rebuild_update(cp); + con3270_update_status(cp); + while (cp->update_flags != 0) { + spin_unlock_irqrestore(&cp->view.lock, flags); + con3270_update(cp); + spin_lock_irqsave(&cp->view.lock, flags); + con3270_wait_write(cp); + } + spin_unlock_irqrestore(&cp->view.lock, flags); +} + +static int __init +con3270_consetup(struct console *co, char *options) +{ + return 0; +} + +/* + * The console structure for the 3270 console + */ +static struct console con3270 = { + .name = "tty3270", + .write = con3270_write, + .device = con3270_device, + .unblank = con3270_unblank, + .setup = con3270_consetup, + .flags = CON_PRINTBUFFER, +}; + +/* + * 3270 console initialization code called from console_init(). + * NOTE: This is called before kmalloc is available. + */ +static int __init +con3270_init(void) +{ + struct ccw_device *cdev; + struct raw3270 *rp; + void *cbuf; + int i; + + /* Check if 3270 is to be the console */ + if (!CONSOLE_IS_3270) + return -ENODEV; + + /* Set the console mode for VM */ + if (MACHINE_IS_VM) { + cpcmd("TERM CONMODE 3270", 0, 0); + cpcmd("TERM AUTOCR OFF", 0, 0); + } + + cdev = ccw_device_probe_console(); + if (!cdev) + return -ENODEV; + rp = raw3270_setup_console(cdev); + if (IS_ERR(rp)) + return PTR_ERR(rp); + + condev = (struct con3270 *) alloc_bootmem_low(sizeof(struct con3270)); + memset(condev, 0, sizeof(struct con3270)); + condev->view.dev = rp; + + condev->read = raw3270_request_alloc_bootmem(0); + condev->read->callback = con3270_read_callback; + condev->read->callback_data = condev; + condev->write = + raw3270_request_alloc_bootmem(CON3270_OUTPUT_BUFFER_SIZE); + condev->kreset = raw3270_request_alloc_bootmem(1); + + INIT_LIST_HEAD(&condev->lines); + INIT_LIST_HEAD(&condev->update); + init_timer(&condev->timer); + tasklet_init(&condev->readlet, + (void (*)(unsigned long)) con3270_read_tasklet, + (unsigned long) condev->read); + + raw3270_add_view(&condev->view, &con3270_fn, 0); + + INIT_LIST_HEAD(&condev->freemem); + for (i = 0; i < CON3270_STRING_PAGES; i++) { + cbuf = (void *) alloc_bootmem_low_pages(PAGE_SIZE); + add_string_memory(&condev->freemem, cbuf, PAGE_SIZE); + } + condev->cline = alloc_string(&condev->freemem, condev->view.cols); + condev->cline->len = 0; + con3270_create_status(condev); + condev->input = alloc_string(&condev->freemem, 80); + register_console(&con3270); + return 0; +} + +console_initcall(con3270_init); diff -puN /dev/null drivers/s390/char/defkeymap.c --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/drivers/s390/char/defkeymap.c Thu Jan 8 14:11:35 2004 @@ -0,0 +1,156 @@ + +/* Do not edit this file! It was automatically generated by */ +/* loadkeys --mktable defkeymap.map > defkeymap.c */ + +#include +#include +#include + +u_short plain_map[NR_KEYS] = { + 0xf000, 0xf000, 0xf000, 0xf000, 0xf000, 0xf000, 0xf000, 0xf000, + 0xf000, 0xf000, 0xf000, 0xf000, 0xf000, 0xf000, 0xf000, 0xf000, + 0xf000, 0xf000, 0xf000, 0xf000, 0xf000, 0xf000, 0xf000, 0xf000, + 0xf000, 0xf000, 0xf000, 0xf000, 0xf000, 0xf000, 0xf000, 0xf000, + 0xf000, 0xf000, 0xf000, 0xf000, 0xf000, 0xf000, 0xf000, 0xf000, + 0xf000, 0xf000, 0xf000, 0xf000, 0xf000, 0xf000, 0xf000, 0xf000, + 0xf000, 0xf000, 0xf000, 0xf000, 0xf000, 0xf000, 0xf000, 0xf000, + 0xf000, 0xf000, 0xf000, 0xf000, 0xf000, 0xf000, 0xf000, 0xf000, + 0xf020, 0xf000, 0xf0e2, 0xf0e4, 0xf0e0, 0xf0e1, 0xf0e3, 0xf0e5, + 0xf0e7, 0xf0f1, 0xf0a2, 0xf02e, 0xf03c, 0xf028, 0xf02b, 0xf07c, + 0xf026, 0xf0e9, 0xf0e2, 0xf0eb, 0xf0e8, 0xf0ed, 0xf0ee, 0xf0ef, + 0xf0ec, 0xf0df, 0xf021, 0xf024, 0xf02a, 0xf029, 0xf03b, 0xf0ac, + 0xf02d, 0xf02f, 0xf0c2, 0xf0c4, 0xf0c0, 0xf0c1, 0xf0c3, 0xf0c5, + 0xf0c7, 0xf0d1, 0xf0a6, 0xf02c, 0xf025, 0xf05f, 0xf03e, 0xf03f, + 0xf0f8, 0xf0c9, 0xf0ca, 0xf0cb, 0xf0c8, 0xf0cd, 0xf0ce, 0xf0cf, + 0xf0cc, 0xf060, 0xf03a, 0xf023, 0xf040, 0xf027, 0xf03d, 0xf022, +}; + +static u_short shift_map[NR_KEYS] = { + 0xf0d8, 0xf061, 0xf062, 0xf063, 0xf064, 0xf065, 0xf066, 0xf067, + 0xf068, 0xf069, 0xf0ab, 0xf0bb, 0xf0f0, 0xf0fd, 0xf0fe, 0xf0b1, + 0xf0b0, 0xf06a, 0xf06b, 0xf06c, 0xf06d, 0xf06e, 0xf06f, 0xf070, + 0xf071, 0xf072, 0xf000, 0xf000, 0xf0e6, 0xf0b8, 0xf0c6, 0xf0a4, + 0xf0b5, 0xf07e, 0xf073, 0xf074, 0xf075, 0xf076, 0xf077, 0xf078, + 0xf079, 0xf07a, 0xf0a1, 0xf0bf, 0xf0d0, 0xf0dd, 0xf0de, 0xf0ae, + 0xf402, 0xf0a3, 0xf0a5, 0xf0b7, 0xf0a9, 0xf0a7, 0xf0b6, 0xf0bc, + 0xf0bd, 0xf0be, 0xf05b, 0xf05d, 0xf000, 0xf0a8, 0xf0b4, 0xf0d7, + 0xf07b, 0xf041, 0xf042, 0xf043, 0xf044, 0xf045, 0xf046, 0xf047, + 0xf048, 0xf049, 0xf000, 0xf0f4, 0xf0f6, 0xf0f2, 0xf0f3, 0xf0f5, + 0xf07d, 0xf04a, 0xf04b, 0xf04c, 0xf04d, 0xf04e, 0xf04f, 0xf050, + 0xf051, 0xf052, 0xf0b9, 0xf0fb, 0xf0fc, 0xf0f9, 0xf0fa, 0xf0ff, + 0xf05c, 0xf0f7, 0xf053, 0xf054, 0xf055, 0xf056, 0xf057, 0xf058, + 0xf059, 0xf05a, 0xf0b2, 0xf0d4, 0xf0d6, 0xf0d2, 0xf0d3, 0xf0d5, + 0xf030, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, 0xf037, + 0xf038, 0xf039, 0xf0b3, 0xf0db, 0xf0dc, 0xf0d9, 0xf0da, 0xf000, +}; + +static u_short ctrl_map[NR_KEYS] = { + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf11f, 0xf120, 0xf121, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf01a, 0xf003, 0xf212, 0xf004, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf109, 0xf10a, 0xf206, 0xf00a, 0xf200, 0xf200, +}; + +static u_short shift_ctrl_map[NR_KEYS] = { + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf10c, 0xf10d, 0xf10e, 0xf10f, 0xf110, 0xf111, 0xf112, + 0xf113, 0xf11e, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf100, 0xf101, 0xf211, 0xf103, 0xf104, 0xf105, 0xf20b, + 0xf20a, 0xf108, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +ushort *key_maps[MAX_NR_KEYMAPS] = { + plain_map, shift_map, 0, 0, + ctrl_map, shift_ctrl_map, 0 +}; + +unsigned int keymap_count = 4; + + +/* + * Philosophy: most people do not define more strings, but they who do + * often want quite a lot of string space. So, we statically allocate + * the default and allocate dynamically in chunks of 512 bytes. + */ + +char func_buf[] = { + '\033', '[', '[', 'A', 0, + '\033', '[', '[', 'B', 0, + '\033', '[', '[', 'C', 0, + '\033', '[', '[', 'D', 0, + '\033', '[', '[', 'E', 0, + '\033', '[', '1', '7', '~', 0, + '\033', '[', '1', '8', '~', 0, + '\033', '[', '1', '9', '~', 0, + '\033', '[', '2', '0', '~', 0, + '\033', '[', '2', '1', '~', 0, + '\033', '[', '2', '3', '~', 0, + '\033', '[', '2', '4', '~', 0, + '\033', '[', '2', '5', '~', 0, + '\033', '[', '2', '6', '~', 0, + '\033', '[', '2', '8', '~', 0, + '\033', '[', '2', '9', '~', 0, + '\033', '[', '3', '1', '~', 0, + '\033', '[', '3', '2', '~', 0, + '\033', '[', '3', '3', '~', 0, + '\033', '[', '3', '4', '~', 0, +}; + + +char *funcbufptr = func_buf; +int funcbufsize = sizeof(func_buf); +int funcbufleft = 0; /* space left */ + +char *func_table[MAX_NR_FUNC] = { + func_buf + 0, + func_buf + 5, + func_buf + 10, + func_buf + 15, + func_buf + 20, + func_buf + 25, + func_buf + 31, + func_buf + 37, + func_buf + 43, + func_buf + 49, + func_buf + 55, + func_buf + 61, + func_buf + 67, + func_buf + 73, + func_buf + 79, + func_buf + 85, + func_buf + 91, + func_buf + 97, + func_buf + 103, + func_buf + 109, + 0, +}; + +struct kbdiacr accent_table[MAX_DIACR] = { + {'^', 'c', '\003'}, {'^', 'd', '\004'}, + {'^', 'z', '\032'}, {'^', '\012', '\000'}, +}; + +unsigned int accent_table_size = 4; diff -puN /dev/null drivers/s390/char/defkeymap.map --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/drivers/s390/char/defkeymap.map Thu Jan 8 14:11:35 2004 @@ -0,0 +1,191 @@ +# Default keymap for 3270 (ebcdic codepage 037). +keymaps 0-1,4-5 + +keycode 0 = nul Oslash +keycode 1 = nul a +keycode 2 = nul b +keycode 3 = nul c +keycode 4 = nul d +keycode 5 = nul e +keycode 6 = nul f +keycode 7 = nul g +keycode 8 = nul h +keycode 9 = nul i +keycode 10 = nul guillemotleft +keycode 11 = nul guillemotright +keycode 12 = nul eth +keycode 13 = nul yacute +keycode 14 = nul thorn +keycode 15 = nul plusminus +keycode 16 = nul degree +keycode 17 = nul j +keycode 18 = nul k +keycode 19 = nul l +keycode 20 = nul m +keycode 21 = nul n +keycode 22 = nul o +keycode 23 = nul p +keycode 24 = nul q +keycode 25 = nul r +keycode 26 = nul nul +keycode 27 = nul nul +keycode 28 = nul ae +keycode 29 = nul cedilla +keycode 30 = nul AE +keycode 31 = nul currency +keycode 32 = nul mu +keycode 33 = nul tilde +keycode 34 = nul s +keycode 35 = nul t +keycode 36 = nul u +keycode 37 = nul v +keycode 38 = nul w +keycode 39 = nul x +keycode 40 = nul y +keycode 41 = nul z +keycode 42 = nul exclamdown +keycode 43 = nul questiondown +keycode 44 = nul ETH +keycode 45 = nul Yacute +keycode 46 = nul THORN +keycode 47 = nul registered +keycode 48 = nul dead_circumflex +keycode 49 = nul sterling +keycode 50 = nul yen +keycode 51 = nul periodcentered +keycode 52 = nul copyright +keycode 53 = nul section +keycode 54 = nul paragraph +keycode 55 = nul onequarter +keycode 56 = nul onehalf +keycode 57 = nul threequarters +keycode 58 = nul bracketleft +keycode 59 = nul bracketright +keycode 60 = nul nul +keycode 61 = nul diaeresis +keycode 62 = nul acute +keycode 63 = nul multiply +keycode 64 = space braceleft +keycode 65 = nul A +keycode 66 = acircumflex B +keycode 67 = adiaeresis C +keycode 68 = agrave D +keycode 69 = aacute E +keycode 70 = atilde F +keycode 71 = aring G +keycode 72 = ccedilla H +keycode 73 = ntilde I +keycode 74 = cent nul +keycode 75 = period ocircumflex +keycode 76 = less odiaeresis +keycode 77 = parenleft ograve +keycode 78 = plus oacute +keycode 79 = bar otilde +keycode 80 = ampersand braceright +keycode 81 = eacute J +keycode 82 = acircumflex K +keycode 83 = ediaeresis L +keycode 84 = egrave M +keycode 85 = iacute N +keycode 86 = icircumflex O +keycode 87 = idiaeresis P +keycode 88 = igrave Q +keycode 89 = ssharp R +keycode 90 = exclam onesuperior +keycode 91 = dollar ucircumflex +keycode 92 = asterisk udiaeresis +keycode 93 = parenright ugrave +keycode 94 = semicolon uacute +keycode 95 = notsign ydiaeresis +keycode 96 = minus backslash +keycode 97 = slash division +keycode 98 = Acircumflex S +keycode 99 = Adiaeresis T +keycode 100 = Agrave U +keycode 101 = Aacute V +keycode 102 = Atilde W +keycode 103 = Aring X +keycode 104 = Ccedilla Y +keycode 105 = Ntilde Z +keycode 106 = brokenbar twosuperior +keycode 107 = comma Ocircumflex +keycode 108 = percent Odiaeresis +keycode 109 = underscore Ograve +keycode 110 = greater Oacute +keycode 111 = question Otilde +keycode 112 = oslash zero +keycode 113 = Eacute one +keycode 114 = Ecircumflex two +keycode 115 = Ediaeresis three +keycode 116 = Egrave four +keycode 117 = Iacute five +keycode 118 = Icircumflex six +keycode 119 = Idiaeresis seven +keycode 120 = Igrave eight +keycode 121 = grave nine +keycode 122 = colon threesuperior +keycode 123 = numbersign Ucircumflex +keycode 124 = at Udiaeresis +keycode 125 = apostrophe Ugrave +keycode 126 = equal Uacute +keycode 127 = quotedbl nul + +# AID keys +control keycode 74 = F22 +control keycode 75 = F23 +control keycode 76 = F24 +control keycode 107 = Control_z # PA3 +control keycode 108 = Control_c # PA1 +control keycode 109 = KeyboardSignal # Clear +control keycode 110 = Control_d # PA2 +control keycode 122 = F10 +control keycode 123 = F11 # F11 +control keycode 124 = Last_Console # F12 +control keycode 125 = Linefeed +shift control keycode 65 = F13 +shift control keycode 66 = F14 +shift control keycode 67 = F15 +shift control keycode 68 = F16 +shift control keycode 69 = F17 +shift control keycode 70 = F18 +shift control keycode 71 = F19 +shift control keycode 72 = F20 +shift control keycode 73 = F21 +shift control keycode 113 = F1 +shift control keycode 114 = F2 +shift control keycode 115 = Incr_Console +shift control keycode 116 = F4 +shift control keycode 117 = F5 +shift control keycode 118 = F6 +shift control keycode 119 = Scroll_Backward +shift control keycode 120 = Scroll_Forward +shift control keycode 121 = F9 + +string F1 = "\033[[A" +string F2 = "\033[[B" +string F3 = "\033[[C" +string F4 = "\033[[D" +string F5 = "\033[[E" +string F6 = "\033[17~" +string F7 = "\033[18~" +string F8 = "\033[19~" +string F9 = "\033[20~" +string F10 = "\033[21~" +string F11 = "\033[23~" +string F12 = "\033[24~" +string F13 = "\033[25~" +string F14 = "\033[26~" +string F15 = "\033[28~" +string F16 = "\033[29~" +string F17 = "\033[31~" +string F18 = "\033[32~" +string F19 = "\033[33~" +string F20 = "\033[34~" +# string F21 ?? +# string F22 ?? +# string F23 ?? +# string F24 ?? +compose '^' 'c' to Control_c +compose '^' 'd' to Control_d +compose '^' 'z' to Control_z +compose '^' '\012' to nul diff -puN /dev/null drivers/s390/char/fs3270.c --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/drivers/s390/char/fs3270.c Thu Jan 8 14:11:35 2004 @@ -0,0 +1,373 @@ +/* + * drivers/s390/char/fs3270.c + * IBM/3270 Driver - fullscreen driver. + * + * Author(s): + * Original 3270 Code for 2.4 written by Richard Hitt (UTS Global) + * Rewritten for 2.5/2.6 by Martin Schwidefsky + * -- Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "raw3270.h" +#include "ctrlchar.h" + +struct raw3270_fn fs3270_fn; + +struct fs3270 { + struct raw3270_view view; + pid_t fs_pid; /* Pid of controlling program. */ + int read_command; /* ccw command to use for reads. */ + int write_command; /* ccw command to use for writes. */ + int attention; /* Got attention. */ + struct raw3270_request *clear; /* single clear request. */ + wait_queue_head_t attn_wait; /* Attention wait queue. */ +}; + +static void +fs3270_wake_up(struct raw3270_request *rq, void *data) +{ + wake_up((wait_queue_head_t *) data); +} + +static int +fs3270_do_io(struct raw3270_view *view, struct raw3270_request *rq) +{ + wait_queue_head_t wq; + int rc; + + init_waitqueue_head(&wq); + rq->callback = fs3270_wake_up; + rq->callback_data = &wq; + rc = raw3270_start(view, rq); + if (rc) + return rc; + /* Started sucessfully. Now wait for completion. */ + wait_event(wq, raw3270_request_final(rq)); + return rq->rc; +} + +static void +fs3270_reset_callback(struct raw3270_request *rq, void *data) +{ + raw3270_request_reset(rq); +} + +/* + * Switch to the fullscreen view. + */ +static int +fs3270_activate(struct raw3270_view *view) +{ + struct fs3270 *fp; + + fp = (struct fs3270 *) view; + raw3270_request_set_cmd(fp->clear, TC_EWRITEA); + fp->clear->callback = fs3270_reset_callback; + return raw3270_start(view, fp->clear); +} + +/* + * Shutdown fullscreen view. + */ +static void +fs3270_deactivate(struct raw3270_view *view) +{ + // FIXME: is this a good idea? The user program using fullscreen 3270 + // will die just because a console message appeared. On the other + // hand the fullscreen device is unoperational now. + struct fs3270 *fp; + + fp = (struct fs3270 *) view; + if (fp->fs_pid != 0) + kill_proc(fp->fs_pid, SIGHUP, 1); + fp->fs_pid = 0; +} + +static int +fs3270_irq(struct fs3270 *fp, struct raw3270_request *rq, struct irb *irb) +{ + /* Handle ATTN. Set indication and wake waiters for attention. */ + if (irb->scsw.dstat & DEV_STAT_ATTENTION) { + fp->attention = 1; + wake_up(&fp->attn_wait); + } + + if (rq) { + if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK) + rq->rc = -EIO; + else + /* Normal end. Copy residual count. */ + rq->rescnt = irb->scsw.count; + } + return RAW3270_IO_DONE; +} + +/* + * Process reads from fullscreen 3270. + */ +static ssize_t +fs3270_read(struct file *filp, char *data, size_t count, loff_t *off) +{ + struct fs3270 *fp; + struct raw3270_request *rq; + struct idal_buffer *ib; + int rc; + + if (count == 0 || count > 65535) + return -EINVAL; + fp = filp->private_data; + if (!fp) + return -ENODEV; + ib = idal_buffer_alloc(count, 0); + if (!ib) + return -ENOMEM; + rq = raw3270_request_alloc(0); + if (!IS_ERR(rq)) { + if (fp->read_command == 0 && fp->write_command != 0) + fp->read_command = 6; + raw3270_request_set_cmd(rq, fp->read_command ? : 2); + raw3270_request_set_idal(rq, ib); + wait_event(fp->attn_wait, fp->attention); + rc = fs3270_do_io(&fp->view, rq); + if (rc == 0 && idal_buffer_to_user(ib, data, count)) + rc = -EFAULT; + raw3270_request_free(rq); + } else + rc = PTR_ERR(rq); + idal_buffer_free(ib); + return rc; +} + +/* + * Process writes to fullscreen 3270. + */ +static ssize_t +fs3270_write(struct file *filp, const char *data, size_t count, loff_t *off) +{ + struct fs3270 *fp; + struct raw3270_request *rq; + struct idal_buffer *ib; + int write_command; + int rc; + + fp = filp->private_data; + if (!fp) + return -ENODEV; + ib = idal_buffer_alloc(count, 0); + if (!ib) + return -ENOMEM; + rq = raw3270_request_alloc(0); + if (!IS_ERR(rq)) { + if (idal_buffer_from_user(ib, data, count) == 0) { + write_command = fp->write_command ? : 1; + if (write_command == 5) + write_command = 13; + raw3270_request_set_cmd(rq, write_command); + raw3270_request_set_idal(rq, ib); + rc = fs3270_do_io(&fp->view, rq); + } else + rc = -EFAULT; + raw3270_request_free(rq); + } else + rc = PTR_ERR(rq); + idal_buffer_free(ib); + return rc; +} + +/* + * process ioctl commands for the tube driver + */ +static int +fs3270_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + struct fs3270 *fp; + struct raw3270_iocb iocb; + int rc; + + fp = filp->private_data; + if (!fp) + return -ENODEV; + rc = 0; + switch (cmd) { + case TUBICMD: + fp->read_command = arg; + break; + case TUBOCMD: + fp->write_command = arg; + break; + case TUBGETI: + rc = put_user(fp->read_command, (char *) arg); + break; + case TUBGETO: + rc = put_user(fp->write_command,(char *) arg); + break; + case TUBGETMOD: + iocb.model = fp->view.model; + iocb.line_cnt = fp->view.rows; + iocb.col_cnt = fp->view.cols; + iocb.pf_cnt = 24; + iocb.re_cnt = 20; + iocb.map = 0; + if (copy_to_user((char *) arg, &iocb, + sizeof(struct raw3270_iocb))) + rc = -EFAULT; + break; + } + return rc; +} + +/* + * Allocate tty3270 structure. + */ +static struct fs3270 * +fs3270_alloc_view(void) +{ + struct fs3270 *fp; + + fp = (struct fs3270 *) kmalloc(sizeof(struct fs3270),GFP_KERNEL); + if (!fp) + return ERR_PTR(-ENOMEM); + memset(fp, 0, sizeof(struct fs3270)); + fp->clear = raw3270_request_alloc(0); + if (!IS_ERR(fp->clear)) { + kfree(fp); + return ERR_PTR(-ENOMEM); + } + return fp; +} + +/* + * Free tty3270 structure. + */ +static void +fs3270_free_view(struct raw3270_view *view) +{ + raw3270_request_free(((struct fs3270 *) view)->clear); + kfree(view); +} + +/* + * Unlink fs3270 data structure from filp. + */ +static void +fs3270_release(struct raw3270_view *view) +{ +} + +/* View to a 3270 device. Can be console, tty or fullscreen. */ +struct raw3270_fn fs3270_fn = { + .activate = fs3270_activate, + .deactivate = fs3270_deactivate, + .intv = (void *) fs3270_irq, + .release = fs3270_release, + .free = fs3270_free_view +}; + +/* + * This routine is called whenever a 3270 fullscreen device is opened. + */ +static int +fs3270_open(struct inode *inode, struct file *filp) +{ + struct fs3270 *fp; + int minor, rc; + + if (imajor(filp->f_dentry->d_inode) != IBM_FS3270_MAJOR) + return -ENODEV; + minor = iminor(filp->f_dentry->d_inode); + /* Check if some other program is already using fullscreen mode. */ + fp = (struct fs3270 *) raw3270_find_view(&fs3270_fn, minor); + if (!IS_ERR(fp)) { + raw3270_put_view(&fp->view); + return -EBUSY; + } + /* Allocate fullscreen view structure. */ + fp = fs3270_alloc_view(); + if (IS_ERR(fp)) + return PTR_ERR(fp); + + init_waitqueue_head(&fp->attn_wait); + fp->fs_pid = current->pid; + rc = raw3270_add_view(&fp->view, &fs3270_fn, minor); + if (rc) { + fs3270_free_view(&fp->view); + return rc; + } + + rc = raw3270_activate_view(&fp->view); + if (rc) { + raw3270_del_view(&fp->view); + return rc; + } + filp->private_data = fp; + return 0; +} + +/* + * This routine is called when the 3270 tty is closed. We wait + * for the remaining request to be completed. Then we clean up. + */ +static int +fs3270_close(struct inode *inode, struct file *filp) +{ + struct fs3270 *fp; + + fp = filp->private_data; + filp->private_data = 0; + if (fp) + raw3270_del_view(&fp->view); + return 0; +} + +static struct file_operations fs3270_fops = { + .owner = THIS_MODULE, /* owner */ + .read = fs3270_read, /* read */ + .write = fs3270_write, /* write */ + .ioctl = fs3270_ioctl, /* ioctl */ + .open = fs3270_open, /* open */ + .release = fs3270_close, /* release */ +}; + +/* + * 3270 fullscreen driver initialization. + */ +static int __init +fs3270_init(void) +{ + int rc; + + rc = register_chrdev(IBM_FS3270_MAJOR, "fs3270", &fs3270_fops); + if (rc) { + printk(KERN_ERR "fs3270 can't get major number %d: errno %d\n", + IBM_FS3270_MAJOR, rc); + return rc; + } + return 0; +} + +static void __exit +fs3270_exit(void) +{ + unregister_chrdev(IBM_FS3270_MAJOR, "fs3270"); +} + +MODULE_LICENSE("GPL"); +MODULE_ALIAS_CHARDEV_MAJOR(IBM_FS3270_MAJOR); + +module_init(fs3270_init); +module_exit(fs3270_exit); diff -puN /dev/null drivers/s390/char/keyboard.c --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/drivers/s390/char/keyboard.c Thu Jan 8 14:11:35 2004 @@ -0,0 +1,516 @@ +/* + * drivers/s390/char/keyboard.c + * ebcdic keycode functions for s390 console drivers + * + * S390 version + * Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include "keyboard.h" + +/* + * Handler Tables. + */ +#define K_HANDLERS\ + k_self, k_fn, k_spec, k_ignore,\ + k_dead, k_ignore, k_ignore, k_ignore,\ + k_ignore, k_ignore, k_ignore, k_ignore,\ + k_ignore, k_ignore, k_ignore, k_ignore + +typedef void (k_handler_fn)(struct kbd_data *, unsigned char); +static k_handler_fn K_HANDLERS; +static k_handler_fn *k_handler[16] = { K_HANDLERS }; + +/* maximum values each key_handler can handle */ +static const int max_vals[] = { + 255, ARRAY_SIZE(func_table) - 1, NR_FN_HANDLER - 1, 0, + NR_DEAD - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; +static const int NR_TYPES = ARRAY_SIZE(max_vals); + +static unsigned char ret_diacr[NR_DEAD] = { + '`', '\'', '^', '~', '"', ',' +}; + +/* + * Alloc/free of kbd_data structures. + */ +struct kbd_data * +kbd_alloc(void) { + struct kbd_data *kbd; + int i, len; + + kbd = kmalloc(sizeof(struct kbd_data), GFP_KERNEL); + if (!kbd) + goto out; + memset(kbd, 0, sizeof(struct kbd_data)); + kbd->key_maps = kmalloc(sizeof(key_maps), GFP_KERNEL); + if (!key_maps) + goto out_kbd; + memset(kbd->key_maps, 0, sizeof(key_maps)); + for (i = 0; i < ARRAY_SIZE(key_maps); i++) { + if (key_maps[i]) { + kbd->key_maps[i] = + kmalloc(sizeof(u_short)*NR_KEYS, GFP_KERNEL); + if (!kbd->key_maps[i]) + goto out_maps; + memcpy(kbd->key_maps[i], key_maps[i], + sizeof(u_short)*NR_KEYS); + } + } + kbd->func_table = kmalloc(sizeof(func_table), GFP_KERNEL); + if (!kbd->func_table) + goto out_maps; + memset(kbd->func_table, 0, sizeof(func_table)); + for (i = 0; i < ARRAY_SIZE(func_table); i++) { + if (func_table[i]) { + len = strlen(func_table[i]) + 1; + kbd->func_table[i] = kmalloc(len, GFP_KERNEL); + if (!kbd->func_table[i]) + goto out_func; + memcpy(kbd->func_table[i], func_table[i], len); + } + } + kbd->fn_handler = + kmalloc(sizeof(fn_handler_fn *) * NR_FN_HANDLER, GFP_KERNEL); + if (!kbd->fn_handler) + goto out_func; + memset(kbd->fn_handler, 0, sizeof(fn_handler_fn *) * NR_FN_HANDLER); + kbd->accent_table = + kmalloc(sizeof(struct kbdiacr)*MAX_DIACR, GFP_KERNEL); + if (!kbd->accent_table) + goto out_fn_handler; + memcpy(kbd->accent_table, accent_table, + sizeof(struct kbdiacr)*MAX_DIACR); + kbd->accent_table_size = accent_table_size; + return kbd; + +out_fn_handler: + kfree(kbd->fn_handler); +out_func: + for (i = 0; i < ARRAY_SIZE(func_table); i++) + if (kbd->func_table[i]) + kfree(kbd->func_table[i]); + kfree(kbd->func_table); +out_maps: + for (i = 0; i < ARRAY_SIZE(key_maps); i++) + if (kbd->key_maps[i]) + kfree(kbd->key_maps[i]); + kfree(kbd->key_maps); +out_kbd: + kfree(kbd); +out: + return 0; +} + +void +kbd_free(struct kbd_data *kbd) +{ + int i; + + kfree(kbd->accent_table); + kfree(kbd->fn_handler); + for (i = 0; i < ARRAY_SIZE(func_table); i++) + if (kbd->func_table[i]) + kfree(kbd->func_table[i]); + kfree(kbd->func_table); + for (i = 0; i < ARRAY_SIZE(key_maps); i++) + if (kbd->key_maps[i]) + kfree(kbd->key_maps[i]); + kfree(kbd->key_maps); + kfree(kbd); +} + +/* + * Generate ascii -> ebcdic translation table from kbd_data. + */ +void +kbd_ascebc(struct kbd_data *kbd, unsigned char *ascebc) +{ + unsigned short *keymap, keysym; + int i, j, k; + + memset(ascebc, 0x40, 256); + for (i = 0; i < ARRAY_SIZE(key_maps); i++) { + keymap = kbd->key_maps[i]; + if (!keymap) + continue; + for (j = 0; j < NR_KEYS; j++) { + k = ((i & 1) << 7) + j; + keysym = keymap[j]; + if (KTYP(keysym) == (KT_LATIN | 0xf0) || + KTYP(keysym) == (KT_LETTER | 0xf0)) + ascebc[KVAL(keysym)] = k; + else if (KTYP(keysym) == (KT_DEAD | 0xf0)) + ascebc[ret_diacr[KVAL(keysym)]] = k; + } + } +} + +/* + * Generate ebcdic -> ascii translation table from kbd_data. + */ +void +kbd_ebcasc(struct kbd_data *kbd, unsigned char *ebcasc) +{ + unsigned short *keymap, keysym; + int i, j, k; + + memset(ebcasc, ' ', 256); + for (i = 0; i < ARRAY_SIZE(key_maps); i++) { + keymap = kbd->key_maps[i]; + if (!keymap) + continue; + for (j = 0; j < NR_KEYS; j++) { + keysym = keymap[j]; + k = ((i & 1) << 7) + j; + if (KTYP(keysym) == (KT_LATIN | 0xf0) || + KTYP(keysym) == (KT_LETTER | 0xf0)) + ebcasc[k] = KVAL(keysym); + else if (KTYP(keysym) == (KT_DEAD | 0xf0)) + ebcasc[k] = ret_diacr[KVAL(keysym)]; + } + } +} + +/* + * We have a combining character DIACR here, followed by the character CH. + * If the combination occurs in the table, return the corresponding value. + * Otherwise, if CH is a space or equals DIACR, return DIACR. + * Otherwise, conclude that DIACR was not combining after all, + * queue it and return CH. + */ +static unsigned char +handle_diacr(struct kbd_data *kbd, unsigned char ch) +{ + int i, d; + + d = kbd->diacr; + kbd->diacr = 0; + + for (i = 0; i < kbd->accent_table_size; i++) { + if (kbd->accent_table[i].diacr == d && + kbd->accent_table[i].base == ch) + return kbd->accent_table[i].result; + } + + if (ch == ' ' || ch == d) + return d; + + kbd_put_queue(kbd->tty, d); + return ch; +} + +/* + * Handle dead key. + */ +static void +k_dead(struct kbd_data *kbd, unsigned char value) +{ + value = ret_diacr[value]; + kbd->diacr = (kbd->diacr ? handle_diacr(kbd, value) : value); +} + +/* + * Normal character handler. + */ +static void +k_self(struct kbd_data *kbd, unsigned char value) +{ + if (kbd->diacr) + value = handle_diacr(kbd, value); + kbd_put_queue(kbd->tty, value); +} + +/* + * Special key handlers + */ +static void +k_ignore(struct kbd_data *kbd, unsigned char value) +{ +} + +/* + * Function key handler. + */ +static void +k_fn(struct kbd_data *kbd, unsigned char value) +{ + if (kbd->func_table[value]) + kbd_puts_queue(kbd->tty, kbd->func_table[value]); +} + +static void +k_spec(struct kbd_data *kbd, unsigned char value) +{ + if (value >= NR_FN_HANDLER) + return; + if (kbd->fn_handler[value]) + kbd->fn_handler[value](kbd); +} + +/* + * Put utf8 character to tty flip buffer. + * UTF-8 is defined for words of up to 31 bits, + * but we need only 16 bits here + */ +static void +to_utf8(struct tty_struct *tty, ushort c) +{ + if (c < 0x80) + /* 0******* */ + kbd_put_queue(tty, c); + else if (c < 0x800) { + /* 110***** 10****** */ + kbd_put_queue(tty, 0xc0 | (c >> 6)); + kbd_put_queue(tty, 0x80 | (c & 0x3f)); + } else { + /* 1110**** 10****** 10****** */ + kbd_put_queue(tty, 0xe0 | (c >> 12)); + kbd_put_queue(tty, 0x80 | ((c >> 6) & 0x3f)); + kbd_put_queue(tty, 0x80 | (c & 0x3f)); + } +} + +/* + * Process keycode. + */ +void +kbd_keycode(struct kbd_data *kbd, unsigned int keycode) +{ + unsigned short keysym; + unsigned char type, value; + + if (!kbd || !kbd->tty) + return; + + if (keycode >= 384) + keysym = kbd->key_maps[5][keycode - 384]; + else if (keycode >= 256) + keysym = kbd->key_maps[4][keycode - 256]; + else if (keycode >= 128) + keysym = kbd->key_maps[1][keycode - 128]; + else + keysym = kbd->key_maps[0][keycode]; + + type = KTYP(keysym); + if (type >= 0xf0) { + type -= 0xf0; + if (type == KT_LETTER) + type = KT_LATIN; + value = KVAL(keysym); +#ifdef CONFIG_MAGIC_SYSRQ /* Handle the SysRq Hack */ + if (kbd->sysrq) { + if (kbd->sysrq == K(KT_LATIN, '-')) { + kbd->sysrq = 0; + handle_sysrq(value, 0, kbd->tty); + return; + } + if (value == '-') { + kbd->sysrq = K(KT_LATIN, '-'); + return; + } + /* Incomplete sysrq sequence. */ + (*k_handler[KTYP(kbd->sysrq)])(kbd, KVAL(kbd->sysrq)); + kbd->sysrq = 0; + } else if ((type == KT_LATIN && value == '^') || + (type == KT_DEAD && ret_diacr[value] == '^')) { + kbd->sysrq = K(type, value); + return; + } +#endif + (*k_handler[type])(kbd, value); + } else + to_utf8(kbd->tty, keysym); +} + +/* + * Ioctl stuff. + */ +static int +do_kdsk_ioctl(struct kbd_data *kbd, struct kbentry *user_kbe, + int cmd, int perm) +{ + struct kbentry tmp; + ushort *key_map, val, ov; + + if (copy_from_user(&tmp, user_kbe, sizeof(struct kbentry))) + return -EFAULT; +#if NR_KEYS < 256 + if (tmp.kb_index >= NR_KEYS) + return -EINVAL; +#endif +#if MAX_NR_KEYMAPS < 256 + if (tmp.kb_table >= MAX_NR_KEYMAPS) + return -EINVAL; +#endif + + switch (cmd) { + case KDGKBENT: + key_map = kbd->key_maps[tmp.kb_table]; + if (key_map) { + val = U(key_map[tmp.kb_index]); + if (KTYP(val) >= NR_TYPES) + val = K_HOLE; + } else + val = (tmp.kb_index ? K_HOLE : K_NOSUCHMAP); + return put_user(val, &user_kbe->kb_value); + case KDSKBENT: + if (!perm) + return -EPERM; + if (!tmp.kb_index && tmp.kb_value == K_NOSUCHMAP) { + /* disallocate map */ + key_map = kbd->key_maps[tmp.kb_table]; + if (key_map) { + kbd->key_maps[tmp.kb_table] = 0; + kfree(key_map); + } + break; + } + + if (KTYP(tmp.kb_value) >= NR_TYPES) + return -EINVAL; + if (KVAL(tmp.kb_value) > max_vals[KTYP(tmp.kb_value)]) + return -EINVAL; + + if (!(key_map = kbd->key_maps[tmp.kb_table])) { + int j; + + key_map = (ushort *) kmalloc(sizeof(plain_map), + GFP_KERNEL); + if (!key_map) + return -ENOMEM; + kbd->key_maps[tmp.kb_table] = key_map; + for (j = 0; j < NR_KEYS; j++) + key_map[j] = U(K_HOLE); + } + ov = U(key_map[tmp.kb_index]); + if (tmp.kb_value == ov) + break; /* nothing to do */ + /* + * Attention Key. + */ + if (((ov == K_SAK) || (tmp.kb_value == K_SAK)) && + !capable(CAP_SYS_ADMIN)) + return -EPERM; + key_map[tmp.kb_index] = U(tmp.kb_value); + break; + } + return 0; +} + +static int +do_kdgkb_ioctl(struct kbd_data *kbd, struct kbsentry *u_kbs, + int cmd, int perm) +{ + unsigned char kb_func; + char *p; + int len; + + /* Get u_kbs->kb_func. */ + if (get_user(kb_func, &u_kbs->kb_func)) + return -EFAULT; +#if MAX_NR_FUNC < 256 + if (kb_func >= MAX_NR_FUNC) + return -EINVAL; +#endif + + switch (cmd) { + case KDGKBSENT: + p = kbd->func_table[kb_func]; + if (p) { + len = strlen(p); + if (len >= sizeof(u_kbs->kb_string)) + len = sizeof(u_kbs->kb_string) - 1; + if (copy_to_user(u_kbs->kb_string, p, len)) + return -EFAULT; + } else + len = 0; + if (put_user('\0', u_kbs->kb_string + len)) + return -EFAULT; + break; + case KDSKBSENT: + if (!perm) + return -EPERM; + len = strnlen_user(u_kbs->kb_string, + sizeof(u_kbs->kb_string) - 1); + p = kmalloc(len, GFP_KERNEL); + if (!p) + return -ENOMEM; + if (copy_from_user(p, u_kbs->kb_string, len)) { + kfree(p); + return -EFAULT; + } + p[len] = 0; + if (kbd->func_table[kb_func]) + kfree(kbd->func_table[kb_func]); + kbd->func_table[kb_func] = p; + break; + } + return 0; +} + +int +kbd_ioctl(struct kbd_data *kbd, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct kbdiacrs *a; + int ct, perm; + + /* + * To have permissions to do most of the vt ioctls, we either have + * to be the owner of the tty, or have CAP_SYS_TTY_CONFIG. + */ + perm = current->tty == kbd->tty || capable(CAP_SYS_TTY_CONFIG); + switch (cmd) { + case KDGKBTYPE: + return put_user(KB_101, (char*) arg); + case KDGKBENT: + case KDSKBENT: + return do_kdsk_ioctl(kbd, (struct kbentry *)arg, cmd, perm); + case KDGKBSENT: + case KDSKBSENT: + return do_kdgkb_ioctl(kbd, (struct kbsentry *)arg, cmd, perm); + case KDGKBDIACR: + a = (struct kbdiacrs *) arg; + + if (put_user(kbd->accent_table_size, &a->kb_cnt)) + return -EFAULT; + ct = kbd->accent_table_size; + if (copy_to_user(a->kbdiacr, kbd->accent_table, + ct * sizeof(struct kbdiacr))) + return -EFAULT; + return 0; + case KDSKBDIACR: + a = (struct kbdiacrs *) arg; + if (!perm) + return -EPERM; + if (get_user(ct, &a->kb_cnt)) + return -EFAULT; + if (ct >= MAX_DIACR) + return -EINVAL; + kbd->accent_table_size = ct; + if (copy_from_user(kbd->accent_table, a->kbdiacr, + ct * sizeof(struct kbdiacr))) + return -EFAULT; + return 0; + default: + return -ENOIOCTLCMD; + } +} + +EXPORT_SYMBOL(kbd_ioctl); +EXPORT_SYMBOL(kbd_ascebc); +EXPORT_SYMBOL(kbd_free); +EXPORT_SYMBOL(kbd_alloc); +EXPORT_SYMBOL(kbd_keycode); diff -puN /dev/null drivers/s390/char/keyboard.h --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/drivers/s390/char/keyboard.h Thu Jan 8 14:11:35 2004 @@ -0,0 +1,57 @@ +/* + * drivers/s390/char/keyboard.h + * ebcdic keycode functions for s390 console drivers + * + * Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + */ + +#include +#include +#include + +#define NR_FN_HANDLER 20 + +struct kbd_data; + +typedef void (fn_handler_fn)(struct kbd_data *); + +/* + * FIXME: explain key_maps tricks. + */ + +struct kbd_data { + struct tty_struct *tty; + unsigned short **key_maps; + char **func_table; + fn_handler_fn **fn_handler; + struct kbdiacr *accent_table; + unsigned int accent_table_size; + unsigned char diacr; + unsigned short sysrq; +}; + +struct kbd_data *kbd_alloc(void); +void kbd_free(struct kbd_data *); +void kbd_ascebc(struct kbd_data *, unsigned char *); + +void kbd_keycode(struct kbd_data *, unsigned int); +int kbd_ioctl(struct kbd_data *, struct file *, unsigned int, unsigned long); + +/* + * Helper Functions. + */ +extern inline void +kbd_put_queue(struct tty_struct *tty, int ch) +{ + tty_insert_flip_char(tty, ch, 0); + tty_schedule_flip(tty); +} + +extern inline void +kbd_puts_queue(struct tty_struct *tty, char *cp) +{ + while (*cp) + tty_insert_flip_char(tty, *cp++, 0); + tty_schedule_flip(tty); +} diff -puN drivers/s390/char/Makefile~s390-08-new-3270-driver drivers/s390/char/Makefile --- 25/drivers/s390/char/Makefile~s390-08-new-3270-driver Thu Jan 8 14:11:35 2004 +++ 25-akpm/drivers/s390/char/Makefile Thu Jan 8 14:11:35 2004 @@ -2,18 +2,20 @@ # S/390 character devices # -tub3270-objs := tuball.o tubfs.o tubtty.o \ - tubttyaid.o tubttybld.o tubttyscl.o \ - tubttyrcl.o tubttysiz.o +obj-y += ctrlchar.o keyboard.o defkeymap.o + +tub3270-objs := raw3270.o tty3270.o fs3270.o +obj-$(CONFIG_TN3270) += tub3270.o +obj-$(CONFIG_TN3270_CONSOLE) += con3270.o -obj-y += ctrlchar.o obj-$(CONFIG_TN3215) += con3215.o + obj-$(CONFIG_SCLP) += sclp.o sclp_rw.o obj-$(CONFIG_SCLP_TTY) += sclp_tty.o obj-$(CONFIG_SCLP_CONSOLE) += sclp_con.o obj-$(CONFIG_SCLP_VT220_TTY) += sclp_vt220.o obj-$(CONFIG_SCLP_CPI) += sclp_cpi.o -obj-$(CONFIG_TN3270) += tub3270.o + tape-$(CONFIG_S390_TAPE_BLOCK) += tape_block.o tape-$(CONFIG_PROC_FS) += tape_proc.o tape-objs := tape_core.o tape_std.o tape_char.o $(tape-y) diff -puN /dev/null drivers/s390/char/raw3270.c --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/drivers/s390/char/raw3270.c Thu Jan 8 14:11:35 2004 @@ -0,0 +1,1256 @@ +/* + * drivers/s390/char/raw3270.c + * IBM/3270 Driver - core functions. + * + * Author(s): + * Original 3270 Code for 2.4 written by Richard Hitt (UTS Global) + * Rewritten for 2.5 by Martin Schwidefsky + * -- Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "raw3270.h" + +/* The main 3270 data structure. */ +struct raw3270 { + struct list_head list; + struct ccw_device *cdev; + int minor; + + short model, rows, cols; + unsigned long flags; + + struct list_head req_queue; /* Request queue. */ + struct list_head view_list; /* List of available views. */ + struct raw3270_view *view; /* Active view. */ + + struct timer_list timer; /* Device timer. */ + + unsigned char *ascebc; /* ascii -> ebcdic table */ +}; + +/* raw3270->flags */ +#define RAW3270_FLAGS_14BITADDR 0 /* 14-bit buffer addresses */ +#define RAW3270_FLAGS_BUSY 1 /* Device busy, leave it alone */ +#define RAW3270_FLAGS_ATTN 2 /* Device sent an ATTN interrupt */ +#define RAW3270_FLAGS_SHUTDOWN 4 /* Device is in offline processing */ +#define RAW3270_FLAGS_CONSOLE 8 /* Device is the console. */ + +/* Lock to protect global data of raw3270 (devices, views, etc). */ +static spinlock_t raw3270_lock = SPIN_LOCK_UNLOCKED; + +/* List of 3270 devices. */ +static struct list_head raw3270_devices = LIST_HEAD_INIT(raw3270_devices); + +/* + * Flag to indicate if the driver has been registered. Some operations + * like waiting for the end of i/o need to be done differently as long + * as the kernel is still starting up (console support). + */ +static int raw3270_registered; + +/* Module parameters */ +static int tubxcorrect = 0; +MODULE_PARM(tubxcorrect, "i"); + +/* + * Wait queue for device init/delete, view delete. + */ +DECLARE_WAIT_QUEUE_HEAD(raw3270_wait_queue); + +/* + * Encode array for 12 bit 3270 addresses. + */ +unsigned char raw3270_ebcgraf[64] = { + 0x40, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f +}; + +void +raw3270_buffer_address(struct raw3270 *rp, char *cp, unsigned short addr) +{ + if (test_bit(RAW3270_FLAGS_14BITADDR, &rp->flags)) { + cp[0] = (addr >> 8) & 0x3f; + cp[1] = addr & 0xff; + } else { + cp[0] = raw3270_ebcgraf[(addr >> 6) & 0x3f]; + cp[1] = raw3270_ebcgraf[addr & 0x3f]; + } +} + +/* + * Allocate a new 3270 ccw request + */ +struct raw3270_request * +raw3270_request_alloc(size_t size) +{ + struct raw3270_request *rq; + + /* Allocate request structure */ + rq = kmalloc(sizeof(struct raw3270_request), GFP_KERNEL | GFP_DMA); + if (!rq) + return ERR_PTR(-ENOMEM); + memset(rq, 0, sizeof(struct raw3270_request)); + + /* alloc output buffer. */ + if (size > 0) { + rq->buffer = kmalloc(size, GFP_KERNEL | GFP_DMA); + if (!rq->buffer) { + kfree(rq); + return ERR_PTR(-ENOMEM); + } + } + rq->size = size; + INIT_LIST_HEAD(&rq->list); + + /* + * Setup ccw. + */ + rq->ccw.cda = __pa(rq->buffer); + rq->ccw.flags = CCW_FLAG_SLI; + + return rq; +} + +#ifdef CONFIG_TN3270_CONSOLE +/* + * Allocate a new 3270 ccw request from bootmem. Only works very + * early in the boot process. Only con3270.c should be using this. + */ +struct raw3270_request * +raw3270_request_alloc_bootmem(size_t size) +{ + struct raw3270_request *rq; + + rq = alloc_bootmem_low(sizeof(struct raw3270)); + if (!rq) + return ERR_PTR(-ENOMEM); + memset(rq, 0, sizeof(struct raw3270_request)); + + /* alloc output buffer. */ + if (size > 0) { + rq->buffer = alloc_bootmem_low(size); + if (!rq->buffer) { + free_bootmem((unsigned long) rq, + sizeof(struct raw3270)); + return ERR_PTR(-ENOMEM); + } + } + rq->size = size; + INIT_LIST_HEAD(&rq->list); + + /* + * Setup ccw. + */ + rq->ccw.cda = __pa(rq->buffer); + rq->ccw.flags = CCW_FLAG_SLI; + + return rq; +} +#endif + +/* + * Free 3270 ccw request + */ +void +raw3270_request_free (struct raw3270_request *rq) +{ + if (rq->buffer) + kfree(rq->buffer); + kfree(rq); +} + +/* + * Reset request to initial state. + */ +void +raw3270_request_reset(struct raw3270_request *rq) +{ + BUG_ON(!list_empty(&rq->list)); + rq->ccw.cmd_code = 0; + rq->ccw.count = 0; + rq->ccw.cda = __pa(rq->buffer); + rq->ccw.flags = CCW_FLAG_SLI; + rq->rescnt = 0; + rq->rc = 0; +} + +/* + * Set command code to ccw of a request. + */ +void +raw3270_request_set_cmd(struct raw3270_request *rq, u8 cmd) +{ + rq->ccw.cmd_code = cmd; +} + +/* + * Add data fragment to output buffer. + */ +int +raw3270_request_add_data(struct raw3270_request *rq, void *data, size_t size) +{ + if (size + rq->ccw.count > rq->size) + return -E2BIG; + memcpy(rq->buffer + rq->ccw.count, data, size); + rq->ccw.count += size; + return 0; +} + +/* + * Set address/length pair to ccw of a request. + */ +void +raw3270_request_set_data(struct raw3270_request *rq, void *data, size_t size) +{ + rq->ccw.cda = __pa(data); + rq->ccw.count = size; +} + +/* + * Set idal buffer to ccw of a request. + */ +void +raw3270_request_set_idal(struct raw3270_request *rq, struct idal_buffer *ib) +{ + rq->ccw.cda = __pa(ib->data); + rq->ccw.count = ib->size; + rq->ccw.flags |= CCW_FLAG_IDA; +} + +/* + * Stop running ccw. + */ +static int +raw3270_halt_io_nolock(struct raw3270 *rp, struct raw3270_request *rq) +{ + int retries; + int rc; + + if (raw3270_request_final(rq)) + return 0; + /* Check if interrupt has already been processed */ + for (retries = 0; retries < 5; retries++) { + if (retries < 2) + rc = ccw_device_halt(rp->cdev, (long) rq); + else + rc = ccw_device_clear(rp->cdev, (long) rq); + if (rc == 0) + break; /* termination successful */ + } + return rc; +} + +static int +raw3270_halt_io(struct raw3270 *rp, struct raw3270_request *rq) +{ + unsigned long flags; + int rc; + + spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); + rc = raw3270_halt_io_nolock(rp, rq); + spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags); + return rc; +} + +/* + * Add the request to the request queue, try to start it if the + * 3270 device is idle. Return without waiting for end of i/o. + */ +static int +__raw3270_start(struct raw3270 *rp, struct raw3270_view *view, + struct raw3270_request *rq) +{ + rq->view = view; + raw3270_get_view(view); + if (list_empty(&rp->req_queue) && + !test_bit(RAW3270_FLAGS_BUSY, &rp->flags)) { + /* No other requests are on the queue. Start this one. */ + rq->rc = ccw_device_start(rp->cdev, &rq->ccw, + (unsigned long) rq, 0, 0); + if (rq->rc) { + raw3270_put_view(view); + return rq->rc; + } + } + list_add_tail(&rq->list, &rp->req_queue); + return 0; +} + +int +raw3270_start(struct raw3270_view *view, struct raw3270_request *rq) +{ + unsigned long flags; + struct raw3270 *rp; + int rc; + + spin_lock_irqsave(get_ccwdev_lock(view->dev->cdev), flags); + rp = view->dev; + if (!rp || rp->view != view) + rc = -EACCES; + else if (test_bit(RAW3270_FLAGS_SHUTDOWN, &rp->flags)) + rc = -ENODEV; + else + rc = __raw3270_start(rp, view, rq); + spin_unlock_irqrestore(get_ccwdev_lock(view->dev->cdev), flags); + return rc; +} + +int +raw3270_start_irq(struct raw3270_view *view, struct raw3270_request *rq) +{ + struct raw3270 *rp; + + rp = view->dev; + rq->view = view; + raw3270_get_view(view); + list_add_tail(&rq->list, &rp->req_queue); + return 0; +} + +/* + * 3270 interrupt routine, called from the ccw_device layer + */ +static void +raw3270_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb) +{ + struct raw3270 *rp; + struct raw3270_view *view; + struct raw3270_request *rq; + int rc; + + rp = (struct raw3270 *) cdev->dev.driver_data; + if (!rp) + return; + rq = (struct raw3270_request *) intparm; + view = rq ? rq->view : rp->view; + + if (irb->scsw.dstat == + (DEV_STAT_CHN_END | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP)) { + /* Handle CE-DE-UE and subsequent UDE */ + set_bit(RAW3270_FLAGS_BUSY, &rp->flags); + rc = RAW3270_IO_BUSY; + } else if (test_bit(RAW3270_FLAGS_BUSY, &rp->flags)) { + /* Wait for UDE if busy flag is set. */ + if (irb->scsw.dstat & DEV_STAT_DEV_END) { + clear_bit(RAW3270_FLAGS_BUSY, &rp->flags); + /* Got it, now retry. */ + rc = RAW3270_IO_RETRY; + } else + rc = RAW3270_IO_BUSY; + } else if (view) + rc = view->fn->intv(view, rq, irb); + else + rc = RAW3270_IO_DONE; + + switch (rc) { + case RAW3270_IO_DONE: + break; + case RAW3270_IO_BUSY: + /* + * Intervention required by the operator. We have to wait + * for unsolicited device end. + */ + return; + case RAW3270_IO_RETRY: + if (!rq) + break; + rq->rc = ccw_device_start(rp->cdev, &rq->ccw, + (unsigned long) rq, 0, 0); + if (rq->rc == 0) + return; /* Sucessfully restarted. */ + break; + case RAW3270_IO_STOP: + raw3270_halt_io_nolock(rp, rq); + rq->rc = -EIO; + break; + default: + BUG(); + } + if (rq) { + BUG_ON(list_empty(&rq->list)); + /* The request completed, remove from queue and do callback. */ + list_del_init(&rq->list); + if (rq->callback) + rq->callback(rq, rq->callback_data); + /* Do put_device for get_device in raw3270_start. */ + raw3270_put_view(view); + } + /* + * Try to start each request on request queue until one is + * started successful. + */ + while (!list_empty(&rp->req_queue)) { + rq = list_entry(rp->req_queue.next,struct raw3270_request,list); + rq->rc = ccw_device_start(rp->cdev, &rq->ccw, + (unsigned long) rq, 0, 0); + if (rq->rc == 0) + break; + /* Start failed. Remove request and do callback. */ + list_del_init(&rq->list); + if (rq->callback) + rq->callback(rq, rq->callback_data); + /* Do put_device for get_device in raw3270_start. */ + raw3270_put_view(view); + } +} + +/* + * Size sensing. + */ + +struct raw3270_ua { /* Query Reply structure for Usable Area */ + struct { /* Usable Area Query Reply Base */ + short l; /* Length of this structured field */ + char sfid; /* 0x81 if Query Reply */ + char qcode; /* 0x81 if Usable Area */ + char flags0; + char flags1; + short w; /* Width of usable area */ + short h; /* Heigth of usavle area */ + char units; /* 0x00:in; 0x01:mm */ + int xr; + int yr; + char aw; + char ah; + short buffsz; /* Character buffer size, bytes */ + char xmin; + char ymin; + char xmax; + char ymax; + } __attribute__ ((packed)) uab; + struct { /* Alternate Usable Area Self-Defining Parameter */ + char l; /* Length of this Self-Defining Parm */ + char sdpid; /* 0x02 if Alternate Usable Area */ + char res; + char auaid; /* 0x01 is Id for the A U A */ + short wauai; /* Width of AUAi */ + short hauai; /* Height of AUAi */ + char auaunits; /* 0x00:in, 0x01:mm */ + int auaxr; + int auayr; + char awauai; + char ahauai; + } __attribute__ ((packed)) aua; +} __attribute__ ((packed)); + +static unsigned char raw3270_init_data[256]; +static struct raw3270_request raw3270_init_request; +static struct diag210 raw3270_init_diag210; +static DECLARE_MUTEX(raw3270_init_sem); + +static int +raw3270_init_irq(struct raw3270_view *view, struct raw3270_request *rq, + struct irb *irb) +{ + /* + * Unit-Check Processing: + * Expect Command Reject or Intervention Required. + */ + if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK) { + /* Request finished abnormally. */ + if (irb->ecw[0] & SNS0_INTERVENTION_REQ) { + set_bit(RAW3270_FLAGS_BUSY, &view->dev->flags); + return RAW3270_IO_BUSY; + } + } + if (rq) { + if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK) { + if (irb->ecw[0] & SNS0_CMD_REJECT) + rq->rc = -EOPNOTSUPP; + else + rq->rc = -EIO; + } else + /* Request finished normally. Copy residual count. */ + rq->rescnt = irb->scsw.count; + } + if (irb->scsw.dstat & DEV_STAT_ATTENTION) { + set_bit(RAW3270_FLAGS_ATTN, &view->dev->flags); + wake_up(&raw3270_wait_queue); + } + return RAW3270_IO_DONE; +} + +static struct raw3270_fn raw3270_init_fn = { + .intv = raw3270_init_irq +}; + +static struct raw3270_view raw3270_init_view = { + .fn = &raw3270_init_fn +}; + +/* + * raw3270_wait/raw3270_wait_interruptible/__raw3270_wakeup + * Wait for end of request. The request must have been started + * with raw3270_start, rc = 0. The device lock may NOT have been + * released between calling raw3270_start and raw3270_wait. + */ +static void +raw3270_wake_init(struct raw3270_request *rq, void *data) +{ + wake_up((wait_queue_head_t *) data); +} + +/* + * Special wait function that can cope with console initialization. + */ +static int +raw3270_start_init(struct raw3270 *rp, struct raw3270_view *view, + struct raw3270_request *rq) +{ + unsigned long flags; + wait_queue_head_t wq; + int rc; + +#ifdef CONFIG_TN3270_CONSOLE + if (raw3270_registered == 0) { + spin_lock_irqsave(get_ccwdev_lock(view->dev->cdev), flags); + rq->callback = 0; + rc = __raw3270_start(rp, view, rq); + if (rc == 0) + while (!raw3270_request_final(rq)) { + wait_cons_dev(); + barrier(); + } + spin_unlock_irqrestore(get_ccwdev_lock(view->dev->cdev), flags); + return rq->rc; + } +#endif + init_waitqueue_head(&wq); + rq->callback = raw3270_wake_init; + rq->callback_data = &wq; + spin_lock_irqsave(get_ccwdev_lock(view->dev->cdev), flags); + rc = __raw3270_start(rp, view, rq); + spin_unlock_irqrestore(get_ccwdev_lock(view->dev->cdev), flags); + if (rc) + return rc; + /* Now wait for the completion. */ + rc = wait_event_interruptible(wq, raw3270_request_final(rq)); + if (rc == -ERESTARTSYS) { /* Interrupted by a signal. */ + raw3270_halt_io(view->dev, rq); + return -ERESTARTSYS; + } + return rq->rc; +} + +static int +__raw3270_size_device_vm(struct raw3270 *rp) +{ + int rc, model; + + raw3270_init_diag210.vrdcdvno = + _ccw_device_get_device_number(rp->cdev); + raw3270_init_diag210.vrdclen = sizeof(struct diag210); + rc = diag210(&raw3270_init_diag210); + if (rc) + return rc; + model = raw3270_init_diag210.vrdccrmd; + switch (model) { + case 2: + rp->model = model; + rp->rows = 24; + rp->cols = 80; + break; + case 3: + rp->model = model; + rp->rows = 32; + rp->cols = 80; + break; + case 4: + rp->model = model; + rp->rows = 43; + rp->cols = 80; + break; + case 5: + rp->model = model; + rp->rows = 27; + rp->cols = 132; + break; + default: + printk(KERN_WARNING "vrdccrmd is 0x%.8x\n", model); + rc = -EOPNOTSUPP; + break; + } + return rc; +} + +static int +__raw3270_size_device(struct raw3270 *rp) +{ + static const unsigned char wbuf[] = + { 0x00, 0x07, 0x01, 0xff, 0x03, 0x00, 0x81 }; + unsigned long flags; + struct raw3270_ua *uap; + unsigned short count; + int rc; + + /* + * To determine the size of the 3270 device we need to do: + * 1) send a 'read partition' data stream to the device + * 2) wait for the attn interrupt that preceeds the query reply + * 3) do a read modified to get the query reply + * To make things worse we have to cope with intervention + * required (3270 device switched to 'stand-by') and command + * rejects (old devices that can't do 'read partition'). + */ + memset(&raw3270_init_request, 0, sizeof(raw3270_init_request)); + memset(raw3270_init_data, 0, sizeof(raw3270_init_data)); + /* Store 'read partition' data stream to raw3270_init_data */ + memcpy(raw3270_init_data, wbuf, sizeof(wbuf)); + INIT_LIST_HEAD(&raw3270_init_request.list); + raw3270_init_request.ccw.cmd_code = TC_WRITESF; + raw3270_init_request.ccw.flags = CCW_FLAG_SLI; + raw3270_init_request.ccw.count = sizeof(wbuf); + raw3270_init_request.ccw.cda = (__u32) __pa(raw3270_init_data); + + rc = raw3270_start_init(rp, &raw3270_init_view, &raw3270_init_request); + if (rc) { + /* Check error cases: -ERESTARTSYS, -EIO and -EOPNOTSUPP */ + if (rc == -EOPNOTSUPP && MACHINE_IS_VM) + return __raw3270_size_device_vm(rp); + return rc; + } + + /* Wait for attention interrupt. */ +#ifdef CONFIG_TN3270_CONSOLE + if (raw3270_registered == 0) { + spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); + while (!test_and_clear_bit(RAW3270_FLAGS_ATTN, &rp->flags)) + wait_cons_dev(); + spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags); + } else +#endif + rc = wait_event_interruptible(raw3270_wait_queue, + test_and_clear_bit(RAW3270_FLAGS_ATTN, &rp->flags)); + if (rc) + return rc; + + /* + * The device accepted the 'read partition' command. Now + * set up a read ccw and issue it. + */ + raw3270_init_request.ccw.cmd_code = TC_READMOD; + raw3270_init_request.ccw.flags = CCW_FLAG_SLI; + raw3270_init_request.ccw.count = sizeof(raw3270_init_data); + raw3270_init_request.ccw.cda = (__u32) __pa(raw3270_init_data); + rc = raw3270_start_init(rp, &raw3270_init_view, &raw3270_init_request); + if (rc) + return rc; + /* Got a Query Reply */ + count = sizeof(raw3270_init_data) - raw3270_init_request.rescnt; + uap = (struct raw3270_ua *) (raw3270_init_data + 1); + /* Paranoia check. */ + if (raw3270_init_data[0] != 0x88 || uap->uab.qcode != 0x81) + return -EOPNOTSUPP; + /* Copy rows/columns of default Usable Area */ + rp->rows = uap->uab.h; + rp->cols = uap->uab.w; + /* Check for 14 bit addressing */ + if ((uap->uab.flags0 & 0x0d) == 0x01) + set_bit(RAW3270_FLAGS_14BITADDR, &rp->flags); + /* Check for Alternate Usable Area */ + if (uap->uab.l == sizeof(struct raw3270_ua) && + uap->aua.sdpid == 0x02) { + rp->rows = uap->aua.hauai; + rp->cols = uap->aua.wauai; + } + return 0; +} + +static int +raw3270_size_device(struct raw3270 *rp) +{ + int rc; + + down(&raw3270_init_sem); + rp->view = &raw3270_init_view; + raw3270_init_view.dev = rp; + rc = __raw3270_size_device(rp); + raw3270_init_view.dev = 0; + rp->view = 0; + up(&raw3270_init_sem); + if (rc == 0) { /* Found something. */ + /* Try to find a model. */ + rp->model = 0; + if (rp->rows == 24 && rp->cols == 80) + rp->model = 2; + if (rp->rows == 32 && rp->cols == 80) + rp->model = 3; + if (rp->rows == 43 && rp->cols == 80) + rp->model = 4; + if (rp->rows == 27 && rp->cols == 132) + rp->model = 5; + } + return rc; +} + +static int +raw3270_reset_device(struct raw3270 *rp) +{ + int rc; + + down(&raw3270_init_sem); + memset(&raw3270_init_request, 0, sizeof(raw3270_init_request)); + memset(raw3270_init_data, 0, sizeof(raw3270_init_data)); + /* Store reset data stream to raw3270_init_data/raw3270_init_request */ + raw3270_init_data[0] = TW_KR; + INIT_LIST_HEAD(&raw3270_init_request.list); + raw3270_init_request.ccw.cmd_code = TC_EWRITEA; + raw3270_init_request.ccw.flags = CCW_FLAG_SLI; + raw3270_init_request.ccw.count = 1; + raw3270_init_request.ccw.cda = (__u32) __pa(raw3270_init_data); + rp->view = &raw3270_init_view; + raw3270_init_view.dev = rp; + rc = raw3270_start_init(rp, &raw3270_init_view, &raw3270_init_request); + raw3270_init_view.dev = 0; + rp->view = 0; + up(&raw3270_init_sem); + return rc; +} + +/* + * Setup new 3270 device. + */ +static int +raw3270_setup_device(struct ccw_device *cdev, struct raw3270 *rp, char *ascebc) +{ + struct list_head *l; + struct raw3270 *tmp; + int minor; + + memset(rp, 0, sizeof(struct raw3270)); + /* Copy ebcdic -> ascii translation table. */ + memcpy(ascebc, _ascebc, 256); + if (tubxcorrect) { + /* correct brackets and circumflex */ + ascebc['['] = 0xad; + ascebc[']'] = 0xbd; + ascebc['^'] = 0xb0; + } + rp->ascebc = ascebc; + + /* Set defaults. */ + rp->rows = 24; + rp->cols = 80; + + INIT_LIST_HEAD(&rp->req_queue); + INIT_LIST_HEAD(&rp->view_list); + + /* + * Add device to list and find the smallest unused minor + * number for it. + */ + spin_lock(&raw3270_lock); + /* Keep the list sorted. */ + minor = 0; + rp->minor = -1; + list_for_each(l, &raw3270_devices) { + tmp = list_entry(l, struct raw3270, list); + if (tmp->minor > minor) { + rp->minor = minor; + __list_add(&rp->list, l->prev, l); + break; + } + minor++; + } + if (rp->minor == -1 && minor < RAW3270_MAXDEVS) { + rp->minor = minor; + list_add_tail(&rp->list, &raw3270_devices); + } + spin_unlock(&raw3270_lock); + /* No free minor number? Then give up. */ + if (rp->minor == -1) + return -EUSERS; + rp->cdev = cdev; + cdev->dev.driver_data = rp; + cdev->handler = raw3270_irq; + return 0; +} + +#ifdef CONFIG_TN3270_CONSOLE +/* + * Setup 3270 device configured as console. + */ +struct raw3270 * +raw3270_setup_console(struct ccw_device *cdev) +{ + struct raw3270 *rp; + char *ascebc; + int rc; + + rp = (struct raw3270 *) alloc_bootmem(sizeof(struct raw3270)); + ascebc = (char *) alloc_bootmem(256); + rc = raw3270_setup_device(cdev, rp, ascebc); + if (rc) + return ERR_PTR(rc); + set_bit(RAW3270_FLAGS_CONSOLE, &rp->flags); + raw3270_reset_device(rp); + raw3270_size_device(rp); + raw3270_reset_device(rp); + return rp; +} + +void +raw3270_wait_cons_dev(struct raw3270 *rp) +{ + unsigned long flags; + + spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); + wait_cons_dev(); + spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags); +} + +#endif + +/* + * Create a 3270 device structure. + */ +static struct raw3270 * +raw3270_create_device(struct ccw_device *cdev) +{ + struct raw3270 *rp; + char *ascebc; + int rc; + + rp = kmalloc(sizeof(struct raw3270), GFP_KERNEL); + if (!rp) + return ERR_PTR(-ENOMEM); + ascebc = kmalloc(256, GFP_KERNEL); + if (!ascebc) { + kfree(rp); + return ERR_PTR(-ENOMEM); + } + rc = raw3270_setup_device(cdev, rp, ascebc); + if (rc) { + kfree(rp->ascebc); + kfree(rp); + rp = ERR_PTR(rc); + } + /* Get reference to ccw_device structure. */ + get_device(&cdev->dev); + return rp; +} + +/* + * Activate a view. + */ +int +raw3270_activate_view(struct raw3270_view *view) +{ + struct raw3270 *rp; + struct raw3270_view *oldview, *nv; + unsigned long flags; + int rc; + + rp = view->dev; + if (!rp) + return -ENODEV; + spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); + if (rp->view == view) + rc = 0; + else if (test_bit(RAW3270_FLAGS_SHUTDOWN, &rp->flags)) + rc = -ENODEV; + else { + oldview = 0; + if (rp->view) { + oldview = rp->view; + oldview->fn->deactivate(oldview); + } + rp->view = view; + rc = view->fn->activate(view); + if (rc) { + /* Didn't work. Try to reactivate the old view. */ + rp->view = oldview; + if (oldview->fn->activate(oldview) != 0) { + /* Didn't work as well. Try any other view. */ + list_for_each_entry(nv, &rp->view_list, list) + if (nv != view && nv != oldview) { + rp->view = nv; + if (nv->fn->activate(nv) == 0) + break; + rp->view = 0; + } + } + } + } + spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags); + return rc; +} + +/* + * Deactivate current view. + */ +void +raw3270_deactivate_view(struct raw3270_view *view) +{ + unsigned long flags; + struct raw3270 *rp; + + rp = view->dev; + if (!rp) + return; + spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); + if (rp->view == view) { + view->fn->deactivate(view); + rp->view = 0; + /* Move deactivated view to end of list. */ + list_del_init(&view->list); + list_add_tail(&view->list, &rp->view_list); + /* Try to activate another view. */ + if (!test_bit(RAW3270_FLAGS_SHUTDOWN, &rp->flags)) { + list_for_each_entry(view, &rp->view_list, list) + if (view->fn->activate(view) == 0) { + rp->view = view; + break; + } + } + } + spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags); +} + +/* + * Add view to device with minor "minor". + */ +int +raw3270_add_view(struct raw3270_view *view, struct raw3270_fn *fn, int minor) +{ + unsigned long flags; + struct raw3270 *rp; + int rc; + + spin_lock(&raw3270_lock); + rc = -ENODEV; + list_for_each_entry(rp, &raw3270_devices, list) { + if (rp->minor != minor) + continue; + spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); + if (!test_bit(RAW3270_FLAGS_SHUTDOWN, &rp->flags)) { + atomic_set(&view->ref_count, 2); + view->dev = rp; + view->fn = fn; + view->model = rp->model; + view->rows = rp->rows; + view->cols = rp->cols; + view->ascebc = rp->ascebc; + spin_lock_init(&view->lock); + list_add_tail(&view->list, &rp->view_list); + rc = 0; + } + spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags); + break; + } + spin_unlock(&raw3270_lock); + return rc; +} + +/* + * Find specific view of device with minor "minor". + */ +struct raw3270_view * +raw3270_find_view(struct raw3270_fn *fn, int minor) +{ + struct raw3270 *rp; + struct raw3270_view *view, *tmp; + unsigned long flags; + + spin_lock(&raw3270_lock); + view = ERR_PTR(-ENODEV); + list_for_each_entry(rp, &raw3270_devices, list) { + if (rp->minor != minor) + continue; + spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); + if (!test_bit(RAW3270_FLAGS_SHUTDOWN, &rp->flags)) { + list_for_each_entry(tmp, &rp->view_list, list) { + if (tmp->fn == fn) { + raw3270_get_view(tmp); + view = tmp; + } + } + } else + view = ERR_PTR(-ENOENT); + spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags); + break; + } + spin_unlock(&raw3270_lock); + return view; +} + +/* + * Remove view from device and free view structure via call to view->fn->free. + */ +void +raw3270_del_view(struct raw3270_view *view) +{ + unsigned long flags; + struct raw3270 *rp; + + rp = view->dev; + spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); + if (rp->view == view) { + view->fn->deactivate(view); + rp->view = 0; + } + list_del_init(&view->list); + if (!rp->view && !test_bit(RAW3270_FLAGS_SHUTDOWN, &rp->flags)) { + /* Try to activate another view. */ + list_for_each_entry(view, &rp->view_list, list) + if (view->fn->activate(view) == 0) { + rp->view = view; + break; + } + } + spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags); + /* Wait for reference counter to drop to zero. */ + atomic_dec(&view->ref_count); + wait_event(raw3270_wait_queue, atomic_read(&view->ref_count) == 0); + if (view->fn->free) + view->fn->free(view); +} + +/* + * Remove a 3270 device structure. + */ +static void +raw3270_delete_device(struct raw3270 *rp) +{ + struct ccw_device *cdev; + + /* Remove from device chain. */ + spin_lock(&raw3270_lock); + list_del_init(&rp->list); + spin_unlock(&raw3270_lock); + + /* Disconnect from ccw_device. */ + cdev = rp->cdev; + rp->cdev = 0; + cdev->dev.driver_data = 0; + cdev->handler = 0; + + /* Put ccw_device structure. */ + put_device(&cdev->dev); + + /* Now free raw3270 structure. */ + kfree(rp->ascebc); + kfree(rp); +} + +static int +raw3270_probe (struct ccw_device *cdev) +{ + return 0; +} + +/* + * Additional attributes for a 3270 device + */ +static ssize_t +raw3270_model_show(struct device *dev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%i\n", + ((struct raw3270 *) dev->driver_data)->model); +} +static DEVICE_ATTR(model, 0444, raw3270_model_show, 0); + +static ssize_t +raw3270_rows_show(struct device *dev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%i\n", + ((struct raw3270 *) dev->driver_data)->rows); +} +static DEVICE_ATTR(rows, 0444, raw3270_rows_show, 0); + +static ssize_t +raw3270_columns_show(struct device *dev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%i\n", + ((struct raw3270 *) dev->driver_data)->cols); +} +static DEVICE_ATTR(columns, 0444, raw3270_columns_show, 0); + +static struct attribute * raw3270_attrs[] = { + &dev_attr_model.attr, + &dev_attr_rows.attr, + &dev_attr_columns.attr, + NULL, +}; + +static struct attribute_group raw3270_attr_group = { + .attrs = raw3270_attrs, +}; + +static void +raw3270_create_attributes(struct raw3270 *rp) +{ + //FIXME: check return code + sysfs_create_group(&rp->cdev->dev.kobj, &raw3270_attr_group); +} + +/* Hackish. A notifier chain would be cleaner. */ +extern void tty3270_notifier(int index, int active); + +/* + * Set 3270 device online. + */ +static int +raw3270_set_online (struct ccw_device *cdev) +{ + struct raw3270 *rp; + + rp = raw3270_create_device(cdev); + if (IS_ERR(rp)) + return PTR_ERR(rp); + raw3270_reset_device(rp); + raw3270_size_device(rp); + raw3270_reset_device(rp); + raw3270_create_attributes(rp); + tty3270_notifier(rp->minor, 1); + return 0; +} + +/* + * Remove 3270 device structure. + */ +static void +raw3270_remove (struct ccw_device *cdev) +{ + unsigned long flags; + struct raw3270 *rp; + struct raw3270_view *v; + + rp = cdev->dev.driver_data; + set_bit(RAW3270_FLAGS_SHUTDOWN, &rp->flags); + + sysfs_remove_group(&cdev->dev.kobj, &raw3270_attr_group); + + /* Deactivate current view and remove all views. */ + spin_lock_irqsave(get_ccwdev_lock(cdev), flags); + if (rp->view) { + rp->view->fn->deactivate(rp->view); + rp->view = 0; + } + while (!list_empty(&rp->view_list)) { + v = list_entry(rp->view_list.next, struct raw3270_view, list); + if (v->fn->release) + v->fn->release(v); + spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); + raw3270_del_view(v); + spin_lock_irqsave(get_ccwdev_lock(cdev), flags); + } + spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); + + tty3270_notifier(rp->minor, 0); + + /* Reset 3270 device. */ + raw3270_reset_device(rp); + /* And finally remove it. */ + raw3270_delete_device(rp); +} + +/* + * Set 3270 device offline. + */ +static int +raw3270_set_offline (struct ccw_device *cdev) +{ + struct raw3270 *rp; + + rp = cdev->dev.driver_data; + if (test_bit(RAW3270_FLAGS_CONSOLE, &rp->flags)) + return -EBUSY; + raw3270_remove(cdev); + return 0; +} + +static struct ccw_device_id raw3270_id[] = { + { CCW_DEVICE(0x3270, 0) }, + { CCW_DEVICE(0x3271, 0) }, + { CCW_DEVICE(0x3272, 0) }, + { CCW_DEVICE(0x3273, 0) }, + { CCW_DEVICE(0x3274, 0) }, + { CCW_DEVICE(0x3275, 0) }, + { CCW_DEVICE(0x3276, 0) }, + { CCW_DEVICE(0x3277, 0) }, + { CCW_DEVICE(0x3278, 0) }, + { CCW_DEVICE(0x3279, 0) }, + { CCW_DEVICE(0x3174, 0) }, + { /* end of list */ }, +}; + +static struct ccw_driver raw3270_ccw_driver = { + .name = "3270", + .owner = THIS_MODULE, + .ids = raw3270_id, + .probe = &raw3270_probe, + .remove = &raw3270_remove, + .set_online = &raw3270_set_online, + .set_offline = &raw3270_set_offline, +}; + +int +raw3270_init(void) +{ + struct raw3270 *rp; + int rc; + + if (raw3270_registered) + return 0; + raw3270_registered = 1; + rc = ccw_driver_register(&raw3270_ccw_driver); + if (rc == 0) { + /* Create attributes for early (= console) device. */ + spin_lock(&raw3270_lock); + list_for_each_entry(rp, &raw3270_devices, list) { + get_device(&rp->cdev->dev); + raw3270_create_attributes(rp); + tty3270_notifier(rp->minor, 1); + } + spin_unlock(&raw3270_lock); + } + return rc; +} + +void +raw3270_exit(void) +{ + ccw_driver_unregister(&raw3270_ccw_driver); +} + +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(raw3270_init); +EXPORT_SYMBOL(raw3270_exit); +EXPORT_SYMBOL(raw3270_request_alloc); +EXPORT_SYMBOL(raw3270_request_free); +EXPORT_SYMBOL(raw3270_request_reset); +EXPORT_SYMBOL(raw3270_request_set_cmd); +EXPORT_SYMBOL(raw3270_request_add_data); +EXPORT_SYMBOL(raw3270_request_set_data); +EXPORT_SYMBOL(raw3270_request_set_idal); +EXPORT_SYMBOL(raw3270_buffer_address); +EXPORT_SYMBOL(raw3270_add_view); +EXPORT_SYMBOL(raw3270_del_view); +EXPORT_SYMBOL(raw3270_find_view); +EXPORT_SYMBOL(raw3270_activate_view); +EXPORT_SYMBOL(raw3270_deactivate_view); +EXPORT_SYMBOL(raw3270_start); +EXPORT_SYMBOL(raw3270_start_irq); diff -puN /dev/null drivers/s390/char/raw3270.h --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/drivers/s390/char/raw3270.h Thu Jan 8 14:11:35 2004 @@ -0,0 +1,273 @@ +/* + * drivers/s390/char/raw3270.h + * IBM/3270 Driver + * + * Author(s): + * Original 3270 Code for 2.4 written by Richard Hitt (UTS Global) + * Rewritten for 2.5 by Martin Schwidefsky + * -- Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation + */ + +#include +#include + +/* ioctls for fullscreen 3270 */ +#define TUBICMD _IO('3', 3) /* set ccw command for fs reads. */ +#define TUBOCMD _IO('3', 4) /* set ccw command for fs writes. */ +#define TUBGETI _IO('3', 7) /* get ccw command for fs reads. */ +#define TUBGETO _IO('3', 8) /* get ccw command for fs writes. */ +#define TUBSETMOD _IO('3',12) /* FIXME: what does it do ?*/ +#define TUBGETMOD _IO('3',13) /* FIXME: what does it do ?*/ + +/* Local Channel Commands */ +#define TC_WRITE 0x01 /* Write */ +#define TC_EWRITE 0x05 /* Erase write */ +#define TC_READMOD 0x06 /* Read modified */ +#define TC_EWRITEA 0x0d /* Erase write alternate */ +#define TC_WRITESF 0x11 /* Write structured field */ + +/* Buffer Control Orders */ +#define TO_SF 0x1d /* Start field */ +#define TO_SBA 0x11 /* Set buffer address */ +#define TO_IC 0x13 /* Insert cursor */ +#define TO_PT 0x05 /* Program tab */ +#define TO_RA 0x3c /* Repeat to address */ +#define TO_SFE 0x29 /* Start field extended */ +#define TO_EUA 0x12 /* Erase unprotected to address */ +#define TO_MF 0x2c /* Modify field */ +#define TO_SA 0x28 /* Set attribute */ + +/* Field Attribute Bytes */ +#define TF_INPUT 0x40 /* Visible input */ +#define TF_INPUTN 0x4c /* Invisible input */ +#define TF_INMDT 0xc1 /* Visible, Set-MDT */ +#define TF_LOG 0x60 + +/* Character Attribute Bytes */ +#define TAT_RESET 0x00 +#define TAT_FIELD 0xc0 +#define TAT_EXTHI 0x41 +#define TAT_COLOR 0x42 +#define TAT_CHARS 0x43 +#define TAT_TRANS 0x46 + +/* Extended-Highlighting Bytes */ +#define TAX_RESET 0x00 +#define TAX_BLINK 0xf1 +#define TAX_REVER 0xf2 +#define TAX_UNDER 0xf4 + +/* Reset value */ +#define TAR_RESET 0x00 + +/* Color values */ +#define TAC_RESET 0x00 +#define TAC_BLUE 0xf1 +#define TAC_RED 0xf2 +#define TAC_PINK 0xf3 +#define TAC_GREEN 0xf4 +#define TAC_TURQ 0xf5 +#define TAC_YELLOW 0xf6 +#define TAC_WHITE 0xf7 +#define TAC_DEFAULT 0x00 + +/* Write Control Characters */ +#define TW_NONE 0x40 /* No particular action */ +#define TW_KR 0xc2 /* Keyboard restore */ +#define TW_PLUSALARM 0x04 /* Add this bit for alarm */ + +#define RAW3270_MAXDEVS 256 + +int raw3270_init(void); +void raw3270_exit(void); + +/* For TUBGETMOD and TUBSETMOD. Should include. */ +struct raw3270_iocb { + short model; + short line_cnt; + short col_cnt; + short pf_cnt; + short re_cnt; + short map; +}; + +struct raw3270; +struct raw3270_view; + +/* 3270 CCW request */ +struct raw3270_request { + struct list_head list; /* list head for request queueing. */ + struct raw3270_view *view; /* view of this request */ + struct ccw1 ccw; /* single ccw. */ + void *buffer; /* output buffer. */ + size_t size; /* size of output buffer. */ + int rescnt; /* residual count from devstat. */ + int rc; /* return code for this request. */ + + /* Callback for delivering final status. */ + void (*callback)(struct raw3270_request *, void *); + void *callback_data; +}; + +struct raw3270_request *raw3270_request_alloc(size_t size); +struct raw3270_request *raw3270_request_alloc_bootmem(size_t size); +void raw3270_request_free(struct raw3270_request *); +void raw3270_request_reset(struct raw3270_request *); +void raw3270_request_set_cmd(struct raw3270_request *, u8 cmd); +int raw3270_request_add_data(struct raw3270_request *, void *, size_t); +void raw3270_request_set_data(struct raw3270_request *, void *, size_t); +void raw3270_request_set_idal(struct raw3270_request *, struct idal_buffer *); + +static inline int +raw3270_request_final(struct raw3270_request *rq) +{ + return list_empty(&rq->list); +} + +void raw3270_buffer_address(struct raw3270 *, char *, unsigned short); + +/* Return value of *intv (see raw3270_fn below) can be one of the following: */ +#define RAW3270_IO_DONE 0 /* request finished */ +#define RAW3270_IO_BUSY 1 /* request still active */ +#define RAW3270_IO_RETRY 2 /* retry current request */ +#define RAW3270_IO_STOP 3 /* kill current request */ + +/* + * Functions of a 3270 view. + */ +struct raw3270_fn { + int (*activate)(struct raw3270_view *); + void (*deactivate)(struct raw3270_view *); + int (*intv)(struct raw3270_view *, + struct raw3270_request *, struct irb *); + void (*release)(struct raw3270_view *); + void (*free)(struct raw3270_view *); +}; + +/* + * View structure chaining. The raw3270_view structure is meant to + * be embedded at the start of the real view data structure, e.g.: + * struct example { + * struct raw3270_view view; + * ... + * }; + */ +struct raw3270_view { + struct list_head list; + spinlock_t lock; + atomic_t ref_count; + struct raw3270 *dev; + struct raw3270_fn *fn; + unsigned int model; + unsigned int rows, cols; /* # of rows & colums of the view */ + unsigned char *ascebc; /* ascii -> ebcdic table */ +}; + +int raw3270_add_view(struct raw3270_view *, struct raw3270_fn *, int); +int raw3270_activate_view(struct raw3270_view *); +void raw3270_del_view(struct raw3270_view *); +void raw3270_deactivate_view(struct raw3270_view *); +struct raw3270_view *raw3270_find_view(struct raw3270_fn *, int); +int raw3270_start(struct raw3270_view *, struct raw3270_request *); +int raw3270_start_irq(struct raw3270_view *, struct raw3270_request *); + +/* Reference count inliner for view structures. */ +static inline void +raw3270_get_view(struct raw3270_view *view) +{ + atomic_inc(&view->ref_count); +} + +extern wait_queue_head_t raw3270_wait_queue; + +static inline void +raw3270_put_view(struct raw3270_view *view) +{ + if (atomic_dec_return(&view->ref_count) == 0) + wake_up(&raw3270_wait_queue); +} + +struct raw3270 *raw3270_setup_console(struct ccw_device *cdev); +void raw3270_wait_cons_dev(struct raw3270 *); + +/* + * Little memory allocator for string objects. + */ +struct string +{ + struct list_head list; + struct list_head update; + unsigned long size; + unsigned long len; + char string[0]; +} __attribute__ ((aligned(8))); + +static inline struct string * +alloc_string(struct list_head *free_list, unsigned long len) +{ + struct string *cs, *tmp; + unsigned long size; + + size = (len + 7L) & -8L; + list_for_each_entry(cs, free_list, list) { + if (cs->size < size) + continue; + if (cs->size > size + sizeof(struct string)) { + char *endaddr = (char *) (cs + 1) + cs->size; + tmp = (struct string *) (endaddr - size) - 1; + tmp->size = size; + cs->size -= size + sizeof(struct string); + cs = tmp; + } else + list_del(&cs->list); + cs->len = len; + INIT_LIST_HEAD(&cs->list); + INIT_LIST_HEAD(&cs->update); + return cs; + } + return 0; +} + +static inline unsigned long +free_string(struct list_head *free_list, struct string *cs) +{ + struct string *tmp; + struct list_head *p, *left; + + /* Find out the left neighbour in free memory list. */ + left = free_list; + list_for_each(p, free_list) { + if (list_entry(p, struct string, list) > cs) + break; + left = p; + } + /* Try to merge with right neighbour = next element from left. */ + if (left->next != free_list) { + tmp = list_entry(left->next, struct string, list); + if ((char *) (cs + 1) + cs->size == (char *) tmp) { + list_del(&tmp->list); + cs->size += tmp->size + sizeof(struct string); + } + } + /* Try to merge with left neighbour. */ + if (left != free_list) { + tmp = list_entry(left, struct string, list); + if ((char *) (tmp + 1) + tmp->size == (char *) cs) { + tmp->size += cs->size + sizeof(struct string); + return tmp->size; + } + } + __list_add(&cs->list, left, left->next); + return cs->size; +} + +static inline void +add_string_memory(struct list_head *free_list, void *mem, unsigned long size) +{ + struct string *cs; + + cs = (struct string *) mem; + cs->size = size - sizeof(struct string); + free_string(free_list, cs); +} + diff -puN /dev/null drivers/s390/char/tty3270.c --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/drivers/s390/char/tty3270.c Thu Jan 8 14:11:35 2004 @@ -0,0 +1,1828 @@ +/* + * drivers/s390/char/tty3270.c + * IBM/3270 Driver - tty functions. + * + * Author(s): + * Original 3270 Code for 2.4 written by Richard Hitt (UTS Global) + * Rewritten for 2.5 by Martin Schwidefsky + * -- Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + + +#include "raw3270.h" +#include "keyboard.h" + +#define TTY3270_CHAR_BUF_SIZE 256 +#define TTY3270_OUTPUT_BUFFER_SIZE 1024 +#define TTY3270_STRING_PAGES 5 + +struct tty_driver *tty3270_driver; + +struct raw3270_fn tty3270_fn; + +struct tty3270_cell { + unsigned char character; + unsigned char highlight; + unsigned char f_color; +}; + +struct tty3270_line { + struct tty3270_cell *cells; + int len; +}; + +#define ESCAPE_NPAR 8 + +/* + * The main tty view data structure. + * FIXME: + * 1) describe line orientation & lines list concept against screen + * 2) describe conversion of screen to lines + * 3) describe line format. + */ +struct tty3270 { + struct raw3270_view view; + struct tty_struct *tty; /* Pointer to tty structure */ + void **freemem_pages; /* Array of pages used for freemem. */ + struct list_head freemem; /* List of free memory for strings. */ + + /* 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. */ + unsigned long update_flags; /* Update indication bits. */ + struct string *status; /* Lower right of display. */ + struct raw3270_request *write; /* Single write request. */ + struct timer_list timer; /* Output delay timer. */ + + /* Current tty screen. */ + unsigned int cx, cy; /* Current output position. */ + unsigned int highlight; /* Blink/reverse/underscore */ + unsigned int f_color; /* Foreground color */ + struct tty3270_line *screen; + + /* Input stuff. */ + struct string *prompt; /* Output string for input area. */ + struct string *input; /* Input string for read request. */ + struct raw3270_request *read; /* Single read request. */ + struct raw3270_request *kreset; /* Single keyboard reset request. */ + unsigned char inattr; /* Visible/invisible input. */ + int throttle, attn; /* tty throttle/unthrottle. */ + struct tasklet_struct readlet; /* Tasklet to issue read request. */ + struct kbd_data *kbd; /* key_maps stuff. */ + + /* Escape sequence parsing. */ + int esc_state, esc_ques, esc_npar; + int esc_par[ESCAPE_NPAR]; + unsigned int saved_cx, saved_cy; + unsigned int saved_highlight, saved_f_color; + + /* Command recalling. */ + struct list_head rcl_lines; /* List of recallable lines. */ + struct list_head *rcl_walk; /* Point in rcl_lines list. */ + int rcl_nr, rcl_max; /* Number/max number of rcl_lines. */ + + /* Character array for put_char/flush_chars. */ + unsigned int char_count; + char char_buf[TTY3270_CHAR_BUF_SIZE]; +}; + +/* 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 15 + +static void tty3270_update(struct tty3270 *); + +/* + * Setup timeout for a device. On timeout trigger an update. + */ +void +tty3270_set_timer(struct tty3270 *tp, int expires) +{ + if (expires == 0) { + if (timer_pending(&tp->timer)) { + raw3270_put_view(&tp->view); + del_timer(&tp->timer); + } + return; + } + if (timer_pending(&tp->timer)) { + if (mod_timer(&tp->timer, jiffies + expires)) + return; + } + raw3270_get_view(&tp->view); + tp->timer.function = (void (*)(unsigned long)) tty3270_update; + tp->timer.data = (unsigned long) tp; + tp->timer.expires = jiffies + expires; + add_timer(&tp->timer); +} + +/* + * The input line are the two last lines of the screen. + */ +static void +tty3270_update_prompt(struct tty3270 *tp, char *input, int count) +{ + struct string *line; + unsigned int off; + + line = tp->prompt; + if (count != 0) + line->string[5] = TF_INMDT; + else + line->string[5] = tp->inattr; + if (count > tp->view.cols * 2 - 11) + count = tp->view.cols * 2 - 11; + memcpy(line->string + 6, input, count); + line->string[6 + count] = TO_IC; + /* Clear to end of input line. */ + if (count < tp->view.cols * 2 - 11) { + line->string[7 + count] = TO_RA; + line->string[10 + count] = 0; + off = tp->view.cols * tp->view.rows - 9; + raw3270_buffer_address(tp->view.dev, line->string+count+8, off); + line->len = 11 + count; + } else + line->len = 7 + count; + tp->update_flags |= TTY_UPDATE_INPUT; +} + +static void +tty3270_create_prompt(struct tty3270 *tp) +{ + static const unsigned char blueprint[] = + { TO_SBA, 0, 0, 0x6e, TO_SF, TF_INPUT, + /* empty input string */ + TO_IC, TO_RA, 0, 0, 0 }; + struct string *line; + unsigned int offset; + + line = alloc_string(&tp->freemem, + sizeof(blueprint) + tp->view.cols * 2 - 9); + tp->prompt = line; + tp->inattr = TF_INPUT; + /* Copy blueprint to status line */ + memcpy(line->string, blueprint, sizeof(blueprint)); + line->len = sizeof(blueprint); + /* Set output offsets. */ + offset = tp->view.cols * (tp->view.rows - 2); + raw3270_buffer_address(tp->view.dev, line->string + 1, offset); + offset = tp->view.cols * tp->view.rows - 9; + raw3270_buffer_address(tp->view.dev, line->string + 8, offset); + + /* Allocate input string for reading. */ + tp->input = alloc_string(&tp->freemem, tp->view.cols * 2 - 9 + 6); +} + +/* + * 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) +{ + char *str; + + str = (tp->nr_up != 0) ? "History" : "Running"; + memcpy(tp->status->string + 8, str, 7); + codepage_convert(tp->view.ascebc, tp->status->string + 8, 7); + tp->update_flags |= TTY_UPDATE_STATUS; +} + +static void +tty3270_create_status(struct tty3270 * tp) +{ + static const unsigned char blueprint[] = + { TO_SBA, 0, 0, TO_SF, TF_LOG, TO_SA, TAT_COLOR, TAC_GREEN, + 0, 0, 0, 0, 0, 0, 0, TO_SF, TF_LOG, TO_SA, TAT_COLOR, + TAC_RESET }; + struct string *line; + unsigned int offset; + + line = alloc_string(&tp->freemem,sizeof(blueprint)); + tp->status = line; + /* Copy blueprint to status line */ + memcpy(line->string, blueprint, sizeof(blueprint)); + /* Set address to start of status string (= last 9 characters). */ + offset = tp->view.cols * tp->view.rows - 9; + raw3270_buffer_address(tp->view.dev, line->string + 1, offset); +} + +/* + * 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) +{ + unsigned char *cp; + + raw3270_buffer_address(tp->view.dev, line->string + 1, + tp->view.cols * nr); + cp = line->string + line->len - 4; + if (*cp == TO_RA) + raw3270_buffer_address(tp->view.dev, cp + 1, + tp->view.cols * (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 = tp->view.rows - 3; + 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. + */ +static struct string * +tty3270_alloc_string(struct tty3270 *tp, size_t size) +{ + struct string *s, *n; + + s = alloc_string(&tp->freemem, size); + if (s) + return s; + list_for_each_entry_safe(s, n, &tp->lines, list) { + BUG_ON(tp->nr_lines <= tp->view.rows - 2); + 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 + tp->view.rows - 2 >= tp->nr_lines) { + tp->nr_up = tp->nr_lines - tp->view.rows + 2; + 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_COLOR, 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++; +} + +/* + * Write request completion callback. + */ +static void +tty3270_write_callback(struct raw3270_request *rq, void *data) +{ + struct tty3270 *tp; + + tp = (struct tty3270 *) rq->view; + if (rq->rc != 0) { + /* Write wasn't successfull. Refresh all. */ + tty3270_rebuild_update(tp); + tp->update_flags = TTY_UPDATE_ALL; + tty3270_set_timer(tp, 1); + } + raw3270_request_reset(rq); + xchg(&tp->write, rq); +} + +/* + * Update 3270 display. + */ +static void +tty3270_update(struct tty3270 *tp) +{ + static char invalid_sba[2] = { 0xff, 0xff }; + struct raw3270_request *wrq; + unsigned long updated; + struct string *s, *n; + char *sba, *str; + int rc, len; + + wrq = xchg(&tp->write, 0); + if (!wrq) { + tty3270_set_timer(tp, 1); + return; + } + + spin_lock(&tp->view.lock); + updated = 0; + if (tp->update_flags & TTY_UPDATE_ERASE) { + /* Use erase write alternate to erase display. */ + raw3270_request_set_cmd(wrq, TC_EWRITEA); + updated |= TTY_UPDATE_ERASE; + } else + raw3270_request_set_cmd(wrq, TC_WRITE); + + raw3270_request_add_data(wrq, &tp->wcc, 1); + tp->wcc = TW_NONE; + + /* + * Update status line. + */ + if (tp->update_flags & TTY_UPDATE_STATUS) + if (raw3270_request_add_data(wrq, tp->status->string, + tp->status->len) == 0) + updated |= TTY_UPDATE_STATUS; + + /* + * Write input line. + */ + if (tp->update_flags & TTY_UPDATE_INPUT) + if (raw3270_request_add_data(wrq, tp->prompt->string, + 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); + sba = s->string + s->len - 3; + } + if (list_empty(&tp->update)) + updated |= TTY_UPDATE_LIST; + } + wrq->callback = tty3270_write_callback; + rc = raw3270_start(&tp->view, wrq); + if (rc == 0) { + tp->update_flags &= ~updated; + if (tp->update_flags) + tty3270_set_timer(tp, 1); + } else { + raw3270_request_reset(wrq); + xchg(&tp->write, wrq); + } + spin_unlock(&tp->view.lock); + raw3270_put_view(&tp->view); +} + +/* + * Command recalling. + */ +static void +tty3270_rcl_add(struct tty3270 *tp, char *input, int len) +{ + struct string *s; + + tp->rcl_walk = 0; + if (len <= 0) + return; + if (tp->rcl_nr >= tp->rcl_max) { + s = list_entry(tp->rcl_lines.next, struct string, list); + list_del(&s->list); + free_string(&tp->freemem, s); + tp->rcl_nr--; + } + s = tty3270_alloc_string(tp, len); + memcpy(s->string, input, len); + list_add_tail(&s->list, &tp->rcl_lines); + tp->rcl_nr++; +} + +static void +tty3270_rcl_backward(struct kbd_data *kbd) +{ + struct tty3270 *tp; + struct string *s; + + tp = kbd->tty->driver_data; + spin_lock_bh(&tp->view.lock); + if (tp->inattr == TF_INPUT) { + if (tp->rcl_walk && tp->rcl_walk->prev != &tp->rcl_lines) + tp->rcl_walk = tp->rcl_walk->prev; + else if (!list_empty(&tp->rcl_lines)) + tp->rcl_walk = tp->rcl_lines.prev; + s = tp->rcl_walk ? + list_entry(tp->rcl_walk, struct string, list) : 0; + if (tp->rcl_walk) { + s = list_entry(tp->rcl_walk, struct string, list); + tty3270_update_prompt(tp, s->string, s->len); + } else + tty3270_update_prompt(tp, 0, 0); + tty3270_set_timer(tp, 1); + } + spin_unlock_bh(&tp->view.lock); +} + +/* + * Deactivate tty view. + */ +static void +tty3270_exit_tty(struct kbd_data *kbd) +{ + struct tty3270 *tp; + + tp = kbd->tty->driver_data; + raw3270_deactivate_view(&tp->view); +} + +/* + * Scroll forward in history. + */ +static void +tty3270_scroll_forward(struct kbd_data *kbd) +{ + struct tty3270 *tp; + int nr_up; + + tp = kbd->tty->driver_data; + spin_lock_bh(&tp->view.lock); + nr_up = tp->nr_up - tp->view.rows + 2; + if (nr_up < 0) + nr_up = 0; + if (nr_up != tp->nr_up) { + tp->nr_up = nr_up; + tty3270_rebuild_update(tp); + tty3270_update_status(tp); + tty3270_set_timer(tp, 1); + } + spin_unlock_bh(&tp->view.lock); +} + +/* + * Scroll backward in history. + */ +static void +tty3270_scroll_backward(struct kbd_data *kbd) +{ + struct tty3270 *tp; + int nr_up; + + tp = kbd->tty->driver_data; + spin_lock_bh(&tp->view.lock); + nr_up = tp->nr_up + tp->view.rows - 2; + if (nr_up + tp->view.rows - 2 > tp->nr_lines) + nr_up = tp->nr_lines - tp->view.rows + 2; + if (nr_up != tp->nr_up) { + tp->nr_up = nr_up; + tty3270_rebuild_update(tp); + tty3270_update_status(tp); + tty3270_set_timer(tp, 1); + } + spin_unlock_bh(&tp->view.lock); +} + +/* + * Pass input line to tty. + */ +static void +tty3270_read_tasklet(struct raw3270_request *rrq) +{ + static char kreset_data = TW_KR; + struct tty3270 *tp; + char *input; + int len; + + tp = (struct tty3270 *) rrq->view; + spin_lock_bh(&tp->view.lock); + /* + * Two AID keys are special: For 0x7d (enter) the input line + * has to be emitted to the tty and for 0x6d the screen + * needs to be redrawn. + */ + input = 0; + len = 0; + if (tp->input->string[0] == 0x7d) { + /* Enter: write input to tty. */ + input = tp->input->string + 6; + len = tp->input->len - 6 - rrq->rescnt; + if (tp->inattr != TF_INPUTN) + tty3270_rcl_add(tp, input, len); + if (tp->nr_up > 0) { + tp->nr_up = 0; + tty3270_rebuild_update(tp); + tty3270_update_status(tp); + } + /* Clear input area. */ + tty3270_update_prompt(tp, 0, 0); + tty3270_set_timer(tp, 1); + } else if (tp->input->string[0] == 0x6d) { + /* Display has been cleared. Redraw. */ + tty3270_rebuild_update(tp); + tp->update_flags = TTY_UPDATE_ALL; + tty3270_set_timer(tp, 1); + } + spin_unlock_bh(&tp->view.lock); + + /* Start keyboard reset command. */ + raw3270_request_reset(tp->kreset); + raw3270_request_set_cmd(tp->kreset, TC_WRITE); + raw3270_request_add_data(tp->kreset, &kreset_data, 1); + raw3270_start(&tp->view, tp->kreset); + + /* Emit input string. */ + if (tp->tty) { + while (len-- > 0) + kbd_keycode(tp->kbd, *input++); + /* Emit keycode for AID byte. */ + kbd_keycode(tp->kbd, 256 + tp->input->string[0]); + } + + raw3270_request_reset(rrq); + xchg(&tp->read, rrq); + raw3270_put_view(&tp->view); +} + +/* + * Read request completion callback. + */ +static void +tty3270_read_callback(struct raw3270_request *rq, void *data) +{ + raw3270_get_view(rq->view); + /* Schedule tasklet to pass input to tty. */ + tasklet_schedule(&((struct tty3270 *) rq->view)->readlet); +} + +/* + * Issue a read request. Call with device lock. + */ +static void +tty3270_issue_read(struct tty3270 *tp, int lock) +{ + struct raw3270_request *rrq; + int rc; + + rrq = xchg(&tp->read, 0); + if (!rrq) + /* Read already scheduled. */ + return; + rrq->callback = tty3270_read_callback; + rrq->callback_data = tp; + raw3270_request_set_cmd(rrq, TC_READMOD); + raw3270_request_set_data(rrq, tp->input->string, tp->input->len); + /* Issue the read modified request. */ + if (lock) { + rc = raw3270_start(&tp->view, rrq); + } else + rc = raw3270_start_irq(&tp->view, rrq); + if (rc) { + raw3270_request_reset(rrq); + xchg(&tp->read, rrq); + } +} + +/* + * Switch to the tty view. + */ +static int +tty3270_activate(struct raw3270_view *view) +{ + struct tty3270 *tp; + unsigned long flags; + + tp = (struct tty3270 *) view; + spin_lock_irqsave(&tp->view.lock, flags); + tp->nr_up = 0; + tty3270_rebuild_update(tp); + tty3270_update_status(tp); + tp->update_flags = TTY_UPDATE_ALL; + tty3270_set_timer(tp, 1); + spin_unlock_irqrestore(&tp->view.lock, flags); + start_tty(tp->tty); + return 0; +} + +static void +tty3270_deactivate(struct raw3270_view *view) +{ + struct tty3270 *tp; + + tp = (struct tty3270 *) view; + if (tp && tp->tty) + stop_tty(tp->tty); +} + +static int +tty3270_irq(struct tty3270 *tp, struct raw3270_request *rq, struct irb *irb) +{ + /* Handle ATTN. Schedule tasklet to read aid. */ + if (irb->scsw.dstat & DEV_STAT_ATTENTION) { + if (!tp->throttle) + tty3270_issue_read(tp, 0); + else + tp->attn = 1; + } + + if (rq) { + if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK) + rq->rc = -EIO; + else + /* Normal end. Copy residual count. */ + rq->rescnt = irb->scsw.count; + } + return RAW3270_IO_DONE; +} + +/* + * Allocate tty3270 structure. + */ +static struct tty3270 * +tty3270_alloc_view(void) +{ + struct tty3270 *tp; + int pages; + + tp = kmalloc(sizeof(struct tty3270),GFP_KERNEL); + if (!tp) + goto out_err; + memset(tp, 0, sizeof(struct tty3270)); + tp->freemem_pages = + kmalloc(sizeof(void *) * TTY3270_STRING_PAGES, GFP_KERNEL); + if (!tp->freemem_pages) + goto out_tp; + INIT_LIST_HEAD(&tp->freemem); + for (pages = 0; pages < TTY3270_STRING_PAGES; pages++) { + tp->freemem_pages[pages] = (void *) + __get_free_pages(GFP_KERNEL|GFP_DMA, 0); + if (!tp->freemem_pages[pages]) + goto out_pages; + add_string_memory(&tp->freemem, + tp->freemem_pages[pages], PAGE_SIZE); + } + tp->write = raw3270_request_alloc(TTY3270_OUTPUT_BUFFER_SIZE); + if (!tp->write) + goto out_pages; + tp->read = raw3270_request_alloc(0); + if (!tp->read) + goto out_write; + tp->kreset = raw3270_request_alloc(1); + if (!tp->kreset) + goto out_read; + tp->kbd = kbd_alloc(); + if (!tp->kbd) + goto out_reset; + return tp; + +out_reset: + raw3270_request_free(tp->kreset); +out_read: + raw3270_request_free(tp->read); +out_write: + raw3270_request_free(tp->write); +out_pages: + while (pages--) + free_pages((unsigned long) tp->freemem_pages[pages], 0); + kfree(tp->freemem_pages); +out_tp: + kfree(tp); +out_err: + return ERR_PTR(-ENOMEM); +} + +/* + * Free tty3270 structure. + */ +static void +tty3270_free_view(struct tty3270 *tp) +{ + int pages; + + kbd_free(tp->kbd); + raw3270_request_free(tp->kreset); + raw3270_request_free(tp->read); + raw3270_request_free(tp->write); + for (pages = 0; pages < TTY3270_STRING_PAGES; pages++) + free_pages((unsigned long) tp->freemem_pages[pages], 0); + kfree(tp->freemem_pages); + kfree(tp); +} + +/* + * Allocate tty3270 screen. + */ +static int +tty3270_alloc_screen(struct tty3270 *tp) +{ + unsigned long size; + int lines; + + size = sizeof(struct tty3270_line) * (tp->view.rows - 2); + tp->screen = kmalloc(size, GFP_KERNEL); + if (!tp->screen) + goto out_err; + memset(tp->screen, 0, size); + for (lines = 0; lines < tp->view.rows - 2; lines++) { + size = sizeof(struct tty3270_cell) * tp->view.cols; + tp->screen[lines].cells = kmalloc(size, GFP_KERNEL); + if (!tp->screen[lines].cells) + goto out_screen; + memset(tp->screen[lines].cells, 0, size); + } + return 0; +out_screen: + while (lines--) + kfree(tp->screen[lines].cells); + kfree(tp->screen); +out_err: + return -ENOMEM; +} + +/* + * Free tty3270 screen. + */ +static void +tty3270_free_screen(struct tty3270 *tp) +{ + int lines; + + for (lines = 0; lines < tp->view.rows - 2; lines++) + kfree(tp->screen[lines].cells); + kfree(tp->screen); +} + +/* + * Unlink tty3270 data structure from tty. + */ +static void +tty3270_release(struct raw3270_view *view) +{ + struct tty3270 *tp; + struct tty_struct *tty; + + tp = (struct tty3270 *) view; + tty = tp->tty; + if (tty) { + tty->driver_data = 0; + tp->tty = tp->kbd->tty = 0; + tty_hangup(tty); + raw3270_put_view(&tp->view); + } +} + +/* + * Free tty3270 data structure + */ +static void +tty3270_free(struct raw3270_view *view) +{ + tty3270_free_screen((struct tty3270 *) view); + tty3270_free_view((struct tty3270 *) view); +} + +struct raw3270_fn tty3270_fn = { + .activate = tty3270_activate, + .deactivate = tty3270_deactivate, + .intv = (void *) tty3270_irq, + .release = tty3270_release, + .free = tty3270_free +}; + +/* + * This routine is called whenever a 3270 tty is opened. + */ +static int +tty3270_open(struct tty_struct *tty, struct file * filp) +{ + struct tty3270 *tp; + int i, rc; + + if (tty->count > 1) + return 0; + /* Check if the tty3270 is already there. */ + tp = (struct tty3270 *) raw3270_find_view(&tty3270_fn, tty->index); + if (!IS_ERR(tp)) { + tty->driver_data = tp; + tty->winsize.ws_row = tp->view.rows - 2; + tty->winsize.ws_col = tp->view.cols; + tty->low_latency = 0; + tp->tty = tty; + tp->kbd->tty = tty; + tp->inattr = TF_INPUT; + return 0; + } + + /* Allocate tty3270 structure on first open. */ + tp = tty3270_alloc_view(); + if (IS_ERR(tp)) + return PTR_ERR(tp); + + INIT_LIST_HEAD(&tp->lines); + INIT_LIST_HEAD(&tp->update); + INIT_LIST_HEAD(&tp->rcl_lines); + tp->rcl_max = 20; + init_timer(&tp->timer); + tasklet_init(&tp->readlet, + (void (*)(unsigned long)) tty3270_read_tasklet, + (unsigned long) tp->read); + + rc = raw3270_add_view(&tp->view, &tty3270_fn, tty->index); + if (rc) { + tty3270_free_view(tp); + return rc; + } + + rc = tty3270_alloc_screen(tp); + if (rc) { + raw3270_del_view(&tp->view); + raw3270_put_view(&tp->view); + return rc; + } + + tp->tty = tty; + tty->low_latency = 0; + tty->driver_data = tp; + tty->winsize.ws_row = tp->view.rows - 2; + tty->winsize.ws_col = tp->view.cols; + + tty3270_create_prompt(tp); + tty3270_create_status(tp); + tty3270_update_status(tp); + + /* Create blank line for every line in the tty output area. */ + for (i = 0; i < tp->view.rows - 2; i++) + tty3270_blank_line(tp); + + tp->kbd->tty = tty; + tp->kbd->fn_handler[KVAL(K_INCRCONSOLE)] = tty3270_exit_tty; + tp->kbd->fn_handler[KVAL(K_SCROLLBACK)] = tty3270_scroll_backward; + tp->kbd->fn_handler[KVAL(K_SCROLLFORW)] = tty3270_scroll_forward; + tp->kbd->fn_handler[KVAL(K_CONS)] = tty3270_rcl_backward; + kbd_ascebc(tp->kbd, tp->view.ascebc); + + raw3270_activate_view(&tp->view); + return 0; +} + +/* + * This routine is called when the 3270 tty is closed. We wait + * for the remaining request to be completed. Then we clean up. + */ +static void +tty3270_close(struct tty_struct *tty, struct file * filp) +{ + struct tty3270 *tp; + + if (tty->count > 1) + return; + tp = (struct tty3270 *) tty->driver_data; + if (tp) { + tty->driver_data = 0; + tp->tty = tp->kbd->tty = 0; + raw3270_put_view(&tp->view); + } +} + +/* + * We always have room. + */ +static int +tty3270_write_room(struct tty_struct *tty) +{ + return INT_MAX; +} + +/* + * Insert character into the screen at the current position with the + * current color and highlight. This function does NOT do cursor movement. + */ +static void +tty3270_put_character(struct tty3270 *tp, char ch) +{ + struct tty3270_line *line; + struct tty3270_cell *cell; + + line = tp->screen + tp->cy; + if (line->len <= tp->cx) { + while (line->len < tp->cx) { + cell = line->cells + line->len; + cell->character = tp->view.ascebc[' ']; + cell->highlight = tp->highlight; + cell->f_color = tp->f_color; + line->len++; + } + line->len++; + } + cell = line->cells + tp->cx; + cell->character = tp->view.ascebc[(unsigned int) ch]; + cell->highlight = tp->highlight; + cell->f_color = tp->f_color; +} + +/* + * Convert a tty3270_line to a 3270 data fragment usable for output. + */ +static void +tty3270_convert_line(struct tty3270 *tp, int line_nr) +{ + struct tty3270_line *line; + struct tty3270_cell *cell; + struct string *s, *n; + unsigned char highlight; + unsigned char f_color; + char *cp; + int flen, i; + + /* Determine how long the fragment will be. */ + flen = 3; /* Prefix (TO_SBA). */ + line = tp->screen + line_nr; + flen += line->len; + highlight = TAX_RESET; + f_color = TAC_RESET; + for (i = 0, cell = line->cells; i < line->len; i++, cell++) { + if (cell->highlight != highlight) { + flen += 3; /* TO_SA to switch highlight. */ + highlight = cell->highlight; + } + if (cell->f_color != f_color) { + flen += 3; /* TO_SA to switch color. */ + f_color = cell->f_color; + } + } + if (highlight != TAX_RESET) + flen += 3; /* TO_SA to reset hightlight. */ + if (f_color != TAC_RESET) + flen += 3; /* TO_SA to reset color. */ + if (line->len < tp->view.cols) + flen += 4; /* Postfix (TO_RA). */ + + /* Find the line in the list. */ + i = tp->view.rows - 2 - 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) { + /* Reallocate string. */ + n = tty3270_alloc_string(tp, flen); + 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); + s = n; + } + + /* Write 3270 data fragment. */ + cp = s->string; + *cp++ = TO_SBA; + *cp++ = 0; + *cp++ = 0; + + highlight = TAX_RESET; + f_color = TAC_RESET; + for (i = 0, cell = line->cells; i < line->len; i++, cell++) { + if (cell->highlight != highlight) { + *cp++ = TO_SA; + *cp++ = TAT_EXTHI; + *cp++ = cell->highlight; + highlight = cell->highlight; + } + if (cell->f_color != f_color) { + *cp++ = TO_SA; + *cp++ = TAT_COLOR; + *cp++ = cell->f_color; + f_color = cell->f_color; + } + *cp++ = cell->character; + } + if (highlight != TAX_RESET) { + *cp++ = TO_SA; + *cp++ = TAT_EXTHI; + *cp++ = TAX_RESET; + } + if (f_color != TAC_RESET) { + *cp++ = TO_SA; + *cp++ = TAT_COLOR; + *cp++ = TAC_RESET; + } + if (line->len < tp->view.cols) { + *cp++ = TO_RA; + *cp++ = 0; + *cp++ = 0; + *cp++ = 0; + } + + if (tp->nr_up + line_nr < tp->view.rows - 2) { + /* 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; + } + } +} + +/* + * Do carriage return. + */ +static void +tty3270_cr(struct tty3270 *tp) +{ + tp->cx = 0; +} + +/* + * Do line feed. + */ +static void +tty3270_lf(struct tty3270 *tp) +{ + struct tty3270_line temp; + int i; + + tty3270_convert_line(tp, tp->cy); + if (tp->cy < tp->view.rows - 3) { + tp->cy++; + return; + } + /* Last line just filled up. Add new, blank line. */ + tty3270_blank_line(tp); + temp = tp->screen[0]; + temp.len = 0; + for (i = 0; i < tp->view.rows - 3; i++) + tp->screen[i] = tp->screen[i+1]; + tp->screen[tp->view.rows - 3] = temp; + tty3270_rebuild_update(tp); +} + +static void +tty3270_ri(struct tty3270 *tp) +{ + if (tp->cy > 0) { + tty3270_convert_line(tp, tp->cy); + tp->cy--; + } +} + +/* + * Insert characters at current position. + */ +static void +tty3270_insert_characters(struct tty3270 *tp, int n) +{ + struct tty3270_line *line; + int k; + + line = tp->screen + tp->cy; + while (line->len < tp->cx) { + line->cells[line->len].character = tp->view.ascebc[' ']; + line->cells[line->len].highlight = TAX_RESET; + line->cells[line->len].f_color = TAC_RESET; + line->len++; + } + if (n > tp->view.cols - tp->cx) + n = tp->view.cols - tp->cx; + k = min_t(int, line->len - tp->cx, tp->view.cols - tp->cx - n); + while (k--) + line->cells[tp->cx + n + k] = line->cells[tp->cx + k]; + line->len += n; + if (line->len > tp->view.cols) + line->len = tp->view.cols; + while (n-- > 0) { + line->cells[tp->cx + n].character = tp->view.ascebc[' ']; + line->cells[tp->cx + n].highlight = tp->highlight; + line->cells[tp->cx + n].f_color = tp->f_color; + } +} + +/* + * Delete characters at current position. + */ +static void +tty3270_delete_characters(struct tty3270 *tp, int n) +{ + struct tty3270_line *line; + int i; + + line = tp->screen + tp->cy; + if (line->len <= tp->cx) + return; + if (line->len - tp->cx <= n) { + line->len = tp->cx; + return; + } + for (i = tp->cx; i + n < line->len; i++) + line->cells[i] = line->cells[i + n]; + line->len -= n; +} + +/* + * Erase characters at current position. + */ +static void +tty3270_erase_characters(struct tty3270 *tp, int n) +{ + struct tty3270_line *line; + struct tty3270_cell *cell; + + line = tp->screen + tp->cy; + while (line->len > tp->cx && n-- > 0) { + cell = line->cells + tp->cx++; + cell->character = ' '; + cell->highlight = TAX_RESET; + cell->f_color = TAC_RESET; + } + tp->cx += n; + tp->cx = min_t(int, tp->cx, tp->view.cols - 1); +} + +/* + * Erase line, 3 different cases: + * Esc [ 0 K Erase from current position to end of line inclusive + * Esc [ 1 K Erase from beginning of line to current position inclusive + * Esc [ 2 K Erase entire line (without moving cursor) + */ +static void +tty3270_erase_line(struct tty3270 *tp, int mode) +{ + struct tty3270_line *line; + struct tty3270_cell *cell; + int i; + + line = tp->screen + tp->cy; + if (mode == 0) + line->len = tp->cx; + else if (mode == 1) { + for (i = 0; i < tp->cx; i++) { + cell = line->cells + i; + cell->character = ' '; + cell->highlight = TAX_RESET; + cell->f_color = TAC_RESET; + } + if (line->len <= tp->cx) + line->len = tp->cx + 1; + } else if (mode == 2) + line->len = 0; + tty3270_convert_line(tp, tp->cy); +} + +/* + * Erase display, 3 different cases: + * Esc [ 0 J Erase from current position to bottom of screen inclusive + * Esc [ 1 J Erase from top of screen to current position inclusive + * Esc [ 2 J Erase entire screen (without moving the cursor) + */ +static void +tty3270_erase_display(struct tty3270 *tp, int mode) +{ + int i; + + if (mode == 0) { + tty3270_erase_line(tp, 0); + for (i = tp->cy + 1; i < tp->view.rows - 2; i++) { + tp->screen[i].len = 0; + tty3270_convert_line(tp, i); + } + } else if (mode == 1) { + for (i = 0; i < tp->cy; i++) { + tp->screen[i].len = 0; + tty3270_convert_line(tp, i); + } + tty3270_erase_line(tp, 1); + } else if (mode == 2) { + for (i = 0; i < tp->view.rows - 2; i++) { + tp->screen[i].len = 0; + tty3270_convert_line(tp, i); + } + } + tty3270_rebuild_update(tp); +} + +/* + * Set attributes found in an escape sequence. + * Esc [ ; ; ... m + */ +static void +tty3270_set_attributes(struct tty3270 *tp) +{ + static unsigned char f_colors[] = { + TAC_DEFAULT, TAC_RED, TAC_GREEN, TAC_YELLOW, TAC_BLUE, + TAC_PINK, TAC_TURQ, TAC_WHITE, 0, TAC_DEFAULT + }; + int i, attr; + + for (i = 0; i <= tp->esc_npar; i++) { + attr = tp->esc_par[i]; + switch (attr) { + case 0: /* Reset */ + tp->highlight = TAX_RESET; + tp->f_color = TAC_RESET; + break; + /* Highlight. */ + case 4: /* Start underlining. */ + tp->highlight = TAX_UNDER; + break; + case 5: /* Start blink. */ + tp->highlight = TAX_BLINK; + break; + case 7: /* Start reverse. */ + tp->highlight = TAX_REVER; + break; + case 24: /* End underlining */ + if (tp->highlight == TAX_UNDER) + tp->highlight = TAX_RESET; + break; + case 25: /* End blink. */ + if (tp->highlight == TAX_BLINK) + tp->highlight = TAX_RESET; + break; + case 27: /* End reverse. */ + if (tp->highlight == TAX_REVER) + tp->highlight = TAX_RESET; + break; + /* Foreground color. */ + case 30: /* Black */ + case 31: /* Red */ + case 32: /* Green */ + case 33: /* Yellow */ + case 34: /* Blue */ + case 35: /* Magenta */ + case 36: /* Cyan */ + case 37: /* White */ + case 39: /* Black */ + tp->f_color = f_colors[attr - 30]; + break; + } + } +} + +static inline int +tty3270_getpar(struct tty3270 *tp, int ix) +{ + return (tp->esc_par[ix] > 0) ? tp->esc_par[ix] : 1; +} + +static void +tty3270_goto_xy(struct tty3270 *tp, int cx, int cy) +{ + tp->cx = min_t(int, tp->view.cols - 1, max_t(int, 0, cx)); + cy = min_t(int, tp->view.rows - 3, max_t(int, 0, cy)); + if (cy != tp->cy) { + tty3270_convert_line(tp, tp->cy); + tp->cy = cy; + } +} + +/* + * Process escape sequences. Known sequences: + * Esc 7 Save Cursor Position + * Esc 8 Restore Cursor Position + * Esc [ Pn ; Pn ; .. m Set attributes + * Esc [ Pn ; Pn H Cursor Position + * Esc [ Pn ; Pn f Cursor Position + * Esc [ Pn A Cursor Up + * Esc [ Pn B Cursor Down + * Esc [ Pn C Cursor Forward + * Esc [ Pn D Cursor Backward + * Esc [ Pn G Cursor Horizontal Absolute + * Esc [ Pn X Erase Characters + * Esc [ Ps J Erase in Display + * Esc [ Ps K Erase in Line + * // FIXME: add all the new ones. + * + * Pn is a numeric parameter, a string of zero or more decimal digits. + * Ps is a selective parameter. + */ +static void +tty3270_escape_sequence(struct tty3270 *tp, char ch) +{ + enum { ESnormal, ESesc, ESsquare, ESgetpars }; + + if (tp->esc_state == ESnormal) { + if (ch == 0x1b) + /* Starting new escape sequence. */ + tp->esc_state = ESesc; + return; + } + if (tp->esc_state == ESesc) { + tp->esc_state = ESnormal; + switch (ch) { + case '[': + tp->esc_state = ESsquare; + break; + case 'E': + tty3270_cr(tp); + tty3270_lf(tp); + break; + case 'M': + tty3270_ri(tp); + break; + case 'D': + tty3270_lf(tp); + break; + case 'Z': /* Respond ID. */ + kbd_puts_queue(tp->tty, "\033[?6c"); + break; + case '7': /* Save cursor position. */ + tp->saved_cx = tp->cx; + tp->saved_cy = tp->cy; + tp->saved_highlight = tp->highlight; + tp->saved_f_color = tp->f_color; + break; + case '8': /* Restore cursor position. */ + tty3270_convert_line(tp, tp->cy); + tty3270_goto_xy(tp, tp->saved_cx, tp->saved_cy); + tp->highlight = tp->saved_highlight; + tp->f_color = tp->saved_f_color; + break; + case 'c': /* Reset terminal. */ + tp->cx = tp->saved_cx = 0; + tp->cy = tp->saved_cy = 0; + tp->highlight = tp->saved_highlight = TAX_RESET; + tp->f_color = tp->saved_f_color = TAC_RESET; + tty3270_erase_display(tp, 2); + break; + } + return; + } + if (tp->esc_state == ESsquare) { + tp->esc_state = ESgetpars; + memset(tp->esc_par, 0, sizeof(tp->esc_par)); + tp->esc_npar = 0; + tp->esc_ques = (ch == '?'); + if (tp->esc_ques) + return; + } + if (tp->esc_state == ESgetpars) { + if (ch == ';' && tp->esc_npar < ESCAPE_NPAR - 1) { + tp->esc_npar++; + return; + } + if (ch >= '0' && ch <= '9') { + tp->esc_par[tp->esc_npar] *= 10; + tp->esc_par[tp->esc_npar] += ch - '0'; + return; + } + } + tp->esc_state = ESnormal; + if (ch == 'n' && !tp->esc_ques) { + if (tp->esc_par[0] == 5) /* Status report. */ + kbd_puts_queue(tp->tty, "\033[0n"); + else if (tp->esc_par[0] == 6) { /* Cursor report. */ + char buf[40]; + sprintf(buf, "\033[%d;%dR", tp->cy + 1, tp->cx + 1); + kbd_puts_queue(tp->tty, buf); + } + return; + } + if (tp->esc_ques) + return; + switch (ch) { + case 'm': + tty3270_set_attributes(tp); + break; + case 'H': /* Set cursor position. */ + case 'f': + tty3270_goto_xy(tp, tty3270_getpar(tp, 1) - 1, + tty3270_getpar(tp, 0) - 1); + break; + case 'd': /* Set y position. */ + tty3270_goto_xy(tp, tp->cx, tty3270_getpar(tp, 0) - 1); + break; + case 'A': /* Cursor up. */ + case 'F': + tty3270_goto_xy(tp, tp->cx, tp->cy - tty3270_getpar(tp, 0)); + break; + case 'B': /* Cursor down. */ + case 'e': + case 'E': + tty3270_goto_xy(tp, tp->cx, tp->cy + tty3270_getpar(tp, 0)); + break; + case 'C': /* Cursor forward. */ + case 'a': + tty3270_goto_xy(tp, tp->cx + tty3270_getpar(tp, 0), tp->cy); + break; + case 'D': /* Cursor backward. */ + tty3270_goto_xy(tp, tp->cx - tty3270_getpar(tp, 0), tp->cy); + break; + case 'G': /* Set x position. */ + case '`': + tty3270_goto_xy(tp, tty3270_getpar(tp, 0), tp->cy); + break; + case 'X': /* Erase Characters. */ + tty3270_erase_characters(tp, tty3270_getpar(tp, 0)); + break; + case 'J': /* Erase display. */ + tty3270_erase_display(tp, tp->esc_par[0]); + break; + case 'K': /* Erase line. */ + tty3270_erase_line(tp, tp->esc_par[0]); + break; + case 'P': /* Delete characters. */ + tty3270_delete_characters(tp, tty3270_getpar(tp, 0)); + break; + case '@': /* Insert characters. */ + tty3270_insert_characters(tp, tty3270_getpar(tp, 0)); + break; + case 's': /* Save cursor position. */ + tp->saved_cx = tp->cx; + tp->saved_cy = tp->cy; + tp->saved_highlight = tp->highlight; + tp->saved_f_color = tp->f_color; + break; + case 'u': /* Restore cursor position. */ + tty3270_convert_line(tp, tp->cy); + tty3270_goto_xy(tp, tp->saved_cx, tp->saved_cy); + tp->highlight = tp->saved_highlight; + tp->f_color = tp->saved_f_color; + break; + } +} + +/* + * String write routine for 3270 ttys + */ +static void +tty3270_do_write(struct tty3270 *tp, const unsigned char *buf, int count) +{ + int i_msg, i; + + spin_lock_bh(&tp->view.lock); + for (i_msg = 0; !tp->tty->stopped && i_msg < count; i_msg++) { + if (tp->esc_state != 0) { + /* Continue escape sequence. */ + tty3270_escape_sequence(tp, buf[i_msg]); + continue; + } + + switch (buf[i_msg]) { + case 0x07: /* '\a' -- Alarm */ + tp->wcc |= TW_PLUSALARM; + break; + case 0x08: /* Backspace. */ + if (tp->cx > 0) { + tp->cx--; + tty3270_put_character(tp, ' '); + } + break; + case 0x09: /* '\t' -- Tabulate */ + for (i = tp->cx % 8; i < 8; i++) { + if (tp->cx >= tp->view.cols) { + tty3270_cr(tp); + tty3270_lf(tp); + break; + } + tty3270_put_character(tp, ' '); + tp->cx++; + } + break; + case 0x0a: /* '\n' -- New Line */ + tty3270_cr(tp); + tty3270_lf(tp); + break; + case 0x0c: /* '\f' -- Form Feed */ + tty3270_erase_display(tp, 2); + tp->cx = tp->cy = 0; + break; + case 0x0d: /* '\r' -- Carriage Return */ + tp->cx = 0; + break; + case 0x0f: /* SuSE "exit alternate mode" */ + break; + case 0x1b: /* Start escape sequence. */ + tty3270_escape_sequence(tp, buf[i_msg]); + break; + default: /* Insert normal character. */ + if (tp->cx >= tp->view.cols) { + tty3270_cr(tp); + tty3270_lf(tp); + } + tty3270_put_character(tp, buf[i_msg]); + tp->cx++; + 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); + + spin_unlock_bh(&tp->view.lock); +} + +/* + * String write routine for 3270 ttys + */ +static int +tty3270_write(struct tty_struct * tty, int from_user, + const unsigned char *buf, int count) +{ + struct tty3270 *tp; + int length, ret; + + tp = tty->driver_data; + if (!tp) + return 0; + if (tp->char_count > 0) { + tty3270_do_write(tp, tp->char_buf, tp->char_count); + tp->char_count = 0; + } + if (!from_user) { + tty3270_do_write(tp, buf, count); + return count; + } + ret = 0; + while (count > 0) { + length = count < TTY3270_CHAR_BUF_SIZE ? + count : TTY3270_CHAR_BUF_SIZE; + length -= copy_from_user(tp->char_buf, buf, length); + if (length == 0) { + if (!ret) + ret = -EFAULT; + break; + } + tty3270_do_write(tp, tp->char_buf, count); + buf += length; + count -= length; + ret += length; + } + return ret; +} + +/* + * Put single characters to the ttys character buffer + */ +static void +tty3270_put_char(struct tty_struct *tty, unsigned char ch) +{ + struct tty3270 *tp; + + tp = tty->driver_data; + if (!tp) + return; + if (tp->char_count < TTY3270_CHAR_BUF_SIZE) + tp->char_buf[tp->char_count++] = ch; +} + +/* + * Flush all characters from the ttys characeter buffer put there + * by tty3270_put_char. + */ +static void +tty3270_flush_chars(struct tty_struct *tty) +{ + struct tty3270 *tp; + + tp = tty->driver_data; + if (!tp) + return; + if (tp->char_count > 0) { + tty3270_do_write(tp, tp->char_buf, tp->char_count); + tp->char_count = 0; + } +} + +/* + * Returns the number of characters in the output buffer. This is + * used in tty_wait_until_sent to wait until all characters have + * appeared on the screen. + */ +static int +tty3270_chars_in_buffer(struct tty_struct *tty) +{ + return 0; +} + +static void +tty3270_flush_buffer(struct tty_struct *tty) +{ +} + +/* + * Check for visible/invisible input switches + */ +static void +tty3270_set_termios(struct tty_struct *tty, struct termios *old) +{ + struct tty3270 *tp; + int new; + + tp = tty->driver_data; + if (!tp) + return; + spin_lock_bh(&tp->view.lock); + if (L_ICANON(tty)) { + new = L_ECHO(tty) ? TF_INPUT: TF_INPUTN; + if (new != tp->inattr) { + tp->inattr = new; + tty3270_update_prompt(tp, 0, 0); + tty3270_set_timer(tp, 1); + } + } + spin_unlock_bh(&tp->view.lock); +} + +/* + * Disable reading from a 3270 tty + */ +static void +tty3270_throttle(struct tty_struct * tty) +{ + struct tty3270 *tp; + + tp = tty->driver_data; + if (!tp) + return; + tp->throttle = 1; +} + +/* + * Enable reading from a 3270 tty + */ +static void +tty3270_unthrottle(struct tty_struct * tty) +{ + struct tty3270 *tp; + + tp = tty->driver_data; + if (!tp) + return; + tp->throttle = 0; + if (tp->attn) + tty3270_issue_read(tp, 1); +} + +/* + * Hang up the tty device. + */ +static void +tty3270_hangup(struct tty_struct *tty) +{ + // FIXME: implement +} + +static void +tty3270_wait_until_sent(struct tty_struct *tty, int timeout) +{ +} + +static int +tty3270_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct tty3270 *tp; + + tp = tty->driver_data; + if (!tp) + return -ENODEV; + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + return kbd_ioctl(tp->kbd, file, cmd, arg); +} + +static struct tty_operations tty3270_ops = { + .open = tty3270_open, + .close = tty3270_close, + .write = tty3270_write, + .put_char = tty3270_put_char, + .flush_chars = tty3270_flush_chars, + .write_room = tty3270_write_room, + .chars_in_buffer = tty3270_chars_in_buffer, + .flush_buffer = tty3270_flush_buffer, + .throttle = tty3270_throttle, + .unthrottle = tty3270_unthrottle, + .hangup = tty3270_hangup, + .wait_until_sent = tty3270_wait_until_sent, + .ioctl = tty3270_ioctl, + .set_termios = tty3270_set_termios +}; + +void +tty3270_notifier(int index, int active) +{ + if (active) + tty_register_device(tty3270_driver, index, 0); + else + tty_unregister_device(tty3270_driver, index); +} + +/* + * 3270 tty registration code called from tty_init(). + * Most kernel services (incl. kmalloc) are available at this poimt. + */ +int __init +tty3270_init(void) +{ + struct tty_driver *driver; + int ret; + + ret = raw3270_init(); + if (ret) + return ret; + driver = alloc_tty_driver(256); + if (!driver) + return -ENOMEM; + + /* + * Initialize the tty_driver structure + * Entries in tty3270_driver that are NOT initialized: + * proc_entry, set_termios, flush_buffer, set_ldisc, write_proc + */ + driver->owner = THIS_MODULE; + driver->devfs_name = "ttyTUB/"; + driver->driver_name = "ttyTUB"; + driver->name = "ttyTUB"; + driver->major = IBM_TTY3270_MAJOR; + driver->type = TTY_DRIVER_TYPE_SYSTEM; + driver->subtype = SYSTEM_TYPE_TTY; + driver->init_termios = tty_std_termios; + driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_NO_DEVFS; + tty_set_operations(driver, &tty3270_ops); + ret = tty_register_driver(driver); + if (ret) { + printk(KERN_ERR "tty3270 registration failed with %d\n", ret); + put_tty_driver(driver); + return ret; + } + tty3270_driver = driver; + return 0; +} + +static void __exit +tty3270_exit(void) +{ + struct tty_driver *driver; + + driver = tty3270_driver; + tty3270_driver = 0; + tty_unregister_driver(driver); + raw3270_exit(); +} + +MODULE_LICENSE("GPL"); +MODULE_ALIAS_CHARDEV_MAJOR(IBM_TTY3270_MAJOR); + +module_init(tty3270_init); +module_exit(tty3270_exit); diff -puN -L drivers/s390/char/tuball.c drivers/s390/char/tuball.c~s390-08-new-3270-driver /dev/null --- 25/drivers/s390/char/tuball.c +++ /dev/null Thu Apr 11 07:25:15 2002 @@ -1,621 +0,0 @@ -/* - * IBM/3270 Driver -- Copyright (C) 2000 UTS Global LLC - * - * tuball.c -- Initialization, termination, irq lookup - * - * - * - * - * - * Author: Richard Hitt - */ -#include -#include "tubio.h" -#ifndef MODULE -#include -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)) -#include -#include -#else -#include "../../../../arch/s390/kernel/cpcmd.h" -#endif -#endif - -/* Module parameters */ -int tubdebug; -int tubscrolltime = -1; -static int tubxcorrect = 1; /* Do correct ebc<->asc tables */ -#ifdef MODULE -MODULE_PARM(tubdebug, "i"); -MODULE_PARM(tubscrolltime, "i"); -MODULE_PARM(tubxcorrect, "i"); -#endif -/* - * Values for tubdebug and their effects: - * 1 - print in hex on console the first 16 bytes received - * 2 - print address at which array tubminors is allocated - * 4 - attempt to register tty3270_driver - */ -int tubnummins; -tub_t *(*tubminors)[TUBMAXMINS]; -static tub_t *(*(*tubirqs)[256])[256]; -unsigned char tub_ascebc[256]; -unsigned char tub_ebcasc[256]; -static int tubinitminors(void); -static void tubfiniminors(void); -static void tubint(int, void *, struct pt_regs *); - -#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) -static int tubmakemin(int, dev_info_t *); -#else -static int tubmakemin(int, s390_dev_info_t *); -#endif - - -/* Lookup-by-irq functions */ -static int tubaddbyirq(tub_t *, int); -static tub_t *tubfindbyirq(int); -static void tubdelbyirq(tub_t *, int); -static void tubfiniirqs(void); - -/* FIXME: put extern declarations in a header */ -extern int fs3270_init(void); -extern void fs3270_fini(void); -extern int tty3270_init(void); -extern void tty3270_fini(void); - -unsigned char tub_ebcgraf[64] = - { 0x40, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, - 0xc8, 0xc9, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, - 0x50, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, - 0xd8, 0xd9, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, - 0x60, 0x61, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, - 0xe8, 0xe9, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, - 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, - 0xf8, 0xf9, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f }; - -#ifndef MODULE - -/* - * Can't have this driver a module & support console at the same time - */ -#ifdef CONFIG_TN3270_CONSOLE -static struct tty_driver *tub3270_con_device(struct console *, int *); -static void tub3270_con_unblank(void); -static void tub3270_con_write(struct console *, const char *, - unsigned int); - -static struct console tub3270_con = { - "tub3270", /* name */ - tub3270_con_write, /* write */ - NULL, /* read */ - tub3270_con_device, /* device */ - tub3270_con_unblank, /* unblank */ - NULL, /* setup */ - CON_PRINTBUFFER, /* flags */ - 0, /* index */ - 0, /* cflag */ - NULL /* next */ -}; - -static bcb_t tub3270_con_bcb; /* Buffer that receives con writes */ -static spinlock_t tub3270_con_bcblock; /* Lock for the buffer */ -static int tub3270_con_irq = -1; /* set nonneg by _activate() */ -tub_t *tub3270_con_tubp; /* set nonzero by _activate() */ -struct tty_driver tty3270_con_driver; /* for /dev/console at 4, 64 */ - -#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) -int tub3270_con_devno = -1; /* set by tub3270_con_setup() */ -__initfunc(void tub3270_con_setup(char *str, int *ints)) -{ - int vdev; - - vdev = simple_strtoul(str, 0, 16); - if (vdev >= 0 && vdev < 65536) - tub3270_con_devno = vdev; - return; -} - -__initfunc (long tub3270_con_init(long kmem_start, long kmem_end)) -{ - tub3270_con_bcb.bc_len = 65536; - if (!MACHINE_IS_VM && !MACHINE_IS_P390) - return kmem_start; - tub3270_con_bcb.bc_buf = (void *)kmem_start; - kmem_start += tub3270_con_bcb.bc_len; - register_console(&tub3270_con); - return kmem_start; -} -#else -#define tub3270_con_devno console_device - -static void __init tub3270_con_init(void) -{ - tub3270_con_bcb.bc_len = 65536; - if (!CONSOLE_IS_3270) - return; - tub3270_con_bcb.bc_buf = (void *)alloc_bootmem_low( - tub3270_con_bcb.bc_len); - register_console(&tub3270_con); -} -console_initcall(tub3270_con_init); - -#endif - -static struct tty_driver *tub3270_con_device(struct console *conp, int *index) -{ - *index = conp->index + 1; - extern struct tty_driver *tty3270_driver; - return tty3270_driver; -} - -static void -tub3270_con_unblank(void) -{ - /* flush everything: panic has occurred */ -} - -static void -tub3270_con_write(struct console *conp, - const char *buf, unsigned int count) -{ - long flags; - tub_t *tubp = tub3270_con_tubp; - void tty3270_sched_bh(tub_t *); - int rc; - bcb_t obcb; - - obcb.bc_buf = (char *)buf; - obcb.bc_len = obcb.bc_cnt = obcb.bc_wr = - MIN(count, tub3270_con_bcb.bc_len); - obcb.bc_rd = 0; - - spin_lock_irqsave(&tub3270_con_bcblock, flags); - rc = tub3270_movedata(&obcb, &tub3270_con_bcb, 0); - spin_unlock_irqrestore(&tub3270_con_bcblock, flags); - - if (tubp && rc && TUBTRYLOCK(tubp->irq, flags)) { - tty3270_sched_bh(tubp); - TUBUNLOCK(tubp->irq, flags); - } -} - -int tub3270_con_copy(tub_t *tubp) -{ - long flags; - int rc; - - spin_lock_irqsave(&tub3270_con_bcblock, flags); - rc = tub3270_movedata(&tub3270_con_bcb, &tubp->tty_bcb, 0); - spin_unlock_irqrestore(&tub3270_con_bcblock, flags); - return rc; -} -#endif /* CONFIG_TN3270_CONSOLE */ -#endif - -/* - * remove driver: unregister the major number - */ -static void __exit -tub3270_exit(void) -{ - fs3270_fini(); - tty3270_fini(); - tubfiniminors(); -} - -static int -tub3270_is_ours(s390_dev_info_t *dp) -{ - if ((dp->sid_data.cu_type & 0xfff0) == 0x3270) - return 1; - if (dp->sid_data.cu_type == 0x3174) - return 1; - return 0; -} - -/* - * tub3270_init() called by kernel or module initialization - */ -static int __init -tub3270_init(void) -{ - s390_dev_info_t d; - int i, rc; - -#ifdef MODULE - if (tubnummins != 0) { - printk(KERN_ERR "EEEK!! Tube driver cobbigling!!\n"); - return -1; - } -#endif - - /* - * Copy and correct ebcdic - ascii translate tables - */ - memcpy(tub_ascebc, _ascebc, sizeof tub_ascebc); - memcpy(tub_ebcasc, _ebcasc, sizeof tub_ebcasc); - if (tubxcorrect) { - /* correct brackets and circumflex */ - tub_ascebc['['] = 0xad; - tub_ascebc[']'] = 0xbd; - tub_ebcasc[0xad] = '['; - tub_ebcasc[0xbd] = ']'; - tub_ascebc['^'] = 0xb0; - tub_ebcasc[0x5f] = '^'; - } - - rc = tubinitminors(); - if (rc != 0) - return rc; - - if (fs3270_init() || tty3270_init()) { - printk(KERN_ERR "fs3270_init() or tty3270_init() failed\n"); - fs3270_fini(); - tty3270_fini(); - tubfiniminors(); - return -1; - } - - for (i = get_irq_first(); i >= 0; i = get_irq_next(i)) { - if ((rc = get_dev_info_by_irq(i, &d))) - continue; - if (d.status) - continue; - -#ifdef CONFIG_TN3270_CONSOLE -#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) - if (d.sid_data.cu_type == 0x3215 && MACHINE_IS_VM) { - cpcmd("TERM CONMODE 3270", NULL, 0); - d.sid_data.cu_type = 0x3270; - } -#else - if (d.sid_data.cu_type == 0x3215 && CONSOLE_IS_3270) { - cpcmd("TERM CONMODE 3270", NULL, 0); - d.sid_data.cu_type = 0x3270; - } -#endif /* LINUX_VERSION_CODE */ -#endif /* CONFIG_TN3270_CONSOLE */ - if (!tub3270_is_ours(&d)) - continue; - - rc = tubmakemin(i, &d); - if (rc < 0) { - printk(KERN_WARNING - "3270 tube registration ran out of memory" - " after %d devices\n", tubnummins - 1); - break; - } else { - printk(KERN_INFO "3270: %.4x on sch %d, minor %d\n", - d.devno, d.irq, rc); - } - } - - return 0; -} - -/* - * tub3270_movedata(bcb_t *, bcb_t *) -- Move data stream - */ -int -tub3270_movedata(bcb_t *ib, bcb_t *ob, int fromuser) -{ - int count; /* Total move length */ - int rc; - - rc = count = MIN(ib->bc_cnt, ob->bc_len - ob->bc_cnt); - while (count > 0) { - int len1; /* Contig bytes avail in ib */ - - if (ib->bc_wr > ib->bc_rd) - len1 = ib->bc_wr - ib->bc_rd; - else - len1 = ib->bc_len - ib->bc_rd; - if (len1 > count) - len1 = count; - - while (len1 > 0) { - int len2; /* Contig space avail in ob */ - - if (ob->bc_rd > ob->bc_wr) - len2 = ob->bc_rd - ob->bc_wr; - else - len2 = ob->bc_len - ob->bc_wr; - if (len2 > len1) - len2 = len1; - - if (fromuser) { - len2 -= copy_from_user(ob->bc_buf + ob->bc_wr, - ib->bc_buf + ib->bc_rd, - len2); - if (len2 == 0) { - if (!rc) - rc = -EFAULT; - break; - } - } else - memcpy(ob->bc_buf + ob->bc_wr, - ib->bc_buf + ib->bc_rd, - len2); - - ib->bc_rd += len2; - if (ib->bc_rd == ib->bc_len) - ib->bc_rd = 0; - ib->bc_cnt -= len2; - - ob->bc_wr += len2; - if (ob->bc_wr == ob->bc_len) - ob->bc_wr = 0; - ob->bc_cnt += len2; - - len1 -= len2; - count -= len2; - } - } - return rc; -} - -/* - * receive an interrupt - */ -static void -tubint(int irq, void *ipp, struct pt_regs *prp) -{ - devstat_t *dsp = ipp; - tub_t *tubp; - - if ((tubp = IRQ2TUB(irq)) && (tubp->intv)) - (tubp->intv)(tubp, dsp); -} - -/* - * Initialize array of pointers to minor structures tub_t. - * Returns 0 or -ENOMEM. - */ -static int -tubinitminors(void) -{ - tubminors = (tub_t *(*)[TUBMAXMINS])kmalloc(sizeof *tubminors, - GFP_KERNEL); - if (tubminors == NULL) - return -ENOMEM; - memset(tubminors, 0, sizeof *tubminors); - return 0; -} - -/* - * Add a minor 327x device. Argument is an irq value. - * - * Point elements of two arrays to the newly created tub_t: - * 1. (*tubminors)[minor] - * 2. (*(*tubirqs)[irqhi])[irqlo] - * The first looks up from minor number at context time; the second - * looks up from irq at interrupt time. - */ -static int -#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) -tubmakemin(int irq, dev_info_t *dp) -#else -tubmakemin(int irq, s390_dev_info_t *dp) -#endif -{ - tub_t *tubp; - int minor; - long flags; - - if ((minor = ++tubnummins) == TUBMAXMINS) - return -ENODEV; - - tubp = kmalloc(sizeof(tub_t), GFP_KERNEL); - if (tubp == NULL) { - return -ENOMEM; - } - if (tubaddbyirq(tubp, irq) != 0) { - kfree(tubp); - return -ENOMEM; - } - memset(tubp, 0, sizeof(tub_t)); - tubp->minor = minor; - tubp->irq = irq; - TUBLOCK(tubp->irq, flags); - tubp->devno = dp->devno; - tubp->geom_rows = _GEOM_ROWS; - tubp->geom_cols = _GEOM_COLS; - init_waitqueue_head(&tubp->waitq); - - tubp->tty_bcb.bc_len = TTY_OUTPUT_SIZE; - tubp->tty_bcb.bc_buf = (void *)kmalloc(tubp->tty_bcb.bc_len, - GFP_KERNEL|GFP_DMA); - if (tubp->tty_bcb.bc_buf == NULL) { - TUBUNLOCK(tubp->irq, flags); - tubdelbyirq(tubp, irq); - kfree(tubp); - return -ENOMEM; - } - tubp->tty_bcb.bc_cnt = 0; - tubp->tty_bcb.bc_wr = 0; - tubp->tty_bcb.bc_rd = 0; - (*tubminors)[minor] = tubp; - -#ifdef CONFIG_TN3270_CONSOLE - if (CONSOLE_IS_3270) { - if (tub3270_con_tubp == NULL && - tub3270_con_bcb.bc_buf != NULL && - (tub3270_con_devno == -1 || - tub3270_con_devno == dp->devno)) { - extern void tty3270_int(tub_t *, devstat_t *); - - tub3270_con_devno = dp->devno; - tubp->cmd = TBC_CONOPEN; - tubp->flags |= TUB_OPEN_STET | TUB_INPUT_HACK; - tty3270_size(tubp, &flags); - tty3270_aid_init(tubp); - tty3270_scl_init(tubp); - tub3270_con_irq = tubp->irq; - tub3270_con_tubp = tubp; - tubp->intv = tty3270_int; - tubp->cmd = TBC_UPDSTAT; - tty3270_build(tubp); - } - } -#endif /* CONFIG_TN3270_CONSOLE */ - - devfs_mk_cdev(MKDEV(IBM_FS3270_MAJOR, tubp->minor), - S_IFCHR|S_IRUSR|S_IWUSR, "3270/tub%.4x"); - - TUBUNLOCK(tubp->irq, flags); - return minor; -} - -/* - * Release array of pointers to minor structures tub_t, but first - * release any storage pointed to by them. - */ -static void -tubfiniminors(void) -{ - int i; - tub_t **tubpp, *tubp; - - if (tubminors == NULL) - return; - - for (i = 0; i < TUBMAXMINS; i++) { - tubpp = &(*tubminors)[i]; - if ((tubp = *tubpp)) { - devfs_remove("3270/tub%.4x", tubp->devno); - tubdelbyirq(tubp, tubp->irq); - tty3270_rcl_fini(tubp); - kfree(tubp->tty_bcb.bc_buf); - tubp->tty_bcb.bc_buf = NULL; - if (tubp->tty_input) { - kfree(tubp->tty_input); - tubp->tty_input = NULL; - } - tubp->ttyscreen = NULL; - kfree(tubp); - *tubpp = NULL; - } - } - kfree(tubminors); - tubminors = NULL; - tubfiniirqs(); -} - -/* - * tubaddbyirq() -- Add tub_t for irq lookup in tubint() - */ -static int -tubaddbyirq(tub_t *tubp, int irq) -{ - int irqhi = (irq >> 8) & 255; - int irqlo = irq & 255; - tub_t *(*itubpp)[256]; - - /* Allocate array (*tubirqs)[] if first time */ - if (tubirqs == NULL) { - tubirqs = (tub_t *(*(*)[256])[256]) - kmalloc(sizeof *tubirqs, GFP_KERNEL); - if (tubirqs == NULL) - return -ENOMEM; - memset(tubirqs, 0, sizeof *tubirqs); - } - - /* Allocate subarray (*(*tubirqs)[])[] if first use */ - if ((itubpp = (*tubirqs)[irqhi]) == NULL) { - itubpp = (tub_t *(*)[256]) - kmalloc(sizeof(*itubpp), GFP_KERNEL); - if (itubpp == NULL) { - if (tubnummins == 1) { /* if first time */ - kfree(tubirqs); - tubirqs = NULL; - } - return -ENOMEM; - } else { - memset(itubpp, 0, sizeof(*itubpp)); - (*tubirqs)[irqhi] = itubpp; - } - } - - /* Request interrupt service */ - if ((tubp->irqrc = request_irq(irq, tubint, SA_INTERRUPT, - "3270 tube driver", &tubp->devstat)) != 0) - return tubp->irqrc; - - /* Fill in the proper subarray element */ - (*itubpp)[irqlo] = tubp; - return 0; -} - -/* - * tubfindbyirq(irq) - */ -static tub_t * -tubfindbyirq(int irq) -{ - int irqhi = (irq >> 8) & 255; - int irqlo = irq & 255; - tub_t *tubp; - - if (tubirqs == NULL) - return NULL; - if ((*tubirqs)[irqhi] == NULL) - return NULL; - tubp = (*(*tubirqs)[irqhi])[irqlo]; - if (tubp->irq == irq) - return tubp; - return NULL; -} - -/* - * tubdelbyirq(tub_t*, irq) - */ -static void -tubdelbyirq(tub_t *tubp, int irq) -{ - int irqhi = (irq >> 8) & 255; - int irqlo = irq & 255; - tub_t *(*itubpp)[256], *itubp; - - if (tubirqs == NULL) { - printk(KERN_ERR "tubirqs is NULL\n"); - return; - } - itubpp = (*tubirqs)[irqhi]; - if (itubpp == NULL) { - printk(KERN_ERR "tubirqs[%d] is NULL\n", irqhi); - return; - } - itubp = (*itubpp)[irqlo]; - if (itubp == NULL) { - printk(KERN_ERR "tubirqs[%d][%d] is NULL\n", irqhi, irqlo); - return; - } - if (itubp->irqrc == 0) - free_irq(irq, &itubp->devstat); - (*itubpp)[irqlo] = NULL; -} - -/* - * tubfiniirqs() -- clean up storage in tub_t *(*(*tubirqs)[256])[256] - */ -static void -tubfiniirqs(void) -{ - int i; - tub_t *(*itubpp)[256]; - - if (tubirqs != NULL) { - for (i = 0; i < 256; i++) { - if ((itubpp = (*tubirqs)[i])) { - kfree(itubpp); - (*tubirqs)[i] = NULL; - } - } - kfree(tubirqs); - tubirqs = NULL; - } -} - -module_init(tub3270_init); -module_exit(tub3270_exit); diff -puN -L drivers/s390/char/tubfs.c drivers/s390/char/tubfs.c~s390-08-new-3270-driver /dev/null --- 25/drivers/s390/char/tubfs.c +++ /dev/null Thu Apr 11 07:25:15 2002 @@ -1,473 +0,0 @@ -/* - * IBM/3270 Driver -- Copyright (C) UTS Global LLC - * - * tubfs.c -- Fullscreen driver - * - * - * - * - * - * Author: Richard Hitt - */ -#include "tubio.h" - -int fs3270_major = -1; /* init to impossible -1 */ - -static int fs3270_open(struct inode *, struct file *); -static int fs3270_close(struct inode *, struct file *); -static int fs3270_ioctl(struct inode *, struct file *, unsigned int, unsigned long); -static ssize_t fs3270_read(struct file *, char *, size_t, loff_t *); -static ssize_t fs3270_write(struct file *, const char *, size_t, loff_t *); -static int fs3270_wait(tub_t *, long *); -static void fs3270_int(tub_t *tubp, devstat_t *dsp); -extern void tty3270_refresh(tub_t *); - -static struct file_operations fs3270_fops = { - .owner = THIS_MODULE, /* owner */ - .read = fs3270_read, /* read */ - .write = fs3270_write, /* write */ - .ioctl = fs3270_ioctl, /* ioctl */ - .open = fs3270_open, /* open */ - .release = fs3270_close, /* release */ -}; - -/* - * fs3270_init() -- Initialize fullscreen tubes - */ -int -fs3270_init(void) -{ - int rc; - - rc = register_chrdev(IBM_FS3270_MAJOR, "fs3270", &fs3270_fops); - if (rc) { - printk(KERN_ERR "tubmod can't get major nbr %d: error %d\n", - IBM_FS3270_MAJOR, rc); - return -1; - } - devfs_mk_dir("3270"); - devfs_mk_cdev(MKDEV(IBM_FS3270_MAJOR, 0), - S_IFCHR|S_IRUGO|S_IWUGO, "3270/tub"); - fs3270_major = IBM_FS3270_MAJOR; - return 0; -} - -/* - * fs3270_fini() -- Uninitialize fullscreen tubes - */ -void -fs3270_fini(void) -{ - if (fs3270_major != -1) { - devfs_remove("3270"); - devfs_remove("3270/tub"); - unregister_chrdev(fs3270_major, "fs3270"); - fs3270_major = -1; - } -} - -/* - * fs3270_open - */ -static int -fs3270_open(struct inode *ip, struct file *fp) -{ - tub_t *tubp; - long flags; - - /* See INODE2TUB(ip) for handling of "/dev/3270/tub" */ - if ((tubp = INODE2TUB(ip)) == NULL) - return -ENOENT; - - TUBLOCK(tubp->irq, flags); - if (tubp->mode == TBM_FS || tubp->mode == TBM_FSLN) { - TUBUNLOCK(tubp->irq, flags); - return -EBUSY; - } - - fp->private_data = ip; - tubp->mode = TBM_FS; - tubp->intv = fs3270_int; - tubp->dstat = 0; - tubp->fs_pid = current->pid; - tubp->fsopen = 1; - TUBUNLOCK(tubp->irq, flags); - return 0; -} - -/* - * fs3270_close aka release: free the irq - */ -static int -fs3270_close(struct inode *ip, struct file *fp) -{ - tub_t *tubp; - long flags; - - if ((tubp = INODE2TUB(ip)) == NULL) - return -ENODEV; - - fs3270_wait(tubp, &flags); - tubp->fsopen = 0; - tubp->fs_pid = 0; - tubp->intv = NULL; - tubp->mode = 0; - tty3270_refresh(tubp); - TUBUNLOCK(tubp->irq, flags); - return 0; -} - -/* - * fs3270_release() called from tty3270_hangup() - */ -void -fs3270_release(tub_t *tubp) -{ - long flags; - - if (tubp->mode != TBM_FS) - return; - fs3270_wait(tubp, &flags); - tubp->fsopen = 0; - tubp->fs_pid = 0; - tubp->intv = NULL; - tubp->mode = 0; - /*tty3270_refresh(tubp);*/ - TUBUNLOCK(tubp->irq, flags); -} - -/* - * fs3270_wait(tub_t *tubp, int *flags) -- Wait to use tube - * Entered without irq lock - * On return: - * * Lock is held - * * Value is 0 or -ERESTARTSYS - */ -static int -fs3270_wait(tub_t *tubp, long *flags) -{ - DECLARE_WAITQUEUE(wait, current); - - TUBLOCK(tubp->irq, *flags); - add_wait_queue(&tubp->waitq, &wait); - while (!signal_pending(current) && - ((tubp->mode != TBM_FS) || - (tubp->flags & (TUB_WORKING | TUB_RDPENDING)) != 0)) { -#warning FIXME: [kj] use set_current_state instead of current->state= - current->state = TASK_INTERRUPTIBLE; - TUBUNLOCK(tubp->irq, *flags); - schedule(); -#warning FIXME: [kj] use set_current_state instead of current->state= - current->state = TASK_RUNNING; - TUBLOCK(tubp->irq, *flags); - } - remove_wait_queue(&tubp->waitq, &wait); - return signal_pending(current)? -ERESTARTSYS: 0; -} - -/* - * fs3270_io(tubp, ccw1_t*) -- start I/O on the tube - * Entered with irq lock held, WORKING off - */ -static int -fs3270_io(tub_t *tubp, ccw1_t *ccwp) -{ - int rc; - - rc = do_IO(tubp->irq, ccwp, tubp->irq, 0, 0); - tubp->flags |= TUB_WORKING; - tubp->dstat = 0; - return rc; -} - -/* - * fs3270_tasklet(tubp) -- Perform back-half processing - */ -static void -fs3270_tasklet(unsigned long data) -{ - long flags; - tub_t *tubp; - addr_t *ip; - - tubp = (tub_t *) data; - TUBLOCK(tubp->irq, flags); - tubp->flags &= ~TUB_BHPENDING; - - if (tubp->wbuf) { /* if we were writing */ - for (ip = tubp->wbuf; ip < tubp->wbuf+33; ip++) { - if (*ip == 0) - break; - kfree(phys_to_virt(*ip)); - } - kfree(tubp->wbuf); - tubp->wbuf = NULL; - } - - if ((tubp->flags & (TUB_ATTN | TUB_RDPENDING)) == - (TUB_ATTN | TUB_RDPENDING)) { - fs3270_io(tubp, &tubp->rccw); - tubp->flags &= ~(TUB_ATTN | TUB_RDPENDING); - } - - if ((tubp->flags & TUB_WORKING) == 0) - wake_up_interruptible(&tubp->waitq); - - TUBUNLOCK(tubp->irq, flags); -} - -/* - * fs3270_sched_tasklet(tubp) -- Schedule the back half - * Irq lock must be held on entry and remains held on exit. - */ -static void -fs3270_sched_tasklet(tub_t *tubp) -{ - if (tubp->flags & TUB_BHPENDING) - return; - tubp->flags |= TUB_BHPENDING; - tasklet_init(&tubp->tasklet, fs3270_tasklet, - (unsigned long) tubp); - tasklet_schedule(&tubp->tasklet); -} - -/* - * fs3270_int(tubp, prp) -- Process interrupt from tube in FS mode - * This routine is entered with irq lock held (see do_IRQ in s390io.c) - */ -static void -fs3270_int(tub_t *tubp, devstat_t *dsp) -{ -#define DEV_UE_BUSY \ - (DEV_STAT_CHN_END | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP) - -#ifdef RBHNOTYET - /* XXX needs more work; must save 2d arg to fs370_io() */ - /* Handle CE-DE-UE and subsequent UDE */ - if (dsp->dstat == DEV_UE_BUSY) { - tubp->flags |= TUB_UE_BUSY; - return; - } else if (tubp->flags & TUB_UE_BUSY) { - tubp->flags &= ~TUB_UE_BUSY; - if (dsp->dstat == DEV_STAT_DEV_END && - (tubp->flags & TUB_WORKING) != 0) { - fs3270_io(tubp); - return; - } - } -#endif - - /* Handle ATTN */ - if (dsp->dstat & DEV_STAT_ATTENTION) - tubp->flags |= TUB_ATTN; - - if (dsp->dstat & DEV_STAT_CHN_END) { - tubp->cswl = dsp->rescnt; - if ((dsp->dstat & DEV_STAT_DEV_END) == 0) - tubp->flags |= TUB_EXPECT_DE; - else - tubp->flags &= ~TUB_EXPECT_DE; - } else if (dsp->dstat & DEV_STAT_DEV_END) { - if ((tubp->flags & TUB_EXPECT_DE) == 0) - tubp->flags |= TUB_UNSOL_DE; - tubp->flags &= ~TUB_EXPECT_DE; - } - if (dsp->dstat & DEV_STAT_DEV_END) - tubp->flags &= ~TUB_WORKING; - - if ((tubp->flags & TUB_WORKING) == 0) - fs3270_sched_tasklet(tubp); -} - -/* - * process ioctl commands for the tube driver - */ -static int -fs3270_ioctl(struct inode *ip, struct file *fp, - unsigned int cmd, unsigned long arg) -{ - tub_t *tubp; - int rc = 0; - long flags; - - if ((tubp = INODE2TUB(ip)) == NULL) - return -ENODEV; - if ((rc = fs3270_wait(tubp, &flags))) { - TUBUNLOCK(tubp->irq, flags); - return rc; - } - - switch(cmd) { - case TUBICMD: tubp->icmd = arg; break; - case TUBOCMD: tubp->ocmd = arg; break; - case TUBGETI: put_user(tubp->icmd, (char *)arg); break; - case TUBGETO: put_user(tubp->ocmd, (char *)arg); break; - case TUBGETMOD: - if (copy_to_user((char *)arg, &tubp->tubiocb, - sizeof tubp->tubiocb)) - rc = -EFAULT; - break; - } - TUBUNLOCK(tubp->irq, flags); - return rc; -} - -/* - * process read commands for the tube driver - */ -static ssize_t -fs3270_read(struct file *fp, char *dp, size_t len, loff_t *off) -{ - tub_t *tubp; - char *kp; - ccw1_t *cp; - int rc; - long flags; - addr_t *idalp, *ip; - char *tp; - int count, piece; - int size; - - if (len == 0 || len > 65535) { - return -EINVAL; - } - - if ((tubp = INODE2TUB((struct inode *)fp->private_data)) == NULL) - return -ENODEV; - - ip = idalp = kmalloc(33*sizeof(addr_t), GFP_ATOMIC|GFP_DMA); - if (idalp == NULL) - return -EFAULT; - memset(idalp, 0, 33 * sizeof *idalp); - count = len; - while (count) { - piece = MIN(count, 0x800); - size = count == len? piece: 0x800; - if ((kp = kmalloc(size, GFP_KERNEL|GFP_DMA)) == NULL) { - len = -ENOMEM; - goto do_cleanup; - } - *ip++ = virt_to_phys(kp); - count -= piece; - } - - if ((rc = fs3270_wait(tubp, &flags)) != 0) { - TUBUNLOCK(tubp->irq, flags); - len = rc; - goto do_cleanup; - } - cp = &tubp->rccw; - if (tubp->icmd == 0 && tubp->ocmd != 0) tubp->icmd = 6; - cp->cmd_code = tubp->icmd?:2; - cp->flags = CCW_FLAG_SLI | CCW_FLAG_IDA; - cp->count = len; - cp->cda = virt_to_phys(idalp); - tubp->flags |= TUB_RDPENDING; - TUBUNLOCK(tubp->irq, flags); - - if ((rc = fs3270_wait(tubp, &flags)) != 0) { - tubp->flags &= ~TUB_RDPENDING; - len = rc; - TUBUNLOCK(tubp->irq, flags); - goto do_cleanup; - } - TUBUNLOCK(tubp->irq, flags); - - len -= tubp->cswl; - count = len; - tp = dp; - ip = idalp; - while (count) { - piece = MIN(count, 0x800); - if (copy_to_user(tp, phys_to_virt(*ip), piece) != 0) { - len = -EFAULT; - goto do_cleanup; - } - count -= piece; - tp += piece; - ip++; - } - -do_cleanup: - for (ip = idalp; ip < idalp+33; ip++) { - if (*ip == 0) - break; - kfree(phys_to_virt(*ip)); - } - kfree(idalp); - return len; -} - -/* - * process write commands for the tube driver - */ -static ssize_t -fs3270_write(struct file *fp, const char *dp, size_t len, loff_t *off) -{ - tub_t *tubp; - ccw1_t *cp; - int rc; - long flags; - void *kb; - addr_t *idalp, *ip; - int count, piece; - int index; - int size; - - if (len > 65535 || len == 0) - return -EINVAL; - - /* Locate the tube */ - if ((tubp = INODE2TUB((struct inode *)fp->private_data)) == NULL) - return -ENODEV; - - ip = idalp = kmalloc(33*sizeof(addr_t), GFP_ATOMIC|GFP_DMA); - if (idalp == NULL) - return -EFAULT; - memset(idalp, 0, 33 * sizeof *idalp); - - count = len; - index = 0; - while (count) { - piece = MIN(count, 0x800); - size = count == len? piece: 0x800; - if ((kb = kmalloc(size, GFP_KERNEL|GFP_DMA)) == NULL) { - len = -ENOMEM; - goto do_cleanup; - } - *ip++ = virt_to_phys(kb); - if (copy_from_user(kb, &dp[index], piece) != 0) { - len = -EFAULT; - goto do_cleanup; - } - count -= piece; - index += piece; - } - - /* Wait till tube's not working or signal is pending */ - if ((rc = fs3270_wait(tubp, &flags))) { - len = rc; - TUBUNLOCK(tubp->irq, flags); - goto do_cleanup; - } - - /* Make CCW and start I/O. Back end will free buffers & idal. */ - tubp->wbuf = idalp; - cp = &tubp->wccw; - cp->cmd_code = tubp->ocmd? tubp->ocmd == 5? 13: tubp->ocmd: 1; - cp->flags = CCW_FLAG_SLI | CCW_FLAG_IDA; - cp->count = len; - cp->cda = virt_to_phys(tubp->wbuf); - fs3270_io(tubp, cp); - TUBUNLOCK(tubp->irq, flags); - return len; - -do_cleanup: - for (ip = idalp; ip < idalp+33; ip++) { - if (*ip == 0) - break; - kfree(phys_to_virt(*ip)); - } - kfree(idalp); - return len; -} diff -puN -L drivers/s390/char/tubio.h drivers/s390/char/tubio.h~s390-08-new-3270-driver /dev/null --- 25/drivers/s390/char/tubio.h +++ /dev/null Thu Apr 11 07:25:15 2002 @@ -1,417 +0,0 @@ -/* - * IBM/3270 Driver -- Copyright (C) 2000 UTS Global LLC - * - * tubio.h -- All-Purpose header file - * - * - * - * - * - * Author: Richard Hitt - */ -#include - -#include -#include -#include - -#include -#ifndef IBM_TTY3270_MAJOR -# define IBM_TTY3270_MAJOR 212 -#endif /* IBM_TTY3270_MAJOR */ -#ifndef IBM_FS3270_MAJOR -# define IBM_FS3270_MAJOR 213 -#endif /* IBM_FS3270_MAJOR */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)) -#include -#endif - -#define TUB(x) (('3'<<8)|(x)) -#define TUBICMD TUB(3) -#define TUBOCMD TUB(4) -#define TUBGETI TUB(7) -#define TUBGETO TUB(8) -#define TUBSETMOD TUB(12) -#define TUBGETMOD TUB(13) -#define TIOPOLL TUB(32) -#define TIOPOKE TUB(33) -#define TIONPOKE TUB(34) -#define TIOTNORM TUB(35) - -/* Local Channel Commands */ -#define TC_WRITE 0x01 -#define TC_EWRITE 0x05 -#define TC_READMOD 0x06 -#define TC_EWRITEA 0x0d -#define TC_WRITESF 0x11 - -/* Buffer Control Orders */ -#define TO_SF 0x1d -#define TO_SBA 0x11 -#define TO_IC 0x13 -#define TO_PT 0x05 -#define TO_RA 0x3c -#define TO_SFE 0x29 -#define TO_EUA 0x12 -#define TO_MF 0x2c -#define TO_SA 0x28 - -/* Field Attribute Bytes */ -#define TF_INPUT 0x40 /* Visible input */ -#define TF_INPUTN 0x4c /* Invisible input */ -#define TF_INMDT 0xc1 /* Visible, Set-MDT */ -#define TF_LOG 0x60 -#define TF_STAT 0x60 - -/* Character Attribute Bytes */ -#define TAT_RESET 0x00 -#define TAT_FIELD 0xc0 -#define TAT_EXTHI 0x41 -#define TAT_COLOR 0x42 -#define TAT_CHARS 0x43 -#define TAT_TRANS 0x46 - -/* Extended-Highlighting Bytes */ -#define TAX_RESET 0x00 -#define TAX_BLINK 0xf1 -#define TAX_REVER 0xf2 -#define TAX_UNDER 0xf4 - -/* Reset value */ -#define TAR_RESET 0x00 - -/* Color values */ -#define TAC_RESET 0x00 -#define TAC_BLUE 0xf1 -#define TAC_RED 0xf2 -#define TAC_PINK 0xf3 -#define TAC_GREEN 0xf4 -#define TAC_TURQ 0xf5 -#define TAC_YELLOW 0xf6 -#define TAC_WHITE 0xf7 -#define TAC_DEFAULT 0x00 - -/* Write Control Characters */ -#define TW_NONE 0x40 /* No particular action */ -#define TW_KR 0xc2 /* Keyboard restore */ -#define TW_PLUSALARM 0x04 /* Add this bit for alarm */ - -/* Attention-ID (AID) Characters */ -#define TA_CLEAR 0x6d -#define TA_PA2 0x6e -#define TA_ENTER 0x7d -/* more to come */ - -#define MIN(a, b) ((a) < (b)? (a): (b)) - -#define TUB_BUFADR(adr, cpp) \ - tty3270_tub_bufadr(tubp, adr, cpp) - -#define TUB_EBCASC(addr, nr) codepage_convert(tub_ebcasc, addr, nr) -#define TUB_ASCEBC(addr, nr) codepage_convert(tub_ascebc, addr, nr) - -/* - * - * General global values for the tube driver - * - */ -enum tubmode { - TBM_LN, /* Line mode */ - TBM_FS, /* Fullscreen mode */ - TBM_FSLN /* Line mode shelled out of fullscreen */ -}; -enum tubstat { /* normal-mode status */ - TBS_RUNNING, /* none of the following */ - TBS_MORE, /* timed "MORE..." in status */ - TBS_HOLD /* untimed "HOLDING" in status */ -}; -enum tubcmd { /* normal-mode actions to do */ - TBC_CONOPEN, /* Erase-write the console */ - TBC_OPEN, /* Open the tty screen */ - TBC_UPDATE, /* Add lines to the log, clear cmdline */ - TBC_UPDLOG, /* Add lines to log */ - TBC_KRUPDLOG, /* Add lines to log, reset kbd */ - TBC_CLEAR, /* Build screen from scratch */ - TBC_CLRUPDLOG, /* Do log & status, not cmdline */ - TBC_UPDSTAT, /* Do status update only */ - TBC_CLRINPUT, /* Clear input area only */ - TBC_UPDINPUT /* Update input area only */ -}; -enum tubwhat { /* echo what= proc actions */ - TW_BOGUS, /* Nothing at all */ - TW_CONFIG /* Output configuration info */ -}; - - - - - -#define TUBMAXMINS 256 -#define _GEOM_ROWS 24 -#define _GEOM_COLS 80 -#define GEOM_ROWS (tubp->geom_rows) -#define GEOM_COLS (tubp->geom_cols) -#define GEOM_MAXROWS 127 -#define GEOM_MAXCOLS 132 -#define GEOM_INPLEN (GEOM_COLS * 2 - 20) -#define GEOM_MAXINPLEN (GEOM_MAXCOLS * 2 - 20) -#define GEOM_INPUT (GEOM_COLS * (GEOM_ROWS - 2) - 1) /* input atr posn */ -#define GEOM_STAT (GEOM_INPUT + 1 + GEOM_INPLEN) -#define GEOM_LOG (GEOM_COLS * GEOM_ROWS - 1) /* log atr posn */ -#define TS_RUNNING "Linux Running " -#define TS_MORE "Linux More... " -#define DEFAULT_SCROLLTIME 5 -#define TS_HOLD "Linux Holding " -/* data length used by tty3270_set_status_area: SBA (3), SF (2), data */ -#define TS_LENGTH (sizeof TS_RUNNING + 3 + 2) - -typedef struct { - int aid; /* What-to-do flags */ - char *string; /* Optional input string */ -} aid_t; -#define AIDENTRY(ch, tubp) (&((tubp)->tty_aid[(ch) & 0x3f])) - -/* For TUBGETMOD and TUBSETMOD. Should include. */ -typedef struct tubiocb { - short model; - short line_cnt; - short col_cnt; - short pf_cnt; - short re_cnt; - short map; -} tubiocb_t; - -/* Flags that go in int aid, above */ -#define TA_CLEARKEY 0x01 /* Key does hardware CLEAR */ -#define TA_SHORTREAD 0x02 /* Key does hardware shortread */ -/* If both are off, key does hardware Read Modified. */ -#define TA_DOENTER 0x04 /* Treat key like ENTER */ -#define TA_DOSTRING 0x08 /* Use string and ENTER */ -#define TA_DOSTRINGD 0x10 /* Display string & set MDT */ -#define TA_CLEARLOG 0x20 /* Make key cause clear of log */ - -/* - * Tube driver buffer control block - */ -typedef struct bcb_s { - char *bc_buf; /* Pointer to buffer */ - int bc_len; /* Length of buffer */ - int bc_cnt; /* Count of bytes buffered */ - int bc_wr; /* Posn to write next byte into */ - int bc_rd; /* Posn to read next byte from */ -} bcb_t; - -typedef struct tub_s { - int minor; - int irq; - int irqrc; - int devno; - int geom_rows; - int geom_cols; - tubiocb_t tubiocb; - int lnopen; - int fsopen; - int icmd; - int ocmd; - devstat_t devstat; - ccw1_t rccw; - ccw1_t wccw; - addr_t *wbuf; - int cswl; - void (*intv)(struct tub_s *, devstat_t *); -#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) - struct wait_queue *waitq; -#else - wait_queue_head_t waitq; -#endif - int dstat; - sense_t sense; - enum tubmode mode; - enum tubstat stat; - enum tubcmd cmd; - int flags; /* See below for values */ - struct tasklet_struct tasklet; - - /* Stuff for fs-driver support */ - pid_t fs_pid; /* Pid if TBM_FS */ - - - /* Stuff for tty-driver support */ - struct tty_struct *tty; - char *tty_input; /* tty input area */ - int tty_inattr; /* input-area field attribute */ -#define TTY_OUTPUT_SIZE 1024 - bcb_t tty_bcb; /* Output buffer control info */ - int tty_oucol; /* Kludge */ - int tty_nextlogx; /* next screen-log position */ - int tty_savecursor; /* saved cursor position */ - int tty_scrolltime; /* scrollforward wait time, sec */ - struct timer_list tty_stimer; /* timer for scrolltime */ - aid_t tty_aid[64]; /* Aid descriptors */ - int tty_aidinit; /* Boolean */ - int tty_showaidx; /* Last aid x to set_aid */ - int tty_14bitadr; /* 14-bit bufadrs okay */ -#define MAX_TTY_ESCA 24 /* Set-Attribute-Order array */ - char tty_esca[MAX_TTY_ESCA]; /* SA array */ - int tty_escx; /* Current index within it */ - - /* For command recall --- */ - char *(*tty_rclbufs)[]; /* Array of ptrs to recall bufs */ - int tty_rclk; /* Size of array tty_rclbufs */ - int tty_rclp; /* Index for most-recent cmd */ - int tty_rclb; /* Index for backscrolling */ - - /* Work area to contain the hardware write stream */ - char (*ttyscreen)[]; /* ptr to data stream area */ - int ttyscreenl; /* its length */ - ccw1_t ttyccw; -} tub_t; - -/* values for flags: */ -#define TUB_WORKING 0x0001 -#define TUB_BHPENDING 0x0002 -#define TUB_RDPENDING 0x0004 -#define TUB_ALARM 0x0008 -#define TUB_SCROLLTIMING 0x0010 -#define TUB_ATTN 0x0020 -#define TUB_IACTIVE 0x0040 -#define TUB_SIZED 0x0080 -#define TUB_EXPECT_DE 0x0100 -#define TUB_UNSOL_DE 0x0200 -#define TUB_OPEN_STET 0x0400 /* No screen clear on open */ -#define TUB_UE_BUSY 0x0800 -#define TUB_INPUT_HACK 0x1000 /* Early init of command line */ - -/* - * Extra stuff for 3270 console support - */ -#ifdef CONFIG_TN3270_CONSOLE -extern int tub3270_con_devno; -extern char (*tub3270_con_output)[]; -extern int tub3270_con_outputl; -extern int tub3270_con_ouwr; -extern int tub3270_con_oucount; -extern int tub3270_con_irq; -extern tub_t *tub3270_con_tubp; -extern struct tty_driver tty3270_con_driver; -#endif /* CONFIG_TN3270_CONSOLE */ - -extern int tubnummins; -extern tub_t *(*tubminors)[TUBMAXMINS]; -extern tub_t *(*(*tubirqs)[256])[256]; -extern unsigned char tub_ascebc[256]; -extern unsigned char tub_ebcasc[256]; -extern unsigned char tub_ebcgraf[64]; -extern int tubdebug; -extern int fs3270_major; -extern int tty3270_major; -extern int tty3270_proc_misc; -extern enum tubwhat tty3270_proc_what; -extern struct tty_driver *tty3270_driver; - -#ifndef spin_trylock_irqsave -#define spin_trylock_irqsave(lock, flags) \ -({ \ - int success; \ - local_irq_save(flags); \ - success = spin_trylock(lock); \ - if (success == 0) \ - local_irq_restore(flags); \ - success; \ -}) -#endif /* if not spin_trylock_irqsave */ - -#ifndef s390irq_spin_trylock_irqsave -#define s390irq_spin_trylock_irqsave(irq, flags) \ - spin_trylock_irqsave(&(ioinfo[irq]->irq_lock), flags) -#endif /* if not s390irq_spin_trylock_irqsave */ - -#define TUBLOCK(irq, flags) \ - s390irq_spin_lock_irqsave(irq, flags) - -#define TUBTRYLOCK(irq, flags) \ - s390irq_spin_trylock_irqsave(irq, flags) - -#define TUBUNLOCK(irq, flags) \ - s390irq_spin_unlock_irqrestore(irq, flags) - -/* - * Find tub_t * given fullscreen device's irq (subchannel number) - */ -#if 0 -extern tub_t *tubfindbyirq(int); -#endif -#define IRQ2TUB(irq) tubfindbyirq(irq) -/* - * Find tub_t * given fullscreen device's inode pointer - * This algorithm takes into account /dev/3270/tub. - */ -extern inline tub_t *INODE2TUB(struct inode *ip) -{ - unsigned int minor = iminor(ip); - tub_t *tubp = NULL; - if (minor == 0 && current->tty) { - if (current->tty->driver == tty3270_driver) - minor = current->tty->index; - } - if (minor <= tubnummins && minor > 0) - tubp = (*tubminors)[minor]; - return tubp; -} - -/* - * Find tub_t * given non-fullscreen (tty) device's tty_struct pointer - */ -extern inline tub_t *TTY2TUB(struct tty_struct *tty) -{ - unsigned index = tty->index; - tub_t *tubp = NULL; - - if (index <= tubnummins && index > 0) - tubp = (*tubminors)[index]; - return tubp; -} - -extern int tub3270_movedata(bcb_t *, bcb_t *, int); -#if 0 -#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) -extern int tubmakemin(int, dev_info_t *); -#else -extern int tubmakemin(int, s390_dev_info_t *); -#endif -#endif -extern int tub3270_con_copy(tub_t *); -extern int tty3270_rcl_init(tub_t *); -extern int tty3270_rcl_set(tub_t *, char *, int); -extern void tty3270_rcl_fini(tub_t *); -extern int tty3270_rcl_get(tub_t *, char *, int, int); -extern void tty3270_rcl_put(tub_t *, char *, int); -extern void tty3270_rcl_purge(tub_t *); -#if 0 -/* these appear to be unused outside of tubttyrcl */ -extern void tty3270_rcl_sync(tub_t *); -extern int tty3270_rcl_resize(tub_t *, int); -#endif -extern int tty3270_size(tub_t *, long *); -extern int tty3270_aid_init(tub_t *); -extern void tty3270_aid_fini(tub_t *); -extern void tty3270_aid_reinit(tub_t *); -extern int tty3270_aid_get(tub_t *, int, int *, char **); -extern int tty3270_aid_set(tub_t *, char *, int); -extern int tty3270_build(tub_t *); -extern void tty3270_scl_settimer(tub_t *); -extern void tty3270_scl_resettimer(tub_t *); -extern int tty3270_scl_set(tub_t *, char *, int); -extern int tty3270_scl_init(tub_t *tubp); -extern void tty3270_scl_fini(tub_t *tubp); diff -puN -L drivers/s390/char/tubttyaid.c drivers/s390/char/tubttyaid.c~s390-08-new-3270-driver /dev/null --- 25/drivers/s390/char/tubttyaid.c +++ /dev/null Thu Apr 11 07:25:15 2002 @@ -1,205 +0,0 @@ -/* - * IBM/3270 Driver -- Copyright (C) 2000 UTS Global LLC - * - * tubttyaid.c -- Linemode Attention-ID functionality - * - * - * - * - * - * Author: Richard Hitt - */ -#include "tubio.h" - -#define PA1_STR "^C" -#define PF3_STR "^D" -#define PF9_STR "\033j" -#define PF10_STR "\033k" -#define PF11_STR "\033j" -/* other AID-key default strings */ - -static aid_t aidtab[64] = { -/* 00 */ { 0, 0 }, -/* C1 = PF13 */ { TA_DOSTRING, 0 }, -/* C2 = PF14 */ { TA_DOSTRING, 0 }, -/* C3 = PF15 */ { TA_DOSTRING, 0 }, -/* C4 = PF16 */ { TA_DOSTRING, 0 }, -/* C5 = PF17 */ { TA_DOSTRING, 0 }, -/* C6 = PF18 */ { TA_DOSTRING, 0 }, -/* C7 = PF19 */ { TA_DOSTRING, 0 }, -/* C8 = PF20 */ { TA_DOSTRING, 0 }, -/* C9 = PF21 */ { TA_DOSTRING, 0 }, -/* 4A = PF22 */ { TA_DOSTRING, 0 }, -/* 4B = PF23 */ { TA_DOSTRING, 0 }, -/* 4C = PF24 */ { TA_DOSTRING, 0 }, -/* 0D */ { 0, 0 }, -/* 0E */ { 0, 0 }, -/* 0F */ { 0, 0 }, -/* 10 */ { 0, 0 }, -/* 11 */ { 0, 0 }, -/* 12 */ { 0, 0 }, -/* 13 */ { 0, 0 }, -/* 14 */ { 0, 0 }, -/* 15 */ { 0, 0 }, -/* 16 */ { 0, 0 }, -/* 17 */ { 0, 0 }, -/* 18 */ { 0, 0 }, -/* 19 */ { 0, 0 }, -/* 1A */ { 0, 0 }, -/* 1B */ { 0, 0 }, -/* 1C */ { 0, 0 }, -/* 1D */ { 0, 0 }, -/* 1E */ { 0, 0 }, -/* 1F */ { 0, 0 }, -/* 60 = NoAID */ { 0, 0 }, -/* 21 */ { 0, 0 }, -/* 22 */ { 0, 0 }, -/* 23 */ { 0, 0 }, -/* 24 */ { 0, 0 }, -/* 25 */ { 0, 0 }, -/* E6 = OpRdr */ { 0, 0 }, -/* E7 = MSRdr */ { 0, 0 }, -/* E8 = NoAID */ { 0, 0 }, -/* 29 */ { 0, 0 }, -/* 2A */ { 0, 0 }, -/* 6B = PA3 */ { TA_SHORTREAD, 0 }, -/* 6C = PA1 */ { TA_SHORTREAD | TA_DOSTRING, PA1_STR }, -/* 6D = CLEAR */ { TA_SHORTREAD | TA_CLEARKEY, 0 }, -/* 6E = PA2 */ { TA_SHORTREAD | TA_CLEARLOG, 0 }, -/* 2F */ { 0, 0 }, -/* F0 = TstRq */ { 0, 0 }, -/* F1 = PF1 */ { TA_DOSTRING, 0 }, -/* F2 = PF2 */ { TA_DOSTRING, 0 }, -/* F3 = PF3 */ { TA_DOSTRING, PF3_STR }, -/* F4 = PF4 */ { TA_DOSTRING, 0 }, -/* F5 = PF5 */ { TA_DOSTRING, 0 }, -/* F6 = PF6 */ { TA_DOSTRING, 0 }, -/* F7 = PF7 */ { TA_DOSTRING, 0 }, -/* F8 = PF8 */ { TA_DOSTRING, 0 }, -/* F9 = PF9 */ { TA_DOSTRING, PF9_STR }, -/* 7A = PF10 */ { TA_DOSTRING, PF10_STR }, -/* 7B = PF11 */ { TA_DOSTRING, PF11_STR }, -/* 7C = PF12 */ { TA_DOSTRING, 0 }, -/* 7D = ENTER */ { TA_DOENTER, 0 }, -/* 7E = Pen */ { 0, 0 }, -/* 3F */ { 0, 0 }, -}; - -int -tty3270_aid_init(tub_t *tubp) -{ - memcpy(tubp->tty_aid, aidtab, sizeof aidtab); - tubp->tty_aidinit = 1; - return 0; -} - -void -tty3270_aid_fini(tub_t *tubp) -{ - int i; - char *sp; - - if (tubp->tty_aidinit == 0) - return; - for (i = 0; i < 64; i++) { - if ((sp = tubp->tty_aid[i].string) == NULL) - continue; - if (sp == aidtab[i].string) - continue; - kfree(sp); - } - tubp->tty_aidinit = 0; -} - -void -tty3270_aid_reinit(tub_t *tubp) -{ - tty3270_aid_fini(tubp); - tty3270_aid_init(tubp); -} - -int -tty3270_aid_get(tub_t *tubp, int aid, int *aidflags, char **aidstring) -{ - aid_t *ap; - - ap = AIDENTRY(aid, tubp); - *aidflags = ap->aid; - *aidstring = ap->string; - return 0; -} - -/* - * tty3270_aid_set() -- write_proc extension - * Parse written string as an AID name. Return 0 if it's not. - * Otherwise absorb the string and return count or -error. - */ -int -tty3270_aid_set(tub_t *tubp, char *buf, int count) -{ - char name[8]; - char *sp; - int aidn, aidx; - aid_t *ap; - int len; - char *pfp; - - if (tubp->tty_aidinit == 0) - return 0; - if (count < 3) /* If AID-key name too short */ - return 0; - name[0] = buf[0] < 0x60? buf[0]: (buf[0] & 0x5f); - name[1] = buf[1] < 0x60? buf[1]: (buf[1] & 0x5f); - if (name[0] == 'P' && name[1] == 'F') { - aidn = simple_strtoul(buf+2, &sp, 10); - if (aidn < 1 || aidn > 24) - return 0; - aidx = aidn > 12? aidn - 12: aidn + 0x30; - ap = &tubp->tty_aid[aidx]; - } else if (name[0] == 'P' && name[1] == 'A') { - aidn = simple_strtoul(buf+2, &sp, 10); - if (aidn < 1 || aidn > 3) - return 0; - switch(aidn) { - case 1: aidx = 0x2c; break; - case 2: aidx = 0x2e; break; - case 3: aidx = 0x2b; break; - default: aidx = 0; break; - } - ap = &tubp->tty_aid[aidx]; - } else { - return 0; - } - - if (*sp == '\0') { - tubp->tty_showaidx = ap - tubp->tty_aid; - return count; - } else if (*sp == '=') { - len = strlen(++sp); - if (len == 0) { - if (ap->string != NULL && - ap->string != aidtab[aidx].string) - kfree(ap->string); - ap->string = aidtab[aidx].string; - ap->aid = aidtab[aidx].aid; - return count; - } - if ((pfp = kmalloc(len + 1, GFP_KERNEL)) == NULL) - return -ENOMEM; - if (ap->string != NULL && - ap->string != aidtab[aidx].string) - kfree(ap->string); - if (sp[len - 1] == '\n') { - ap->aid = TA_DOSTRING; - sp[len - 1] = '\0'; - len--; - } else { - ap->aid = TA_DOSTRINGD; - } - memcpy(pfp, sp, len + 1); - ap->string = pfp; - return count; - } else { - return -EINVAL; - } -} diff -puN -L drivers/s390/char/tubttybld.c drivers/s390/char/tubttybld.c~s390-08-new-3270-driver /dev/null --- 25/drivers/s390/char/tubttybld.c +++ /dev/null Thu Apr 11 07:25:15 2002 @@ -1,587 +0,0 @@ -/* - * IBM/3270 Driver -- Copyright (C) 2000 UTS Global LLC - * - * tubttybld.c -- Linemode tty driver screen-building functions - * - * - * - * - * - * Author: Richard Hitt - */ - -#include "tubio.h" - -extern int tty3270_io(tub_t *); -static void tty3270_set_status_area(tub_t *, char **); -static int tty3270_next_char(tub_t *); -static void tty3270_unnext_char(tub_t *, char); -static void tty3270_update_log_area(tub_t *, char **); -static int tty3270_update_log_area_esc(tub_t *, char **, int *); -static void tty3270_clear_log_area(tub_t *, char **); -static void tty3270_tub_bufadr(tub_t *, int, char **); -static void tty3270_set_bufadr(tub_t *, char **, int *); - -/* - * tty3270_clear_log_area(tub_t *tubp, char **cpp) - */ -static void -tty3270_clear_log_area(tub_t *tubp, char **cpp) -{ - *(*cpp)++ = TO_SBA; - TUB_BUFADR(GEOM_LOG, cpp); - *(*cpp)++ = TO_SF; - *(*cpp)++ = TF_LOG; - *(*cpp)++ = TO_RA; - TUB_BUFADR(GEOM_INPUT, cpp); - *(*cpp)++ = '\0'; - tubp->tty_oucol = tubp->tty_nextlogx = 0; - *(*cpp)++ = TO_SBA; - TUB_BUFADR(tubp->tty_nextlogx, cpp); -} - -static void -tty3270_update_log_area(tub_t *tubp, char **cpp) -{ - int lastx = GEOM_INPUT; - int c; - int next, fill, i; - int sba_needed = 1; - char *overrun = &(*tubp->ttyscreen)[tubp->ttyscreenl - TS_LENGTH]; - - /* Check for possible ESC sequence work to do */ - if (tubp->tty_escx != 0) { - /* If compiling new escape sequence */ - if (tubp->tty_esca[0] == 0x1b) { - if (tty3270_update_log_area_esc(tubp, cpp, &sba_needed)) - return; - /* If esc seq needs refreshing after a write */ - } else if (tubp->tty_esca[0] == TO_SA) { - tty3270_set_bufadr(tubp, cpp, &sba_needed); - for (i = 0; i < tubp->tty_escx; i++) - *(*cpp)++ = tubp->tty_esca[i]; - } else { - printk(KERN_WARNING "tty3270_update_log_area esca " - "character surprising: %.2x\n", tubp->tty_esca[0]); - } - } - - /* Place characters */ - while (tubp->tty_bcb.bc_cnt != 0) { - /* Check for room. TAB could take up to 4 chars. */ - if (&(*cpp)[4] >= overrun) - break; - - /* Fetch a character */ - if ((c = tty3270_next_char(tubp)) == -1) - break; - - switch(c) { - default: - if (tubp->tty_nextlogx >= lastx) { - if (sba_needed == 0 || - tubp->stat == TBS_RUNNING) { - tty3270_unnext_char(tubp, c); - tubp->stat = TBS_MORE; - tty3270_set_status_area(tubp, cpp); - tty3270_scl_settimer(tubp); - } - goto do_return; - } - tty3270_set_bufadr(tubp, cpp, &sba_needed); - /* Use blank if we don't know the character */ - *(*cpp)++ = tub_ascebc[(int)(c < ' '? ' ': c)]; - tubp->tty_nextlogx++; - tubp->tty_oucol++; - break; - case 0x1b: /* ESC */ - tubp->tty_escx = 0; - if (tty3270_update_log_area_esc(tubp, cpp, &sba_needed)) - return; - break; - case '\r': /* 0x0d -- Carriage Return */ - tubp->tty_nextlogx -= - tubp->tty_nextlogx % GEOM_COLS; - sba_needed = 1; - break; - case '\n': /* 0x0a -- New Line */ - if (tubp->tty_oucol == GEOM_COLS) { - tubp->tty_oucol = 0; - break; - } - next = (tubp->tty_nextlogx + GEOM_COLS) / - GEOM_COLS * GEOM_COLS; - tubp->tty_nextlogx = next; - tubp->tty_oucol = 0; - sba_needed = 1; - break; - case '\t': /* 0x09 -- Tabulate */ - fill = (tubp->tty_nextlogx % GEOM_COLS) % 8; - for (; fill < 8; fill++) { - if (tubp->tty_nextlogx >= lastx) - break; - *(*cpp)++ = tub_ascebc[' ']; - tubp->tty_nextlogx++; - tubp->tty_oucol++; - } - break; - case '\a': /* 0x07 -- Alarm */ - tubp->flags |= TUB_ALARM; - break; - case '\f': /* 0x0c -- Form Feed */ - tty3270_clear_log_area(tubp, cpp); - break; - case 0xf: /* SuSE "exit alternate mode" */ - break; - } - } -do_return: -} - -#define NUMQUANT 8 -static int -tty3270_update_log_area_esc(tub_t *tubp, char **cpp, int *sba_needed) -{ - int c; - int i, j; - int start, end, next; - int quant[NUMQUANT]; - char *overrun = &(*tubp->ttyscreen)[tubp->ttyscreenl - TS_LENGTH]; - char sabuf[NUMQUANT*3], *sap = sabuf, *cp; - - /* If starting a sequence, stuff ESC at [0] */ - if (tubp->tty_escx == 0) - tubp->tty_esca[tubp->tty_escx++] = 0x1b; - - /* Now that sequence is started, see if room in buffer */ - if (&(*cpp)[NUMQUANT * 3] >= overrun) - return tubp->tty_escx; - - /* Gather the rest of the sequence's characters */ - while (tubp->tty_escx < sizeof tubp->tty_esca) { - if ((c = tty3270_next_char(tubp)) == -1) - return tubp->tty_escx; - if (tubp->tty_escx == 1) { - switch(c) { - case '[': - tubp->tty_esca[tubp->tty_escx++] = c; - continue; - case '7': - tubp->tty_savecursor = tubp->tty_nextlogx; - goto done_return; - case '8': - next = tubp->tty_savecursor; - goto do_setcur; - default: - goto error_return; - } - } - tubp->tty_esca[tubp->tty_escx++] = c; - if (c != ';' && (c < '0' || c > '9')) - break; - } - - /* Check for overrun */ - if (tubp->tty_escx == sizeof tubp->tty_esca) - goto error_return; - - /* Parse potentially empty string "nn;nn;nn..." */ - i = -1; - j = 2; /* skip ESC, [ */ - c = ';'; - do { - if (c == ';') { - if (++i == NUMQUANT) - goto error_return; - quant[i] = 0; - } else if (c < '0' || c > '9') { - break; - } else { - quant[i] = quant[i] * 10 + c - '0'; - } - c = tubp->tty_esca[j]; - } while (j++ < tubp->tty_escx); - - /* Add 3270 data stream output to execute the sequence */ - switch(c) { - case 'm': /* Set Attribute */ - for (next = 0; next <= i; next++) { - int type = -1, value = 0; - - switch(quant[next]) { - case 0: /* Reset */ - next = tubp->tty_nextlogx; - tty3270_set_bufadr(tubp, cpp, sba_needed); - *(*cpp)++ = TO_SA; - *(*cpp)++ = TAT_EXTHI; - *(*cpp)++ = TAX_RESET; - *(*cpp)++ = TO_SA; - *(*cpp)++ = TAT_COLOR; - *(*cpp)++ = TAC_RESET; - tubp->tty_nextlogx = next; - *sba_needed = 1; - sap = sabuf; - break; - case 1: /* Bright */ - break; - case 2: /* Dim */ - break; - case 4: /* Underscore */ - type = TAT_EXTHI; value = TAX_UNDER; - break; - case 5: /* Blink */ - type = TAT_EXTHI; value = TAX_BLINK; - break; - case 7: /* Reverse */ - type = TAT_EXTHI; value = TAX_REVER; - break; - case 8: /* Hidden */ - break; /* For now ... */ - /* Foreground Colors */ - case 30: /* Black */ - type = TAT_COLOR; value = TAC_DEFAULT; - break; - case 31: /* Red */ - type = TAT_COLOR; value = TAC_RED; - break; - case 32: /* Green */ - type = TAT_COLOR; value = TAC_GREEN; - break; - case 33: /* Yellow */ - type = TAT_COLOR; value = TAC_YELLOW; - break; - case 34: /* Blue */ - type = TAT_COLOR; value = TAC_BLUE; - break; - case 35: /* Magenta */ - type = TAT_COLOR; value = TAC_PINK; - break; - case 36: /* Cyan */ - type = TAT_COLOR; value = TAC_TURQ; - break; - case 37: /* White */ - type = TAT_COLOR; value = TAC_WHITE; - break; - case 39: /* Black */ - type = TAT_COLOR; value = TAC_DEFAULT; - break; - /* Background Colors */ - case 40: /* Black */ - case 41: /* Red */ - case 42: /* Green */ - case 43: /* Yellow */ - case 44: /* Blue */ - case 45: /* Magenta */ - case 46: /* Cyan */ - case 47: /* White */ - break; /* For now ... */ - /* Oops */ - default: - break; - } - if (type != -1) { - tty3270_set_bufadr(tubp, cpp, sba_needed); - *(*cpp)++ = TO_SA; - *(*cpp)++ = type; - *(*cpp)++ = value; - *sap++ = TO_SA; - *sap++ = type; - *sap++ = value; - } - } - break; - - case 'H': /* Cursor Home */ - case 'f': /* Force Cursor Position */ - if (quant[0]) quant[0]--; - if (quant[1]) quant[1]--; - next = quant[0] * GEOM_COLS + quant[1]; - goto do_setcur; - case 'A': /* Cursor Up */ - if (quant[i] == 0) quant[i] = 1; - next = tubp->tty_nextlogx - GEOM_COLS * quant[i]; - goto do_setcur; - case 'B': /* Cursor Down */ - if (quant[i] == 0) quant[i] = 1; - next = tubp->tty_nextlogx + GEOM_COLS * quant[i]; - goto do_setcur; - case 'C': /* Cursor Forward */ - if (quant[i] == 0) quant[i] = 1; - next = tubp->tty_nextlogx % GEOM_COLS; - start = tubp->tty_nextlogx - next; - next = start + MIN(next + quant[i], GEOM_COLS - 1); - goto do_setcur; - case 'D': /* Cursor Backward */ - if (quant[i] == 0) quant[i] = 1; - next = MIN(quant[i], tubp->tty_nextlogx % GEOM_COLS); - next = tubp->tty_nextlogx - next; - goto do_setcur; - case 'G': - if (quant[0]) quant[0]--; - next = tubp->tty_nextlogx / GEOM_COLS * GEOM_COLS + quant[0]; -do_setcur: - if (next < 0) - break; - tubp->tty_nextlogx = next; - tubp->tty_oucol = tubp->tty_nextlogx % GEOM_COLS; - *sba_needed = 1; - break; - - case 'r': /* Define scroll area */ - start = quant[0]; - if (start <= 0) start = 1; - if (start > GEOM_ROWS - 2) start = GEOM_ROWS - 2; - tubp->tty_nextlogx = (start - 1) * GEOM_COLS; - tubp->tty_oucol = 0; - *sba_needed = 1; - break; - - case 'X': /* Erase for n chars from cursor */ - start = tubp->tty_nextlogx; - end = start + (quant[0]?: 1); - goto do_fill; - case 'J': /* Erase to screen end from cursor */ - *(*cpp)++ = TO_SBA; - TUB_BUFADR(tubp->tty_nextlogx, cpp); - *(*cpp)++ = TO_RA; - TUB_BUFADR(GEOM_INPUT, cpp); - *(*cpp)++ = tub_ascebc[' ']; - *(*cpp)++ = TO_SBA; - TUB_BUFADR(tubp->tty_nextlogx, cpp); - break; - case 'K': - start = tubp->tty_nextlogx; - end = (start + GEOM_COLS) / GEOM_COLS * GEOM_COLS; -do_fill: - if (start >= GEOM_INPUT) - break; - if (end > GEOM_INPUT) - end = GEOM_INPUT; - if (end <= start) - break; - *(*cpp)++ = TO_SBA; - TUB_BUFADR(start, cpp); - if (end - start > 4) { - *(*cpp)++ = TO_RA; - TUB_BUFADR(end, cpp); - *(*cpp)++ = tub_ascebc[' ']; - } else while (start++ < end) { - *(*cpp)++ = tub_ascebc[' ']; - } - tubp->tty_nextlogx = end; - tubp->tty_oucol = tubp->tty_nextlogx % GEOM_COLS; - *sba_needed = 1; - break; - } -done_return: - tubp->tty_escx = 0; - cp = sabuf; - while (cp != sap) - tubp->tty_esca[tubp->tty_escx++] = *cp++; - return 0; -error_return: - tubp->tty_escx = 0; - return 0; -} - -static int -tty3270_next_char(tub_t *tubp) -{ - int c; - bcb_t *ib; - - ib = &tubp->tty_bcb; - if (ib->bc_cnt == 0) - return -1; - c = ib->bc_buf[ib->bc_rd++]; - if (ib->bc_rd == ib->bc_len) - ib->bc_rd = 0; - ib->bc_cnt--; - return c; -} - -static void -tty3270_unnext_char(tub_t *tubp, char c) -{ - bcb_t *ib; - - ib = &tubp->tty_bcb; - if (ib->bc_rd == 0) - ib->bc_rd = ib->bc_len; - ib->bc_buf[--ib->bc_rd] = c; - ib->bc_cnt++; -} - - -static void -tty3270_clear_input_area(tub_t *tubp, char **cpp) -{ - *(*cpp)++ = TO_SBA; - TUB_BUFADR(GEOM_INPUT, cpp); - *(*cpp)++ = TO_SF; - *(*cpp)++ = tubp->tty_inattr; - *(*cpp)++ = TO_IC; - *(*cpp)++ = TO_RA; - TUB_BUFADR(GEOM_STAT, cpp); - *(*cpp)++ = '\0'; -} - -static void -tty3270_update_input_area(tub_t *tubp, char **cpp) -{ - int len; - - *(*cpp)++ = TO_SBA; - TUB_BUFADR(GEOM_INPUT, cpp); - *(*cpp)++ = TO_SF; - *(*cpp)++ = TF_INMDT; - len = strlen(tubp->tty_input); - memcpy(*cpp, tubp->tty_input, len); - *cpp += len; - *(*cpp)++ = TO_IC; - len = GEOM_INPLEN - len; - if (len > 4) { - *(*cpp)++ = TO_RA; - TUB_BUFADR(GEOM_STAT, cpp); - *(*cpp)++ = '\0'; - } else { - for (; len > 0; len--) - *(*cpp)++ = '\0'; - } -} - -/* - * tty3270_set_status_area(tub_t *tubp, char **cpp) - */ -static void -tty3270_set_status_area(tub_t *tubp, char **cpp) -{ - char *sp; - - if (tubp->stat == TBS_RUNNING) - sp = TS_RUNNING; - else if (tubp->stat == TBS_MORE) - sp = TS_MORE; - else if (tubp->stat == TBS_HOLD) - sp = TS_HOLD; - else - sp = "Linux Whatstat"; - - *(*cpp)++ = TO_SBA; - TUB_BUFADR(GEOM_STAT, cpp); - *(*cpp)++ = TO_SF; - *(*cpp)++ = TF_STAT; - memcpy(*cpp, sp, sizeof TS_RUNNING); - TUB_ASCEBC(*cpp, sizeof TS_RUNNING); - *cpp += sizeof TS_RUNNING; -} - -/* - * tty3270_build() -- build an output stream - */ -int -tty3270_build(tub_t *tubp) -{ - char *cp, *startcp; - int chancmd; - int writecc = TW_KR; - int force = 0; - - if (tubp->mode == TBM_FS) - return 0; - - cp = startcp = *tubp->ttyscreen + 1; - - switch(tubp->cmd) { - default: - printk(KERN_WARNING "tty3270_build unknown command %d\n", tubp->cmd); - return 0; - case TBC_OPEN: -tbc_open: - tubp->flags &= ~TUB_INPUT_HACK; - chancmd = TC_EWRITEA; - tty3270_clear_input_area(tubp, &cp); - tty3270_set_status_area(tubp, &cp); - tty3270_clear_log_area(tubp, &cp); - break; - case TBC_UPDLOG: - if (tubp->flags & TUB_INPUT_HACK) - goto tbc_open; - chancmd = TC_WRITE; - writecc = TW_NONE; - tty3270_update_log_area(tubp, &cp); - break; - case TBC_KRUPDLOG: - chancmd = TC_WRITE; - force = 1; - tty3270_update_log_area(tubp, &cp); - break; - case TBC_CLRUPDLOG: - chancmd = TC_WRITE; - tty3270_set_status_area(tubp, &cp); - tty3270_clear_log_area(tubp, &cp); - tty3270_update_log_area(tubp, &cp); - break; - case TBC_UPDATE: - chancmd = TC_EWRITEA; - tubp->tty_oucol = tubp->tty_nextlogx = 0; - tty3270_clear_input_area(tubp, &cp); - tty3270_set_status_area(tubp, &cp); - tty3270_update_log_area(tubp, &cp); - break; - case TBC_UPDSTAT: - chancmd = TC_WRITE; - tty3270_set_status_area(tubp, &cp); - break; - case TBC_CLRINPUT: - chancmd = TC_WRITE; - tty3270_clear_input_area(tubp, &cp); - break; - case TBC_UPDINPUT: - chancmd = TC_WRITE; - tty3270_update_input_area(tubp, &cp); - break; - } - - /* Set Write Control Character and start I/O */ - if (force == 0 && cp == startcp && - (tubp->flags & TUB_ALARM) == 0) - return 0; - if (tubp->flags & TUB_ALARM) { - tubp->flags &= ~TUB_ALARM; - writecc |= TW_PLUSALARM; - } - **tubp->ttyscreen = writecc; - tubp->ttyccw.cmd_code = chancmd; - tubp->ttyccw.flags = CCW_FLAG_SLI; - tubp->ttyccw.cda = virt_to_phys(*tubp->ttyscreen); - tubp->ttyccw.count = cp - *tubp->ttyscreen; - tty3270_io(tubp); - return 1; -} - -static void -tty3270_tub_bufadr(tub_t *tubp, int adr, char **cpp) -{ - if (tubp->tty_14bitadr) { - *(*cpp)++ = (adr >> 8) & 0x3f; - *(*cpp)++ = adr & 0xff; - } else { - *(*cpp)++ = tub_ebcgraf[(adr >> 6) & 0x3f]; - *(*cpp)++ = tub_ebcgraf[adr & 0x3f]; - } -} - -static void -tty3270_set_bufadr(tub_t *tubp, char **cpp, int *sba_needed) -{ - if (!*sba_needed) - return; - if (tubp->tty_nextlogx >= GEOM_INPUT) { - tubp->tty_nextlogx = GEOM_INPUT - 1; - tubp->tty_oucol = tubp->tty_nextlogx % GEOM_COLS; - } - *(*cpp)++ = TO_SBA; - TUB_BUFADR(tubp->tty_nextlogx, cpp); - *sba_needed = 0; -} diff -puN -L drivers/s390/char/tubtty.c drivers/s390/char/tubtty.c~s390-08-new-3270-driver /dev/null --- 25/drivers/s390/char/tubtty.c +++ /dev/null Thu Apr 11 07:25:15 2002 @@ -1,955 +0,0 @@ -/* - * IBM/3270 Driver -- Copyright (C) 2000, 2001 UTS Global LLC - * - * tubtty.c -- Linemode tty driver - * - * - * - * - * - * Author: Richard Hitt - */ -#include -#include "tubio.h" - -/* Initialization & uninitialization for tubtty */ -int tty3270_init(void); -void tty3270_fini(void); - -/* Interface routines from the upper tty layer to the tty driver */ -static int tty3270_open(struct tty_struct *, struct file *); -static void tty3270_close(struct tty_struct *, struct file *); -static int tty3270_write(struct tty_struct *, int, - const unsigned char *, int); -static void tty3270_put_char(struct tty_struct *, unsigned char); -static void tty3270_flush_chars(struct tty_struct *); -static int tty3270_write_room(struct tty_struct *); -static int tty3270_chars_in_buffer(struct tty_struct *); -static int tty3270_ioctl(struct tty_struct *, struct file *, - unsigned int cmd, unsigned long arg); -static void tty3270_set_termios(struct tty_struct *, struct termios *); -static void tty3270_hangup(struct tty_struct *); -static void tty3270_flush_buffer(struct tty_struct *); -static int tty3270_read_proc(char *, char **, off_t, int, int *, void *); -static int tty3270_write_proc(struct file *, const char *, - unsigned long, void *); - -/* tty3270 utility functions */ -static void tty3270_tasklet(unsigned long); - void tty3270_sched_bh(tub_t *); -static int tty3270_wait(tub_t *, long *); - void tty3270_int(tub_t *, devstat_t *); -static int tty3270_try_logging(tub_t *); -static void tty3270_start_input(tub_t *); -static void tty3270_do_input(tub_t *); -static void tty3270_do_enter(tub_t *, char *, int); -static void tty3270_do_showi(tub_t *, char *, int); - int tty3270_io(tub_t *); -static int tty3270_show_tube(int, char *, int); - -static int tty3270_major = -1; -struct tty_driver *tty3270_driver; - -static int tty3270_proc_index; -static int tty3270_proc_data; -static int tty3270_proc_misc; -static enum tubwhat tty3270_proc_what; - -static struct tty_operations tty3270_ops = { - .open = tty3270_open, - .close = tty3270_close, - .write = tty3270_write, - .put_char = tty3270_put_char, - .flush_chars = tty3270_flush_chars, - .write_room = tty3270_write_room, - .chars_in_buffer = tty3270_chars_in_buffer, -#if 0 - .ioctl = tty3270_ioctl, -#endif - .set_termios = tty3270_set_termios, - .hangup = tty3270_hangup, - .flush_buffer = tty3270_flush_buffer, - .read_proc = tty3270_read_proc, - .write_proc = tty3270_write_proc, -}; - -/* - * tty3270_init() -- Register the tty3270 driver - */ -int -tty3270_init(void) -{ - struct tty_driver *td = alloc_tty_driver(TUBMAXMINS); - int rc; - - if (!td) - return -ENOMEM; - - /* Initialize for tty driver */ - td->owner = THIS_MODULE; - td->driver_name = "tty3270"; - td->name = "tty3270"; - td->major = IBM_TTY3270_MAJOR; - td->minor_start = 0; - td->type = TTY_DRIVER_TYPE_SYSTEM; - td->subtype = SYSTEM_TYPE_TTY; - td->init_termios = tty_std_termios; - td->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_NO_DEVFS; - tty_set_operations(td, &tty3270_ops); - rc = tty_register_driver(td); - if (rc) { - put_tty_driver(td); - printk(KERN_ERR "tty3270 registration failed with %d\n", rc); - } else { - tty3270_major = IBM_TTY3270_MAJOR; - if (td->proc_entry != NULL) - td->proc_entry->mode = S_IRUGO | S_IWUGO; - tty3270_driver = td; - } - return rc; -} - -/* - * tty3270_fini() -- Uninitialize linemode tubes - */ -void -tty3270_fini(void) -{ - if (tty3270_major != -1) { - tty_unregister_driver(tty3270_driver); - put_tty_driver(tty3270_driver); - tty3270_driver = NULL; - tty3270_major = -1; - } -} - -static int -tty3270_open(struct tty_struct *tty, struct file *filp) -{ - tub_t *tubp; - long flags; - int rc; - int cmd; - - if ((tubp = TTY2TUB(tty)) == NULL) { - return -ENODEV; - } - - if ((rc = tty3270_wait(tubp, &flags)) != 0) - goto do_fail; - if (tubp->lnopen > 0) { - tubp->lnopen++; - TUBUNLOCK(tubp->irq, flags); - return 0; - } - if (tubp->flags & TUB_OPEN_STET) { - cmd = TBC_UPDLOG; - } else { - cmd = TBC_OPEN; - tubp->flags &= ~TUB_SIZED; - } - if ((rc = tty3270_size(tubp, &flags)) != 0) - goto do_fail; - if ((rc = tty3270_rcl_init(tubp)) != 0) - goto do_fail; - if ((rc = tty3270_aid_init(tubp)) != 0) - goto do_fail; - if ((rc = tty3270_scl_init(tubp)) != 0) - goto do_fail; - tubp->mode = TBM_LN; - tubp->intv = tty3270_int; - tubp->tty = tty; - tubp->lnopen = 1; - tty->driver_data = tubp; - tty->winsize.ws_row = tubp->geom_rows - 2; - tty->winsize.ws_col = tubp->geom_cols; - if (tubp->tty_input == NULL) - tubp->tty_input = kmalloc(GEOM_INPLEN, GFP_KERNEL|GFP_DMA); - tubp->tty_inattr = TF_INPUT; - tubp->cmd = cmd; - tty3270_build(tubp); - TUBUNLOCK(tubp->irq, flags); - return 0; - -do_fail: - tty3270_scl_fini(tubp); - tty3270_aid_fini(tubp); - tty3270_rcl_fini(tubp); - TUBUNLOCK(tubp->irq, flags); - return rc; -} - -static void -tty3270_close(struct tty_struct *tty, struct file *filp) -{ - tub_t *tubp; - long flags; - - if ((tubp = tty->driver_data) == NULL) - return; - - tty3270_wait(tubp, &flags); - if (--tubp->lnopen > 0) - goto do_return; - tubp->tty = NULL; - tty->driver_data = NULL; - tty3270_aid_fini(tubp); - tty3270_rcl_fini(tubp); - tty3270_scl_fini(tubp); -do_return: - TUBUNLOCK(tubp->irq, flags); -} - -static int -tty3270_write(struct tty_struct *tty, int fromuser, - const unsigned char *buf, int count) -{ - tub_t *tubp; - long flags; - bcb_t obcb; - int rc = 0; - - if ((tubp = tty->driver_data) == NULL) - return -1; - -#ifdef CONFIG_TN3270_CONSOLE - if (CONSOLE_IS_3270 && tub3270_con_tubp == tubp) - tub3270_con_copy(tubp); -#endif /* CONFIG_TN3270_CONSOLE */ - - obcb.bc_buf = (char *)buf; - obcb.bc_len = obcb.bc_cnt = obcb.bc_wr = count; - obcb.bc_rd = 0; - - TUBLOCK(tubp->irq, flags); - rc = tub3270_movedata(&obcb, &tubp->tty_bcb, fromuser); - tty3270_try_logging(tubp); - TUBUNLOCK(tubp->irq, flags); - return rc; -} - -static void -tty3270_put_char(struct tty_struct *tty, unsigned char ch) -{ - long flags; - tub_t *tubp; - bcb_t *ob; - - if ((tubp = tty->driver_data) == NULL) - return; - - TUBLOCK(tubp->irq, flags); - ob = &tubp->tty_bcb; - if (ob->bc_cnt < ob->bc_len) { - ob->bc_buf[ob->bc_wr++] = ch; - if (ob->bc_wr == ob->bc_len) - ob->bc_wr = 0; - ob->bc_cnt++; - } - tty3270_try_logging(tubp); - TUBUNLOCK(tubp->irq, flags); -} - -static void -tty3270_flush_chars(struct tty_struct *tty) -{ - tub_t *tubp; - long flags; - - if ((tubp = tty->driver_data) == NULL) - return; - - TUBLOCK(tubp->irq, flags); - tty3270_try_logging(tubp); - TUBUNLOCK(tubp->irq, flags); -} - -static int -tty3270_write_room(struct tty_struct *tty) -{ - tub_t *tubp; - bcb_t *ob; - - if ((tubp = tty->driver_data) == NULL) - return -1; - - ob = &tubp->tty_bcb; - return ob->bc_len - ob->bc_cnt; -} - -static int -tty3270_chars_in_buffer(struct tty_struct *tty) -{ - tub_t *tubp; - bcb_t *ob; - - if ((tubp = tty->driver_data) == NULL) - return -1; - - ob = &tubp->tty_bcb; - return ob->bc_cnt; -} - -static int -tty3270_ioctl(struct tty_struct *tty, struct file *file, - unsigned int cmd, unsigned long arg) -{ - tub_t *tubp; - long flags; - int ret = 0; - struct termios termios; - - if ((tubp = tty->driver_data) == NULL) - return -ENODEV; - - TUBLOCK(tubp->irq, flags); - if (tty->flags * (1 << TTY_IO_ERROR)) { - ret = -EIO; - goto do_return; - } - switch(cmd) { - case TCGETS: - ret = -ENOIOCTLCMD; - goto do_return; - case TCFLSH: /* arg: 2 or 0 */ - ret = -ENOIOCTLCMD; - goto do_return; - case TCSETSF: - if (user_termios_to_kernel_termios(&termios, - (struct termios *)arg)) { - ret = -EFAULT; - goto do_return; - } - ret = -ENOIOCTLCMD; - goto do_return; - case TCGETA: - ret = -ENOIOCTLCMD; - goto do_return; - case TCSETA: - if (user_termio_to_kernel_termios(&termios, - (struct termio *)arg)) { - ret = -EFAULT; - goto do_return; - } - ret = -ENOIOCTLCMD; - goto do_return; - default: - ret = -ENOIOCTLCMD; - break; - } - -do_return: - TUBUNLOCK(tubp->irq, flags); - return ret; -} - -static void -tty3270_set_termios(struct tty_struct *tty, struct termios *old) -{ - tub_t *tubp; - long flags; - int new; - - if ((tubp = tty->driver_data) == NULL) - return; - - if (tty3270_wait(tubp, &flags) != 0) { - TUBUNLOCK(tubp->irq, flags); - return; - } - new = L_ICANON(tty)? L_ECHO(tty)? TF_INPUT: TF_INPUTN: - tubp->tty_inattr; - if (new != tubp->tty_inattr) { - tubp->tty_inattr = new; - tubp->cmd = TBC_CLRINPUT; - tty3270_build(tubp); - } - - TUBUNLOCK(tubp->irq, flags); -} - -static void -tty3270_flush_buffer(struct tty_struct *tty) -{ - tub_t *tubp; - bcb_t *ob; - long flags; - - if ((tubp = tty->driver_data) == NULL) - return; - - if (tubp->mode == TBM_FS && tubp->fs_pid != 0) { - kill_proc(tubp->fs_pid, SIGHUP, 1); - } - - if ((tubp->flags & TUB_OPEN_STET) == 0) { - ob = &tubp->tty_bcb; - TUBLOCK(tubp->irq, flags); - ob->bc_rd = 0; - ob->bc_wr = 0; - ob->bc_cnt = 0; - TUBUNLOCK(tubp->irq, flags); - } - wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); -} - -static int -tty3270_read_proc(char *buf, char **start, off_t off, int count, - int *eof, void *data) -{ - tub_t *tubp; - int begin = 0; - int i; - int rc; - int len = 0; - - if (tty3270_proc_what == TW_CONFIG) { - /* - * Describe the 3270 configuration in ascii lines. - * Line 1: 0 0 - * Console line: CONSOLE - * Other lines: - */ - len += sprintf(buf + len, "0 %d 0\n", fs3270_major); - for (i = 1; i <= tubnummins; i++) { - tubp = (*tubminors)[i]; -#ifdef CONFIG_TN3270_CONSOLE - if (CONSOLE_IS_3270 && tubp == tub3270_con_tubp) - len += sprintf(buf + len, "%.4x CONSOLE %d\n", - tubp->devno, i); - else -#endif - len += sprintf(buf + len, "%.4x %d %d\n", - tubp->devno, tty3270_major, i); - if (begin + len > off + count) - break; - if (begin + len < off) { - begin += len; - len = 0; - } - } - if (i > tubnummins) - *eof = 1; - if (off >= begin + len) { - rc = 0; - } else { - *start = buf + off - begin; - rc = MIN(count, begin + len - off); - } - if (*eof && rc == 0) - tty3270_proc_what = TW_BOGUS; - return rc; - } - - len += sprintf(buf, "There are %d devices. fs major is %d, " - "tty major is %d.\n", tubnummins, fs3270_major, - tty3270_major); - len += sprintf(buf+len, " index=%d data=%d misc=%d\n", - tty3270_proc_index, - tty3270_proc_data, - tty3270_proc_misc); - - /* - * Display info for the tube with minor nr in index - */ - len += tty3270_show_tube(tty3270_proc_index, buf+len, count-len); - - *eof = 1; - if (off >= begin + len) - return 0; - *start = buf + off - begin; - return MIN(count, begin + len - off); -} - -static int -tty3270_write_proc(struct file *file, const char *buffer, - unsigned long count, void *data) -{ - char mybuf[GEOM_MAXINPLEN]; - int mycount; - tub_t *tubp; - struct tty_struct *tty; - int rc; - - mycount = MIN(count, sizeof mybuf - 1); - if (copy_from_user(mybuf, buffer, mycount) != 0) - return -EFAULT; - mybuf[mycount] = '\0'; - - /* - * User-mode settings affect only the current tty --- - */ - tubp = NULL; - tty = current->tty; - if (tty && tty->driver == tty3270_driver) - tubp = (*tubminors)[tty->index]; - if (tubp) { - if ((rc = tty3270_aid_set(tubp, mybuf, mycount + 1))) - return rc > 0? count: rc; - if ((rc = tty3270_rcl_set(tubp, mybuf, mycount + 1))) - return rc > 0? count: rc; - if ((rc = tty3270_scl_set(tubp, mybuf, mycount + 1))) - return rc > 0? count: rc; - } - - /* - * Superuser-mode settings affect the driver overall --- - */ - if (!capable(CAP_SYS_TTY_CONFIG)) { - return -EPERM; - } else if (strncmp(mybuf, "index=", 6) == 0) { - tty3270_proc_index = simple_strtoul(mybuf + 6, 0,0); - return count; - } else if (strncmp(mybuf, "data=", 5) == 0) { - tty3270_proc_data = simple_strtoul(mybuf + 5, 0, 0); - return count; - } else if (strncmp(mybuf, "misc=", 5) == 0) { - tty3270_proc_misc = simple_strtoul(mybuf + 5, 0, 0); - return count; - } else if (strncmp(mybuf, "what=", 5) == 0) { - if (strcmp(mybuf+5, "bogus") == 0) - tty3270_proc_what = 0; - else if (strncmp(mybuf+5, "config", 6) == 0) - tty3270_proc_what = TW_CONFIG; - return count; - } else { - return -EINVAL; - } -} - -static void -tty3270_hangup(struct tty_struct *tty) -{ - tub_t *tubp; - extern void fs3270_release(tub_t *); - - if ((tubp = tty->driver_data) == NULL) - return; - tty3270_rcl_purge(tubp); - tty3270_aid_reinit(tubp); - fs3270_release(tubp); -} - - -/* - * tty3270_tasklet(tubp) -- Perform back-half processing - */ -static void -tty3270_tasklet(unsigned long data) -{ - tub_t *tubp; - ioinfo_t *ioinfop; - long flags; - struct tty_struct *tty; - - tubp = (tub_t *) data; - ioinfop = ioinfo[tubp->irq]; - while (TUBTRYLOCK(tubp->irq, flags) == 0) { - if (ioinfop->ui.flags.unready == 1) - return; - } - if (ioinfop->ui.flags.unready == 1 || - ioinfop->ui.flags.ready == 0) - goto do_unlock; - - tubp->flags &= ~TUB_BHPENDING; - tty = tubp->tty; - - if (tubp->flags & TUB_UNSOL_DE) { - tubp->flags &= ~TUB_UNSOL_DE; - if (tty != NULL) { - tty_hangup(tty); - wake_up_interruptible(&tubp->waitq); - goto do_unlock; - } - } - - if (tubp->flags & TUB_IACTIVE) { /* If read ended, */ - tty3270_do_input(tubp); - tubp->flags &= ~TUB_IACTIVE; - } - - if ((tubp->flags & TUB_WORKING) == 0) { - if (tubp->flags & TUB_ATTN) { - tty3270_start_input(tubp); - tubp->flags &= ~TUB_ATTN; - } else if (tty3270_try_logging(tubp) == 0) { - wake_up_interruptible(&tubp->waitq); - } - } - - if (tty != NULL) { - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup != NULL) - (tty->ldisc.write_wakeup)(tty); - wake_up_interruptible(&tty->write_wait); - } -do_unlock: - TUBUNLOCK(tubp->irq, flags); -} - -/* - * tty3270_sched_bh(tubp) -- Schedule the back half - * Irq lock must be held on entry and remains held on exit. - */ -void -tty3270_sched_bh(tub_t *tubp) -{ - if (tubp->flags & TUB_BHPENDING) - return; - tubp->flags |= TUB_BHPENDING; - tasklet_init(&tubp->tasklet, tty3270_tasklet, - (unsigned long) tubp); - tasklet_schedule(&tubp->tasklet); -} - -/* - * tty3270_io() -- Perform line-mode reads and writes here - */ -int -tty3270_io(tub_t *tubp) -{ - int rc; - ccw1_t *ccwp; - - tubp->flags |= TUB_WORKING; - tubp->dstat = 0; - ccwp = &tubp->ttyccw; - - rc = do_IO(tubp->irq, ccwp, tubp->irq, 0, 0); - return rc; -} - -/* - * tty3270_wait(tubp) -- Wait until TUB_WORKING is off - * On entry the lock must not be held; on exit it is held. - */ -static int -tty3270_wait(tub_t *tubp, long *flags) -{ - DECLARE_WAITQUEUE(wait, current); - - TUBLOCK(tubp->irq, *flags); - add_wait_queue(&tubp->waitq, &wait); - while (!signal_pending(current) && - (tubp->flags & TUB_WORKING) != 0) { -#warning FIXME: [kj] use set_current_state instead of current->state= - current->state = TASK_INTERRUPTIBLE; - TUBUNLOCK(tubp->irq, *flags); - schedule(); -#warning FIXME: [kj] use set_current_state instead of current->state= - current->state = TASK_RUNNING; - TUBLOCK(tubp->irq, *flags); - } - remove_wait_queue(&tubp->waitq, &wait); - return signal_pending(current)? -ERESTARTSYS: 0; -} - -void -tty3270_int(tub_t *tubp, devstat_t *dsp) -{ -#define DEV_UE_BUSY \ - (DEV_STAT_CHN_END | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP) -#define DEV_NOT_WORKING \ - (DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_CHECK) - - tubp->dstat = dsp->dstat; - - /* Handle CE-DE-UE and subsequent UDE */ - if (dsp->dstat == DEV_UE_BUSY) { - tubp->flags |= TUB_UE_BUSY; - return; - } else if (tubp->flags & TUB_UE_BUSY) { - tubp->flags &= ~TUB_UE_BUSY; - if (dsp->dstat == DEV_STAT_DEV_END && - (tubp->flags & TUB_WORKING) != 0) { - tty3270_io(tubp); - return; - } - } - - /* Handle ATTN */ - if (dsp->dstat & DEV_STAT_ATTENTION) - tubp->flags |= TUB_ATTN; - - if (dsp->dstat & DEV_STAT_CHN_END) { - tubp->cswl = dsp->rescnt; - if ((dsp->dstat & DEV_STAT_DEV_END) == 0) - tubp->flags |= TUB_EXPECT_DE; - else - tubp->flags &= ~TUB_EXPECT_DE; - } else if (dsp->dstat & DEV_STAT_DEV_END) { - if ((tubp->flags & TUB_EXPECT_DE) == 0) - tubp->flags |= TUB_UNSOL_DE; - tubp->flags &= ~TUB_EXPECT_DE; - } - if (dsp->dstat & DEV_NOT_WORKING) - tubp->flags &= ~TUB_WORKING; - if (dsp->dstat & DEV_STAT_UNIT_CHECK) - tubp->sense = dsp->ii.sense; - if ((tubp->flags & TUB_WORKING) == 0) - tty3270_sched_bh(tubp); -} - -/* - * tty3270_refresh(), called by fs3270_close() when tubp->fsopen == 0. - * On entry, lock is held. - */ -void -tty3270_refresh(tub_t *tubp) -{ - if (tubp->lnopen) { - tubp->mode = TBM_LN; - tubp->intv = tty3270_int; - tty3270_scl_resettimer(tubp); - tubp->cmd = TBC_UPDATE; - tty3270_build(tubp); - } -} - -static int -tty3270_try_logging(tub_t *tubp) -{ - if (tubp->flags & TUB_WORKING) - return 0; - if (tubp->mode == TBM_FS) - return 0; - if (tubp->stat == TBS_HOLD) - return 0; - if (tubp->stat == TBS_MORE) - return 0; -#ifdef CONFIG_TN3270_CONSOLE - if (CONSOLE_IS_3270 && tub3270_con_tubp == tubp) - tub3270_con_copy(tubp); -#endif /* CONFIG_TN3270_CONSOLE */ - if (tubp->tty_bcb.bc_cnt == 0) - return 0; - if (tubp->intv != tty3270_int) - return 0; - tubp->cmd = TBC_UPDLOG; - return tty3270_build(tubp); -} - -/* tty3270 utility functions */ - -static void -tty3270_start_input(tub_t *tubp) -{ - if (tubp->tty_input == NULL) - return; - tubp->ttyccw.cda = virt_to_phys(tubp->tty_input); - tubp->ttyccw.cmd_code = TC_READMOD; - tubp->ttyccw.count = GEOM_INPLEN; - tubp->ttyccw.flags = CCW_FLAG_SLI; - tty3270_io(tubp); - tubp->flags |= TUB_IACTIVE; -} - -static void -tty3270_do_input(tub_t *tubp) -{ - int count; - char *in; - int aidflags; - char *aidstring; - - count = GEOM_INPLEN - tubp->cswl; - if ((in = tubp->tty_input) == NULL) - goto do_build; - tty3270_aid_get(tubp, in[0], &aidflags, &aidstring); - - if (aidflags & TA_CLEARKEY) { - tubp->stat = TBS_RUNNING; - tty3270_scl_resettimer(tubp); - tubp->cmd = TBC_UPDATE; - } else if (aidflags & TA_CLEARLOG) { - tubp->stat = TBS_RUNNING; - tty3270_scl_resettimer(tubp); - tubp->cmd = TBC_CLRUPDLOG; - } else if (aidflags & TA_DOENTER) { - if (count <= 6) { - switch(tubp->stat) { - case TBS_MORE: - tubp->stat = TBS_HOLD; - tty3270_scl_resettimer(tubp); - break; - case TBS_HOLD: - tubp->stat = TBS_MORE; - tty3270_scl_settimer(tubp); - break; - case TBS_RUNNING: - tty3270_do_enter(tubp, in + 6, 0); - break; - } - tubp->cmd = TBC_UPDSTAT; - goto do_build; - } - in += 6; - count -= 6; - TUB_EBCASC(in, count); - tubp->cmd = TBC_CLRINPUT; - tty3270_do_enter(tubp, in, count); - } else if ((aidflags & TA_DOSTRING) != 0 && aidstring != NULL) { - tubp->cmd = TBC_KRUPDLOG; - tty3270_do_enter(tubp, aidstring, strlen(aidstring)); - } else if ((aidflags & TA_DOSTRINGD) != 0 && aidstring != NULL) { - tty3270_do_showi(tubp, aidstring, strlen(aidstring)); - tubp->cmd = TBC_UPDINPUT; - } else { - if (in[0] != 0x60) - tubp->flags |= TUB_ALARM; - tubp->cmd = TBC_KRUPDLOG; - } -do_build: - tty3270_build(tubp); -} - -static void -tty3270_do_enter(tub_t *tubp, char *cp, int count) -{ - struct tty_struct *tty; - int func = -1; - - if ((tty = tubp->tty) == NULL) - return; - if (count < 0) - return; - if (count == 2 && (cp[0] == '^' || cp[0] == '\252')) { - switch(cp[1]) { - case 'c': case 'C': - func = INTR_CHAR(tty); - break; - case 'd': case 'D': - func = EOF_CHAR(tty); - break; - case 'z': case 'Z': - func = SUSP_CHAR(tty); - break; - } - } else if (count == 2 && cp[0] == 0x1b) { /* if ESC */ - int inc = 0; - char buf[GEOM_INPLEN + 1]; - int len; - - switch(cp[1]) { - case 'k': case 'K': - inc = -1; - break; - case 'j': case 'J': - inc = 1; - break; - } - if (inc == 0) - goto not_rcl; - len = tty3270_rcl_get(tubp, buf, sizeof buf, inc); - if (len == 0) { - tubp->flags |= TUB_ALARM; - return; - } - tty3270_do_showi(tubp, buf, len); - tubp->cmd = TBC_UPDINPUT; - return; - } -not_rcl: - if (func != -1) { - *tty->flip.flag_buf_ptr++ = TTY_NORMAL; - *tty->flip.char_buf_ptr++ = func; - tty->flip.count++; - } else { - tty3270_rcl_put(tubp, cp, count); - memcpy(tty->flip.char_buf_ptr, cp, count); - /* Add newline unless line ends with "^n" */ - if (count < 2 || cp[count - 1] != 'n' || - (cp[count - 2] != '^' && cp[count - 2] != '\252')) { - tty->flip.char_buf_ptr[count] = '\n'; - count++; - } else { - count -= 2; /* Lop trailing "^n" from text */ - } - memset(tty->flip.flag_buf_ptr, TTY_NORMAL, count); - tty->flip.char_buf_ptr += count; - tty->flip.flag_buf_ptr += count; - tty->flip.count += count; - } - tty_flip_buffer_push(tty); -} - -static void -tty3270_do_showi(tub_t *tubp, char *cp, int cl) -{ - if (cl > GEOM_INPLEN) - cl = GEOM_INPLEN; - memset(tubp->tty_input, 0, GEOM_INPLEN); - memcpy(tubp->tty_input, cp, cl); - TUB_ASCEBC(tubp->tty_input, cl); -} - - - -/* Debugging routine */ -static int -tty3270_show_tube(int minor, char *buf, int count) -{ - tub_t *tubp; - struct tty_struct *tty; - struct termios *mp; - int len; - -/*012345678901234567890123456789012345678901234567890123456789 */ -/*Info for tub_t[dd] at xxxxxxxx: */ -/* geom: rows=dd cols=dd model=d */ -/* lnopen=dd fsopen=dd waitq=xxxxxxxx */ -/* dstat=xx mode=dd stat=dd flags=xxxx */ -/* oucount=dddd ourd=ddddd ouwr=ddddd nextlogx=ddddd */ -/* tty=xxxxxxxx */ -/* write_wait=xxxxxxxx read_wait=xxxxxxxx */ -/* iflag=xxxxxxxx oflag=xxxxxxxx cflag=xxxxxxxx lflag=xxxxxxxx */ - - if (minor < 0 || minor > tubnummins || - (tubp = (*tubminors)[minor]) == NULL) - return sprintf(buf, "No tube at index=%d\n", minor); - - tty = tubp->tty; - len = 0; - - len += sprintf(buf+len, "Info for tub_t[%d] at %p:\n", minor, tubp); - - len += sprintf(buf+len, "inattr is at %p\n", &tubp->tty_inattr); - - - len += sprintf(buf+len, " geom: rows=%.2d cols=%.2d model=%.1d\n", - tubp->geom_rows, tubp->geom_cols, tubp->tubiocb.model); - - len += sprintf(buf+len, - " lnopen=%-2d fsopen=%-2d waitq=%p\n", - tubp->lnopen, tubp->fsopen, &tubp->waitq); - - len += sprintf(buf+len, " dstat=%.2x mode=%-2d " - "stat=%-2d flags=%-4x\n", tubp->dstat, - tubp->mode, tubp->stat, tubp->flags); - -#ifdef RBH_FIXTHIS - len += sprintf(buf+len, - " oucount=%-4d ourd=%-5d ouwr=%-5d" - " nextlogx=%-5d\n", tubp->tty_oucount, - tubp->tty_ourd, tubp->tty_ouwr, tubp->tty_nextlogx); -#endif - - len += sprintf(buf+len, " tty=%p\n",tubp->tty); - - if (tty) - len += sprintf(buf+len, - " write_wait=%p read_wait=%p\n", - &tty->write_wait, &tty->read_wait); - - if (tty && ((mp = tty->termios))) - len += sprintf(buf+len," iflag=%.8x oflag=%.8x " - "cflag=%.8x lflag=%.8x\n", mp->c_iflag, - mp->c_oflag, mp->c_cflag, mp->c_lflag); - - - return len; -} diff -puN -L drivers/s390/char/tubttyrcl.c drivers/s390/char/tubttyrcl.c~s390-08-new-3270-driver /dev/null --- 25/drivers/s390/char/tubttyrcl.c +++ /dev/null Thu Apr 11 07:25:15 2002 @@ -1,202 +0,0 @@ -/* - * IBM/3270 Driver -- Copyright (C) 2000 UTS Global LLC - * - * tubttyrcl.c -- Linemode Command-recall functionality - * - * - * - * - * - * Author: Richard Hitt - */ -#include "tubio.h" - -static void tty3270_rcl_sync(tub_t *); -static int tty3270_rcl_resize(tub_t *, int); - -int -tty3270_rcl_init(tub_t *tubp) -{ - return tty3270_rcl_resize(tubp, 20); -} - -static int -tty3270_rcl_resize(tub_t *tubp, int newrclk) -{ - char *(*newrclb)[]; - - if (newrclk > 1000) - return -EINVAL; - if (newrclk <= 0) { - tty3270_rcl_purge(tubp), - kfree(tubp->tty_rclbufs); - tubp->tty_rclbufs = NULL; - return 0; - } - if ((newrclb = (char *(*)[])kmalloc( - newrclk * sizeof (char *), GFP_KERNEL)) == NULL) - return -ENOMEM; - memset(newrclb, 0, newrclk * sizeof (char *)); - if (tubp->tty_rclbufs != NULL) { - int i, j, k; - char *data; - - i = tubp->tty_rclp; - j = newrclk; - k = tubp->tty_rclk; - while (j-- && k--) { - if ((data = (*tubp->tty_rclbufs)[i]) == NULL) - break; - (*newrclb)[j] = data; - (*tubp->tty_rclbufs)[i] = NULL; - if (--i < 0) - i = tubp->tty_rclk - 1; - } - tty3270_rcl_purge(tubp); - kfree(tubp->tty_rclbufs); - } - tubp->tty_rclbufs = newrclb; - tubp->tty_rclk = newrclk; - tubp->tty_rclp = newrclk - 1; - tty3270_rcl_sync(tubp); - return 0; -} - -int -tty3270_rcl_set(tub_t *tubp, char *buf, int count) -{ -#define RCL_SIZ "recallsize=" -#define L_RCL_SIZ (strlen(RCL_SIZ)) - int newsize; - int len; - int rc; - char *rcl_siz = RCL_SIZ; - int l_rcl_siz = L_RCL_SIZ; - - if (count < l_rcl_siz || strncmp(buf, rcl_siz, l_rcl_siz) != 0) - return 0; - if ((len = count - l_rcl_siz) == 0) - return count; - newsize = simple_strtoul(buf + l_rcl_siz, 0, 0); - rc = tty3270_rcl_resize(tubp, newsize); - return rc < 0? rc: count; -} - -void -tty3270_rcl_fini(tub_t *tubp) -{ - if (tubp->tty_rclbufs != NULL) { - tty3270_rcl_purge(tubp); - kfree(tubp->tty_rclbufs); - tubp->tty_rclbufs = NULL; - } -} - -void -tty3270_rcl_purge(tub_t *tubp) -{ - int i; - char *buf; - - if (tubp->tty_rclbufs == NULL) - return; - for (i = 0; i < tubp->tty_rclk; i++) { - if ((buf = (*tubp->tty_rclbufs)[i]) == NULL) - continue; - kfree(buf); - (*tubp->tty_rclbufs)[i] = NULL; - } -} - -int -tty3270_rcl_get(tub_t *tubp, char *buf, int len, int inc) -{ - int iter; - int i; - char *data; - - if (tubp->tty_rclbufs == NULL) - return 0; - if (tubp->tty_rclk <= 0) /* overcautious */ - return 0; - if (inc != 1 && inc != -1) /* overcautious */ - return 0; - - if ((i = tubp->tty_rclb) == -1) { - i = tubp->tty_rclp; - if (inc == 1) - i++; - } else { - i += inc; - } - for (iter = tubp->tty_rclk; iter; iter--, i += inc) { - if (i < 0) - i = tubp->tty_rclk - 1; - else if (i >= tubp->tty_rclk) - i = 0; - if ((*tubp->tty_rclbufs)[i] != NULL) - break; - } - if (iter < 0 || (data = (*tubp->tty_rclbufs)[i]) == NULL) - return 0; - tubp->tty_rclb = i; - if ((len = MIN(len - 1, strlen(data))) <= 0) - return 0; - memcpy(buf, data, len); - buf[len] = '\0'; - return len; -} - -void -tty3270_rcl_put(tub_t *tubp, char *data, int len) -{ - char *buf, **bufp; - int i; - - if (tubp->tty_rclbufs == NULL) - return; - - if (tubp->tty_rclk <= 0) /* overcautious */ - return; - - /* If input area is invisible, don't log */ - if (tubp->tty_inattr == TF_INPUTN) - return; - - /* If this & most recent cmd text match, don't log */ - if ((buf = (*tubp->tty_rclbufs)[tubp->tty_rclp]) != NULL && - strlen(buf) == len && memcmp(buf, data, len) == 0) { - tty3270_rcl_sync(tubp); - return; - } - - /* Don't stack zero-length commands */ - if (len == 0) { - tty3270_rcl_sync(tubp); - return; - } - - i = tubp->tty_rclp; - if (++i == tubp->tty_rclk) - i = 0; - bufp = &(*tubp->tty_rclbufs)[i]; - if (*bufp == NULL || strlen(*bufp) < len + 1) { - if (*bufp) { - kfree(*bufp); - *bufp = NULL; - } - if ((*bufp = kmalloc(len + 1, GFP_ATOMIC)) == NULL) - return; - } - memcpy(*bufp, data, len); - (*bufp)[len] = '\0'; - tubp->tty_rclp = i; - tty3270_rcl_sync(tubp); -} - -static void -tty3270_rcl_sync(tub_t *tubp) -{ - tubp->tty_rclb = -1; -} - diff -puN -L drivers/s390/char/tubttyscl.c drivers/s390/char/tubttyscl.c~s390-08-new-3270-driver /dev/null --- 25/drivers/s390/char/tubttyscl.c +++ /dev/null Thu Apr 11 07:25:15 2002 @@ -1,88 +0,0 @@ -/* - * IBM/3270 Driver -- Copyright (C) 2000, 2001 UTS Global LLC - * - * tubttyscl.c -- Linemode tty driver scroll-timing functions - * - * - * - * - * - * Author: Richard Hitt - */ -#include "tubio.h" - void tty3270_scl_settimer(tub_t *); - void tty3270_scl_resettimer(tub_t *); -static void tty3270_scl_timeout(unsigned long); - -void -tty3270_scl_settimer(tub_t *tubp) -{ - struct timer_list *tp = &tubp->tty_stimer; - - if (tubp->flags & TUB_SCROLLTIMING) - return; - if (tubp->tty_scrolltime == 0) - return; - - init_timer(tp); - tp->expires = jiffies + HZ * tubp->tty_scrolltime; - tp->data = (unsigned long)tubp; - tp->function = tty3270_scl_timeout; - add_timer(tp); - tubp->flags |= TUB_SCROLLTIMING; -} - -void -tty3270_scl_resettimer(tub_t *tubp) -{ - struct timer_list *tp = &tubp->tty_stimer; - - if ((tubp->flags & TUB_SCROLLTIMING) == 0) - return; - - del_timer(tp); - tubp->flags &= ~TUB_SCROLLTIMING; -} - -static void -tty3270_scl_timeout(unsigned long data) -{ - tub_t *tubp = (void *)data; - long flags; - - TUBLOCK(tubp->irq, flags); - tubp->stat = TBS_RUNNING; - tty3270_scl_resettimer(tubp); - tubp->cmd = TBC_CLRUPDLOG; - tty3270_build(tubp); - TUBUNLOCK(tubp->irq, flags); -} - -int -tty3270_scl_set(tub_t *tubp, char *buf, int count) -{ - if (strncmp(buf, "scrolltime=", 11) == 0) { - tubp->tty_scrolltime = - simple_strtoul(buf + 11, 0, 0); - return count; - } - return 0; -} - -int -tty3270_scl_init(tub_t *tubp) -{ - extern int tubscrolltime; - - tubp->tty_scrolltime = tubscrolltime; - if (tubp->tty_scrolltime < 0) - tubp->tty_scrolltime = DEFAULT_SCROLLTIME; - return 0; -} - -void -tty3270_scl_fini(tub_t *tubp) -{ - if ((tubp->flags & TUB_OPEN_STET) == 0) - tty3270_scl_resettimer(tubp); -} diff -puN -L drivers/s390/char/tubttysiz.c drivers/s390/char/tubttysiz.c~s390-08-new-3270-driver /dev/null --- 25/drivers/s390/char/tubttysiz.c +++ /dev/null Thu Apr 11 07:25:15 2002 @@ -1,306 +0,0 @@ -/* - * IBM/3270 Driver -- Copyright (C) 2000 UTS Global LLC - * - * tubttysiz.c -- Linemode screen-size determiner - * - * - * - * - * - * Author: Richard Hitt - */ -#include "tubio.h" -static int tty3270_size_io(tub_t *tubp); -static void tty3270_size_int(tub_t *tubp, devstat_t *dsp); -static int tty3270_size_wait(tub_t *tubp, long *flags, int stat); - -/* - * Structure representing Usable Area Query Reply Base - */ -typedef struct { - short l; /* Length of this structured field */ - char sfid; /* 0x81 if Query Reply */ - char qcode; /* 0x81 if Usable Area */ -#define QCODE_UA 0x81 - char flags0; -#define FLAGS0_ADDR 0x0f -#define FLAGS0_ADDR_12_14 1 /* 12/14-bit adrs ok */ -#define FLAGS0_ADDR_12_14_16 3 /* 12/14/16-bit adrs ok */ - char flags1; - short w; /* Width of usable area */ - short h; /* Heigth of usavle area */ - char units; /* 0x00:in; 0x01:mm */ - int xr; - int yr; - char aw; - char ah; - short buffsz; /* Character buffer size, bytes */ - char xmin; - char ymin; - char xmax; - char ymax; -} __attribute__ ((packed)) uab_t; - -/* - * Structure representing Alternate Usable Area Self-Defining Parameter - */ -typedef struct { - char l; /* Length of this Self-Defining Parm */ - char sdpid; /* 0x02 if Alternate Usable Area */ -#define SDPID_AUA 0x02 - char res; - char auaid; /* 0x01 is Id for the A U A */ - short wauai; /* Width of AUAi */ - short hauai; /* Height of AUAi */ - char auaunits; /* 0x00:in, 0x01:mm */ - int auaxr; - int auayr; - char awauai; - char ahauai; -} __attribute__ ((packed)) aua_t; - -/* - * Structure representing one followed by the other - */ -typedef struct { - uab_t uab; - aua_t aua; -} __attribute__ ((packed)) ua_t; - -/* - * Try to determine screen size using Read Partition (Query) - */ -int -tty3270_size(tub_t *tubp, long *flags) -{ - char wbuf[7] = { 0x00, 0x07, 0x01, 0xff, 0x03, 0x00, 0x81 }; - int rc = 0; - int count; - unsigned char *cp; - ua_t *uap; - char miniscreen[256]; - char (*screen)[]; - int screenl; - int geom_rows, geom_cols, fourteenbitadr; - void (*oldint)(struct tub_s *, devstat_t *); - - if (tubp->flags & TUB_SIZED) - return 0; - fourteenbitadr = 0; - geom_rows = tubp->geom_rows; - geom_cols = tubp->geom_cols; - - oldint = tubp->intv; - tubp->intv = tty3270_size_int; - - if (tubp->cmd == TBC_CONOPEN) { - tubp->ttyccw.cmd_code = TC_EWRITEA; - cp = miniscreen; - *cp++ = TW_KR; - /* more? */ - tubp->ttyccw.flags = CCW_FLAG_SLI; - tubp->ttyccw.cda = virt_to_phys(miniscreen); - tubp->ttyccw.count = (char *)cp - miniscreen; - rc = tty3270_size_io(tubp); - rc = tty3270_size_wait(tubp, flags, 0); - } - - tubp->ttyccw.cmd_code = TC_WRITESF; - tubp->ttyccw.flags = CCW_FLAG_SLI; - tubp->ttyccw.cda = virt_to_phys(wbuf); - tubp->ttyccw.count = sizeof wbuf; - -try_again: - rc = tty3270_size_io(tubp); - if (rc) - printk("tty3270_size_io returned %d\n", rc); - - rc = tty3270_size_wait(tubp, flags, 0); - if (rc != 0) { - goto do_return; - } - - /* - * Unit-Check Processing: - * Expect Command Reject or Intervention Required. - * For Command Reject assume old hdwe/software and - * set a default size of 80x24. - * For Intervention Required, wait for signal pending - * or Unsolicited Device End; if the latter, retry. - */ - if (tubp->dstat & DEV_STAT_UNIT_CHECK) { - if (tubp->sense.data[0] & SNS0_CMD_REJECT) { - goto use_diag210; /* perhaps it's tn3270 */ - } else if (tubp->sense.data[0] & SNS0_INTERVENTION_REQ) { - if ((rc = tty3270_size_wait(tubp, flags, - DEV_STAT_DEV_END))) - goto do_return; - goto try_again; - } else { - printk("tty3270_size(): unkn sense %.2x\n", - tubp->sense.data[0]); - goto do_return; - } - } - if ((rc = tty3270_size_wait(tubp, flags, DEV_STAT_ATTENTION))) - goto do_return; - - /* Set up a read ccw and issue it */ - tubp->ttyccw.cmd_code = TC_READMOD; - tubp->ttyccw.flags = CCW_FLAG_SLI; - tubp->ttyccw.cda = virt_to_phys(miniscreen); - tubp->ttyccw.count = sizeof miniscreen; - tty3270_size_io(tubp); - rc = tty3270_size_wait(tubp, flags, 0); - if (rc != 0) - goto do_return; - - count = sizeof miniscreen - tubp->cswl; - cp = miniscreen; - if (*cp++ != 0x88) - goto do_return; - uap = (void *)cp; - if (uap->uab.qcode != QCODE_UA) - goto do_return; - geom_rows = uap->uab.h; - geom_cols = uap->uab.w; - if ((uap->uab.flags0 & FLAGS0_ADDR) == FLAGS0_ADDR_12_14 || - (uap->uab.flags0 & FLAGS0_ADDR) == FLAGS0_ADDR_12_14_16) - fourteenbitadr = 1; - if (uap->uab.l <= sizeof uap->uab) - goto do_return; - if (uap->aua.sdpid != SDPID_AUA) { - printk("AUA sdpid was 0x%.2x, expecting 0x%.2x\n", - uap->aua.sdpid, SDPID_AUA); - goto do_return; - } - geom_rows = uap->aua.hauai; - geom_cols = uap->aua.wauai; - goto do_return; - -use_diag210: - if (MACHINE_IS_VM) { - diag210_t d210; - - d210.vrdcdvno = tubp->devno; - d210.vrdclen = sizeof d210; - rc = diag210(&d210); - if (rc) { - printk("tty3270_size: diag210 for 0x%.4x " - "returned %d\n", tubp->devno, rc); - goto do_return; - } - switch(d210.vrdccrmd) { - case 2: - geom_rows = 24; - geom_cols = 80; - goto do_return; - case 3: - geom_rows = 32; - geom_cols = 80; - goto do_return; - case 4: - geom_rows = 43; - geom_cols = 80; - goto do_return; - case 5: - geom_rows = 27; - geom_cols = 132; - goto do_return; - default: - printk("vrdccrmd is 0x%.8x\n", d210.vrdccrmd); - } - } - -do_return: - if (geom_rows == 0) { - geom_rows = _GEOM_ROWS; - geom_cols = _GEOM_COLS; - } - tubp->tubiocb.pf_cnt = 24; - tubp->tubiocb.re_cnt = 20; - tubp->tubiocb.map = 0; - - screenl = geom_rows * geom_cols + 100; - screen = (char (*)[])kmalloc(screenl, GFP_KERNEL); - if (screen == NULL) { - printk("ttyscreen size %d unavailable\n", screenl); - } else { - if (tubp->ttyscreen) - kfree(tubp->ttyscreen); - tubp->tubiocb.line_cnt = tubp->geom_rows = geom_rows; - tubp->tubiocb.col_cnt = tubp->geom_cols = geom_cols; - tubp->tty_14bitadr = fourteenbitadr; - tubp->ttyscreen = screen; - tubp->ttyscreenl = screenl; - if (geom_rows == 24 && geom_cols == 80) - tubp->tubiocb.model = 2; - else if (geom_rows == 32 && geom_cols == 80) - tubp->tubiocb.model = 3; - else if (geom_rows == 43 && geom_cols == 80) - tubp->tubiocb.model = 4; - else if (geom_rows == 27 && geom_cols == 132) - tubp->tubiocb.model = 5; - else - tubp->tubiocb.model = 0; - tubp->flags |= TUB_SIZED; - } - if (rc == 0 && tubp->ttyscreen == NULL) - rc = -ENOMEM; - tubp->intv = oldint; - return rc; -} - -static int -tty3270_size_io(tub_t *tubp) -{ - tubp->flags |= TUB_WORKING; - tubp->dstat = 0; - - return do_IO(tubp->irq, &tubp->ttyccw, tubp->irq, 0, 0); -} - -static void -tty3270_size_int(tub_t *tubp, devstat_t *dsp) -{ -#define DEV_NOT_WORKING \ - (DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_CHECK) - - tubp->dstat = dsp->dstat; - if (dsp->dstat & DEV_STAT_CHN_END) - tubp->cswl = dsp->rescnt; - if (dsp->dstat & DEV_NOT_WORKING) - tubp->flags &= ~TUB_WORKING; - if (dsp->dstat & DEV_STAT_UNIT_CHECK) - tubp->sense = dsp->ii.sense; - - wake_up_interruptible(&tubp->waitq); -} - -/* - * Wait for something. If the third arg is zero, wait until - * tty3270_size_int() turns off TUB_WORKING. If the third arg - * is not zero, it is a device-status bit; wait until dstat - * has the bit turned on. Never wait if signal is pending. - * Return 0 unless signal pending, in which case -ERESTARTSYS. - */ -static int -tty3270_size_wait(tub_t *tubp, long *flags, int stat) -{ - DECLARE_WAITQUEUE(wait, current); - - add_wait_queue(&tubp->waitq, &wait); - while (!signal_pending(current) && - (stat? (tubp->dstat & stat) == 0: - (tubp->flags & TUB_WORKING) != 0)) { -#warning FIXME: [kj] use set_current_state instead of current->state= - current->state = TASK_INTERRUPTIBLE; - TUBUNLOCK(tubp->irq, *flags); - schedule(); -#warning FIXME: [kj] use set_current_state instead of current->state= - current->state = TASK_RUNNING; - TUBLOCK(tubp->irq, *flags); - } - remove_wait_queue(&tubp->waitq, &wait); - return signal_pending(current)? -ERESTARTSYS: 0; -} _