patch-2.3.99-pre8 linux/drivers/s390/char/hwc_rw.c
Next file: linux/drivers/s390/char/hwc_rw.h
Previous file: linux/drivers/s390/char/hwc_con.c
Back to the patch index
Back to the overall index
- Lines: 2017
- Date:
Fri May 12 11:41:44 2000
- Orig file:
v2.3.99-pre7/linux/drivers/s390/char/hwc_rw.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.3.99-pre7/linux/drivers/s390/char/hwc_rw.c linux/drivers/s390/char/hwc_rw.c
@@ -0,0 +1,2016 @@
+/*
+ * drivers/s390/char/hwc_rw.c
+ * driver: reading from and writing to system console on S/390 via HWC
+ *
+ * S390 version
+ * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
+ * Author(s): Martin Peschke <peschke@fh-brandenburg.de>
+ *
+ *
+ *
+ *
+ *
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/ctype.h>
+#include <linux/mm.h>
+#include <linux/timer.h>
+#include <linux/bootmem.h>
+
+#include <asm/ebcdic.h>
+#include <asm/uaccess.h>
+#include <asm/types.h>
+#include <asm/bitops.h>
+#include <asm/setup.h>
+#include <asm/page.h>
+
+#ifndef MIN
+#define MIN(a,b) ((a<b) ? a : b)
+#endif
+
+#define HWC_RW_PRINT_HEADER "hwc low level driver: "
+
+#define USE_VM_DETECTION
+
+#define DEFAULT_CASE_DELIMITER '%'
+
+#define DUMP_HWC_INIT_ERROR
+
+#define DUMP_HWC_WRITE_ERROR
+
+#define DUMP_HWC_WRITE_LIST_ERROR
+
+#define DUMP_HWC_READ_ERROR
+
+#undef DUMP_HWCB_INPUT
+
+#undef BUFFER_STRESS_TEST
+
+typedef struct {
+ unsigned char *next;
+ unsigned short int mto_char_sum;
+ unsigned char mto_number;
+ unsigned char times_lost;
+ unsigned short int mto_number_lost;
+ unsigned long int mto_char_sum_lost;
+} __attribute__ ((packed))
+
+hwcb_list_t;
+
+#define MAX_HWCB_ROOM (PAGE_SIZE - sizeof(hwcb_list_t))
+
+#define MAX_MESSAGE_SIZE (MAX_HWCB_ROOM - sizeof(write_hwcb_t))
+
+#define BUF_HWCB hwc_data.hwcb_list_tail
+#define OUT_HWCB hwc_data.hwcb_list_head
+#define ALL_HWCB_MTO hwc_data.mto_number
+#define ALL_HWCB_CHAR hwc_data.mto_char_sum
+
+#define _LIST(hwcb) ((hwcb_list_t*)(&(hwcb)[PAGE_SIZE-sizeof(hwcb_list_t)]))
+
+#define _HWCB_CHAR(hwcb) (_LIST(hwcb)->mto_char_sum)
+
+#define _HWCB_MTO(hwcb) (_LIST(hwcb)->mto_number)
+
+#define _HWCB_CHAR_LOST(hwcb) (_LIST(hwcb)->mto_char_sum_lost)
+
+#define _HWCB_MTO_LOST(hwcb) (_LIST(hwcb)->mto_number_lost)
+
+#define _HWCB_TIMES_LOST(hwcb) (_LIST(hwcb)->times_lost)
+
+#define _HWCB_NEXT(hwcb) (_LIST(hwcb)->next)
+
+#define BUF_HWCB_CHAR _HWCB_CHAR(BUF_HWCB)
+
+#define BUF_HWCB_MTO _HWCB_MTO(BUF_HWCB)
+
+#define BUF_HWCB_NEXT _HWCB_NEXT(BUF_HWCB)
+
+#define OUT_HWCB_CHAR _HWCB_CHAR(OUT_HWCB)
+
+#define OUT_HWCB_MTO _HWCB_MTO(OUT_HWCB)
+
+#define OUT_HWCB_NEXT _HWCB_NEXT(OUT_HWCB)
+
+#define BUF_HWCB_CHAR_LOST _HWCB_CHAR_LOST(BUF_HWCB)
+
+#define BUF_HWCB_MTO_LOST _HWCB_MTO_LOST(BUF_HWCB)
+
+#define OUT_HWCB_CHAR_LOST _HWCB_CHAR_LOST(OUT_HWCB)
+
+#define OUT_HWCB_MTO_LOST _HWCB_MTO_LOST(OUT_HWCB)
+
+#define BUF_HWCB_TIMES_LOST _HWCB_TIMES_LOST(BUF_HWCB)
+
+#include "hwc.h"
+
+#define __HWC_RW_C__
+#include "hwc_rw.h"
+#undef __HWC_RW_C__
+
+static unsigned char _obuf[MAX_HWCB_ROOM];
+
+static unsigned char
+ _page[PAGE_SIZE] __attribute__ ((aligned (PAGE_SIZE)));
+
+typedef u32 kmem_pages_t;
+
+#define MAX_KMEM_PAGES (sizeof(kmem_pages_t) << 3)
+
+#define HWC_TIMER_RUNS 1
+#define FLUSH_HWCBS 2
+
+static struct {
+
+ hwc_ioctls_t ioctls;
+
+ hwc_ioctls_t init_ioctls;
+
+ unsigned char *hwcb_list_head;
+
+ unsigned char *hwcb_list_tail;
+
+ unsigned short int mto_number;
+
+ unsigned int mto_char_sum;
+
+ unsigned char hwcb_count;
+
+ unsigned long kmem_start;
+
+ unsigned long kmem_end;
+
+ kmem_pages_t kmem_pages;
+
+ unsigned char *obuf;
+
+ unsigned short int obuf_cursor;
+
+ unsigned short int obuf_count;
+
+ unsigned short int obuf_start;
+
+ unsigned char *page;
+
+ u32 current_servc;
+
+ unsigned char *current_hwcb;
+
+ unsigned char write_nonprio:1;
+ unsigned char write_prio:1;
+ unsigned char read_nonprio:1;
+ unsigned char read_prio:1;
+
+ unsigned char flags;
+
+ spinlock_t lock;
+
+ struct timer_list write_timer;
+} hwc_data =
+{
+ {
+ },
+ {
+ 8,
+ 0,
+ 80,
+ CODE_ASCII,
+ 1,
+ 50,
+ MAX_KMEM_PAGES,
+
+ 0,
+
+ 0x6c
+
+ },
+ NULL,
+ NULL,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ _obuf,
+ 0,
+ 0,
+ 0,
+ _page,
+ 0,
+ NULL,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+
+};
+
+#define DELAYED_WRITE 0
+#define IMMEDIATE_WRITE 1
+
+static signed int do_hwc_write (int from_user, unsigned char *,
+ unsigned int,
+ unsigned char,
+ unsigned char);
+
+static asmlinkage int
+internal_print (char write_time, const char *fmt,...)
+{
+ va_list args;
+ int i;
+ unsigned char buf[512];
+
+ va_start (args, fmt);
+ i = vsprintf (buf, fmt, args);
+ va_end (args);
+ return do_hwc_write (0, buf, i, CODE_ASCII, write_time);
+}
+
+int
+hwc_printk (const char *fmt,...)
+{
+ va_list args;
+ int i;
+ unsigned char buf[512];
+ unsigned long flags;
+ int retval;
+
+ spin_lock_irqsave (&hwc_data.lock, flags);
+
+ i = vsprintf (buf, fmt, args);
+ va_end (args);
+ retval = do_hwc_write (0, buf, i, CODE_ASCII, IMMEDIATE_WRITE);
+
+ spin_unlock_irqrestore (&hwc_data.lock, flags);
+
+ return retval;
+}
+
+#ifdef DUMP_HWCB_INPUT
+
+static void
+dump_storage_area (unsigned char *area, unsigned short int count)
+{
+ unsigned short int index;
+ ioctl_nl_t old_final_nl;
+
+ if (!area || !count)
+ return;
+
+ old_final_nl = hwc_data.ioctls.final_nl;
+ hwc_data.ioctls.final_nl = 1;
+
+ internal_print (DELAYED_WRITE, "\n%8x ", area);
+
+ for (index = 0; index < count; index++) {
+
+ if (area[index] <= 0xF)
+ internal_print (DELAYED_WRITE, "0%x", area[index]);
+ else
+ internal_print (DELAYED_WRITE, "%x", area[index]);
+
+ if ((index & 0xF) == 0xF)
+ internal_print (DELAYED_WRITE, "\n%8x ",
+ &area[index + 1]);
+ else if ((index & 3) == 3)
+ internal_print (DELAYED_WRITE, " ");
+ }
+
+ internal_print (IMMEDIATE_WRITE, "\n");
+
+ hwc_data.ioctls.final_nl = old_final_nl;
+}
+#endif
+
+static inline u32
+service_call (
+ u32 hwc_command_word,
+ unsigned char hwcb[])
+{
+ unsigned int condition_code = 1;
+
+ __asm__ __volatile__ ("L 1, 0(0,%0) \n\t"
+ "LRA 2, 0(0,%1) \n\t"
+ ".long 0xB2200012 \n\t"
+ :
+ :"a" (&hwc_command_word), "a" (hwcb)
+ :"1", "2", "memory");
+
+ __asm__ __volatile__ ("IPM %0 \n\t"
+ "SRL %0, 28 \n\t"
+ :"=r" (condition_code));
+
+ return condition_code;
+}
+
+static inline unsigned char *
+ext_int_param (void)
+{
+ u32 param;
+
+ __asm__ __volatile__ ("L %0,128(0,0)\n\t"
+ :"=r" (param));
+
+ return ((unsigned char *) param);
+}
+
+static int
+prepare_write_hwcb (void)
+{
+ write_hwcb_t *hwcb;
+
+ if (!BUF_HWCB)
+ return -ENOMEM;
+
+ BUF_HWCB_MTO = 0;
+ BUF_HWCB_CHAR = 0;
+
+ hwcb = (write_hwcb_t *) BUF_HWCB;
+
+ memcpy (hwcb, &write_hwcb_template, sizeof (write_hwcb_t));
+
+ if (!hwc_data.write_nonprio && hwc_data.write_prio)
+ hwcb->msgbuf.type = ET_PMsgCmd;
+
+ return 0;
+}
+
+static int
+sane_write_hwcb (void)
+{
+ unsigned short int lost_msg;
+ unsigned int lost_char;
+ unsigned char lost_hwcb;
+ unsigned char *bad_addr;
+ unsigned long page;
+ int page_nr;
+
+ if (!OUT_HWCB)
+ return -ENOMEM;
+
+ if ((unsigned long) OUT_HWCB & 0xFFF) {
+
+ bad_addr = OUT_HWCB;
+
+#ifdef DUMP_HWC_WRITE_LIST_ERROR
+ __asm__ ("LHI 1,0xe30\n\t"
+ "LRA 2,0(0,%0) \n\t"
+ "J .+0 \n\t"
+ :
+ : "a" (bad_addr)
+ : "1", "2");
+#endif
+
+ hwc_data.kmem_pages = 0;
+ if ((unsigned long) BUF_HWCB & 0xFFF) {
+
+ lost_hwcb = hwc_data.hwcb_count;
+ lost_msg = ALL_HWCB_MTO;
+ lost_char = ALL_HWCB_CHAR;
+
+ OUT_HWCB = NULL;
+ BUF_HWCB = NULL;
+ ALL_HWCB_MTO = 0;
+ ALL_HWCB_CHAR = 0;
+ hwc_data.hwcb_count = 0;
+ } else {
+
+ lost_hwcb = hwc_data.hwcb_count - 1;
+ lost_msg = ALL_HWCB_MTO - BUF_HWCB_MTO;
+ lost_char = ALL_HWCB_CHAR - BUF_HWCB_CHAR;
+ OUT_HWCB = BUF_HWCB;
+ ALL_HWCB_MTO = BUF_HWCB_MTO;
+ ALL_HWCB_CHAR = BUF_HWCB_CHAR;
+ hwc_data.hwcb_count = 1;
+ page = (unsigned long) BUF_HWCB;
+
+ if (page >= hwc_data.kmem_start &&
+ page < hwc_data.kmem_end) {
+
+ page_nr = (int)
+ ((page - hwc_data.kmem_start) >> 12);
+ set_bit (page_nr, &hwc_data.kmem_pages);
+ }
+ }
+
+ internal_print (
+ DELAYED_WRITE,
+ HWC_RW_PRINT_HEADER
+ "found invalid HWCB at address 0x%x. List corrupted. "
+ "Lost %i HWCBs with %i characters within up to %i "
+ "messages. Saved %i HWCB with last %i characters i"
+ "within up to %i messages.\n",
+ (unsigned int) bad_addr,
+ lost_hwcb, lost_char, lost_msg,
+ hwc_data.hwcb_count,
+ ALL_HWCB_CHAR, ALL_HWCB_MTO);
+ }
+ return 0;
+}
+
+static int
+reuse_write_hwcb (void)
+{
+ int retval;
+
+ if (hwc_data.hwcb_count < 2)
+#ifdef DUMP_HWC_WRITE_LIST_ERROR
+ __asm__ ("LHI 1,0xe31\n\t"
+ "LRA 2,0(0,%0)\n\t"
+ "LRA 3,0(0,%1)\n\t"
+ "J .+0 \n\t"
+ :
+ : "a" (BUF_HWCB), "a" (OUT_HWCB)
+ : "1", "2", "3");
+#else
+ return -EPERM;
+#endif
+
+ if (hwc_data.current_hwcb == OUT_HWCB) {
+
+ if (hwc_data.hwcb_count > 2) {
+
+ BUF_HWCB_NEXT = OUT_HWCB_NEXT;
+
+ BUF_HWCB = OUT_HWCB_NEXT;
+
+ OUT_HWCB_NEXT = BUF_HWCB_NEXT;
+
+ BUF_HWCB_NEXT = NULL;
+ }
+ } else {
+
+ BUF_HWCB_NEXT = OUT_HWCB;
+
+ BUF_HWCB = OUT_HWCB;
+
+ OUT_HWCB = OUT_HWCB_NEXT;
+
+ BUF_HWCB_NEXT = NULL;
+ }
+
+ BUF_HWCB_TIMES_LOST += 1;
+ BUF_HWCB_CHAR_LOST += BUF_HWCB_CHAR;
+ BUF_HWCB_MTO_LOST += BUF_HWCB_MTO;
+ ALL_HWCB_MTO -= BUF_HWCB_MTO;
+ ALL_HWCB_CHAR -= BUF_HWCB_CHAR;
+
+ retval = prepare_write_hwcb ();
+
+ if (hwc_data.hwcb_count == hwc_data.ioctls.max_hwcb)
+ internal_print (
+ DELAYED_WRITE,
+ HWC_RW_PRINT_HEADER
+ "reached my own limit of "
+ "allowed buffer space for output (%i HWCBs = %li "
+ "bytes), skipped content of oldest HWCB %i time(s) "
+ "(%i lines = %i characters)\n",
+ hwc_data.ioctls.max_hwcb,
+ hwc_data.ioctls.max_hwcb * PAGE_SIZE,
+ BUF_HWCB_TIMES_LOST,
+ BUF_HWCB_MTO_LOST,
+ BUF_HWCB_CHAR_LOST);
+ else
+ internal_print (
+ DELAYED_WRITE,
+ HWC_RW_PRINT_HEADER
+ "page allocation failed, "
+ "could not expand buffer for output (currently in "
+ "use: %i HWCBs = %li bytes), skipped content of "
+ "oldest HWCB %i time(s) (%i lines = %i characters)\n",
+ hwc_data.hwcb_count,
+ hwc_data.hwcb_count * PAGE_SIZE,
+ BUF_HWCB_TIMES_LOST,
+ BUF_HWCB_MTO_LOST,
+ BUF_HWCB_CHAR_LOST);
+
+ return retval;
+}
+
+static int
+allocate_write_hwcb (void)
+{
+ unsigned char *page;
+ int page_nr;
+
+ if (hwc_data.hwcb_count == hwc_data.ioctls.max_hwcb)
+ return -ENOMEM;
+
+ page_nr = find_first_zero_bit (&hwc_data.kmem_pages, MAX_KMEM_PAGES);
+ if (page_nr < hwc_data.ioctls.kmem_hwcb) {
+
+ page = (unsigned char *)
+ (hwc_data.kmem_start + (page_nr << 12));
+ set_bit (page_nr, &hwc_data.kmem_pages);
+ } else
+ page = (unsigned char *) __get_free_page (GFP_ATOMIC);
+
+ if (!page)
+ return -ENOMEM;
+
+ if (!OUT_HWCB)
+ OUT_HWCB = page;
+ else
+ BUF_HWCB_NEXT = page;
+
+ BUF_HWCB = page;
+
+ BUF_HWCB_NEXT = NULL;
+
+ hwc_data.hwcb_count++;
+
+ prepare_write_hwcb ();
+
+ BUF_HWCB_TIMES_LOST = 0;
+ BUF_HWCB_MTO_LOST = 0;
+ BUF_HWCB_CHAR_LOST = 0;
+
+#ifdef BUFFER_STRESS_TEST
+
+ internal_print (
+ DELAYED_WRITE,
+ "*** " HWC_RW_PRINT_HEADER
+ "page #%i at 0x%x for buffering allocated. ***\n",
+ hwc_data.hwcb_count, page);
+
+#endif
+
+ return 0;
+}
+
+static int
+release_write_hwcb (void)
+{
+ unsigned long page;
+ int page_nr;
+
+ if (!hwc_data.hwcb_count)
+ return -ENODATA;
+
+ if (hwc_data.hwcb_count == 1) {
+
+ prepare_write_hwcb ();
+
+ ALL_HWCB_CHAR = 0;
+ ALL_HWCB_MTO = 0;
+ BUF_HWCB_TIMES_LOST = 0;
+ BUF_HWCB_MTO_LOST = 0;
+ BUF_HWCB_CHAR_LOST = 0;
+ } else {
+ page = (unsigned long) OUT_HWCB;
+
+ ALL_HWCB_MTO -= OUT_HWCB_MTO;
+ ALL_HWCB_CHAR -= OUT_HWCB_CHAR;
+ hwc_data.hwcb_count--;
+
+ OUT_HWCB = OUT_HWCB_NEXT;
+
+ if (page >= hwc_data.kmem_start &&
+ page < hwc_data.kmem_end) {
+
+ memset ((void *) page, 0, PAGE_SIZE);
+
+ page_nr = (int) ((page - hwc_data.kmem_start) >> 12);
+ clear_bit (page_nr, &hwc_data.kmem_pages);
+ } else
+ free_page (page);
+#ifdef BUFFER_STRESS_TEST
+
+ internal_print (
+ DELAYED_WRITE,
+ "*** " HWC_RW_PRINT_HEADER
+ "page at 0x%x released, %i pages still in use ***\n",
+ page, hwc_data.hwcb_count);
+
+#endif
+ }
+ return 0;
+}
+
+static int
+add_mto (
+ unsigned char *message,
+ unsigned short int count)
+{
+ unsigned short int mto_size;
+ write_hwcb_t *hwcb;
+ mto_t *mto;
+ void *dest;
+
+ if (!BUF_HWCB)
+ return -ENOMEM;
+
+ if (BUF_HWCB == hwc_data.current_hwcb)
+ return -ENOMEM;
+
+ mto_size = sizeof (mto_t) + count;
+
+ hwcb = (write_hwcb_t *) BUF_HWCB;
+
+ if ((MAX_HWCB_ROOM - hwcb->length) < mto_size)
+ return -ENOMEM;
+
+ mto = (mto_t *) (((unsigned long) hwcb) + hwcb->length);
+
+ memcpy (mto, &mto_template, sizeof (mto_t));
+
+ dest = (void *) (((unsigned long) mto) + sizeof (mto_t));
+
+ memcpy (dest, message, count);
+
+ mto->length += count;
+
+ hwcb->length += mto_size;
+ hwcb->msgbuf.length += mto_size;
+ hwcb->msgbuf.mdb.length += mto_size;
+
+ BUF_HWCB_MTO++;
+ ALL_HWCB_MTO++;
+ BUF_HWCB_CHAR += count;
+ ALL_HWCB_CHAR += count;
+
+ return count;
+}
+
+static int
+write_event_data_1 (void)
+{
+ unsigned short int condition_code;
+ int retval;
+
+ if ((!hwc_data.write_prio) && (!hwc_data.write_nonprio))
+ return -EPERM;
+
+ if (hwc_data.current_servc)
+ return -EBUSY;
+
+ retval = sane_write_hwcb ();
+ if (retval < 0)
+ return retval;
+
+ if (!OUT_HWCB_MTO)
+ return -ENODATA;
+
+ condition_code = service_call (HWC_CMDW_WRITEDATA, OUT_HWCB);
+
+#ifdef DUMP_HWC_WRITE_ERROR
+ if (condition_code != HWC_COMMAND_INITIATED)
+ __asm__ ("LHI 1,0xe20\n\t"
+ "L 2,0(0,%0)\n\t"
+ "LRA 3,0(0,%1)\n\t"
+ "J .+0 \n\t"
+ :
+ : "a" (&condition_code), "a" (OUT_HWCB)
+ : "1", "2", "3");
+#endif
+
+ switch (condition_code) {
+ case HWC_COMMAND_INITIATED:
+ hwc_data.current_servc = HWC_CMDW_WRITEDATA;
+ hwc_data.current_hwcb = OUT_HWCB;
+ retval = condition_code;
+ break;
+ case HWC_BUSY:
+ retval = -EBUSY;
+ break;
+ default:
+ retval = -EIO;
+ }
+
+ return retval;
+}
+
+static void
+flush_hwcbs (void)
+{
+ while (hwc_data.hwcb_count > 1)
+ release_write_hwcb ();
+
+ release_write_hwcb ();
+
+ hwc_data.flags &= ~FLUSH_HWCBS;
+}
+
+static int
+write_event_data_2 (void)
+{
+ write_hwcb_t *hwcb;
+ int retval;
+ unsigned char *param;
+
+ param = ext_int_param ();
+ if (param != hwc_data.current_hwcb)
+ return -EINVAL;
+
+ hwcb = (write_hwcb_t *) OUT_HWCB;
+
+#ifdef DUMP_HWC_WRITE_ERROR
+#if 0
+ if (((unsigned char *) hwcb) != param)
+ __asm__ ("LHI 1,0xe22\n\t"
+ "LRA 2,0(0,%0)\n\t"
+ "LRA 3,0(0,%1)\n\t"
+ "LRA 4,0(0,%2)\n\t"
+ "LRA 5,0(0,%3)\n\t"
+ "J .+0 \n\t"
+ :
+ : "a" (OUT_HWCB),
+ "a" (hwc_data.current_hwcb),
+ "a" (BUF_HWCB),
+ "a" (param)
+ : "1", "2", "3", "4", "5");
+#endif
+ if (hwcb->response_code != 0x0020)
+#if 0
+ internal_print (DELAYED_WRITE, HWC_RW_PRINT_HEADER
+ "\n************************ error in write_event_data_2()\n"
+ "OUT_HWCB: 0x%x\n"
+ "BUF_HWCB: 0x%x\n"
+ "response_code: 0x%x\n"
+ "hwc_data.hwcb_count: %d\n"
+ "hwc_data.kmem_pages: 0x%x\n"
+ "hwc_data.ioctls.kmem_hwcb: %d\n"
+ "hwc_data.ioctls.max_hwcb: %d\n"
+ "hwc_data.kmem_start: 0x%x\n"
+ "hwc_data.kmem_end: 0x%x\n"
+ "*****************************************************\n",
+ OUT_HWCB,
+ BUF_HWCB,
+ hwcb->response_code,
+ hwc_data.hwcb_count,
+ hwc_data.kmem_pages,
+ hwc_data.ioctls.kmem_hwcb,
+ hwc_data.ioctls.max_hwcb,
+ hwc_data.kmem_start,
+ hwc_data.kmem_end);
+#endif
+ __asm__ ("LHI 1,0xe21\n\t"
+ "LRA 2,0(0,%0)\n\t"
+ "LRA 3,0(0,%1)\n\t"
+ "LRA 4,0(0,%2)\n\t"
+ "LH 5,0(0,%3)\n\t"
+ "SRL 5,8(0)\n\t"
+ "J .+0 \n\t"
+ :
+ : "a" (OUT_HWCB), "a" (hwc_data.current_hwcb),
+ "a" (BUF_HWCB),
+ "a" (&(hwc_data.hwcb_count))
+ : "1", "2", "3", "4", "5");
+#endif
+
+ if (hwcb->response_code == 0x0020) {
+
+ retval = OUT_HWCB_CHAR;
+ release_write_hwcb ();
+ } else
+ retval = -EIO;
+
+ hwc_data.current_servc = 0;
+ hwc_data.current_hwcb = NULL;
+
+ if (hwc_data.flags & FLUSH_HWCBS)
+ flush_hwcbs ();
+
+ return retval;
+}
+
+static void
+do_put_line (
+ unsigned char *message,
+ unsigned short count)
+{
+
+ if (add_mto (message, count) != count) {
+
+ if (allocate_write_hwcb () < 0)
+ reuse_write_hwcb ();
+
+#ifdef DUMP_HWC_WRITE_LIST_ERROR
+ if (add_mto (message, count) != count)
+ __asm__ ("LHI 1,0xe32\n\t"
+ "LRA 2,0(0,%0)\n\t"
+ "L 3,0(0,%1)\n\t"
+ "LRA 4,0(0,%2)\n\t"
+ "LRA 5,0(0,%3)\n\t"
+ "J .+0 \n\t"
+ :
+ : "a" (message), "a" (&hwc_data.kmem_pages),
+ "a" (BUF_HWCB), "a" (OUT_HWCB)
+ : "1", "2", "3", "4", "5");
+#else
+ add_mto (message, count);
+#endif
+ }
+}
+
+static void
+put_line (
+ unsigned char *message,
+ unsigned short count)
+{
+
+ if ((!hwc_data.obuf_start) && (hwc_data.flags & HWC_TIMER_RUNS)) {
+ del_timer (&hwc_data.write_timer);
+ hwc_data.flags &= ~HWC_TIMER_RUNS;
+ }
+ hwc_data.obuf_start += count;
+
+ do_put_line (message, count);
+
+ hwc_data.obuf_start -= count;
+}
+
+static void
+set_alarm (void)
+{
+ write_hwcb_t *hwcb;
+
+ if ((!BUF_HWCB) || (BUF_HWCB == hwc_data.current_hwcb))
+ allocate_write_hwcb ();
+
+ hwcb = (write_hwcb_t *) BUF_HWCB;
+ hwcb->msgbuf.mdb.mdb_body.go.general_msg_flags |= GMF_SndAlrm;
+}
+
+static void
+hwc_write_timeout (unsigned long data)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave (&hwc_data.lock, flags);
+
+ hwc_data.obuf_start = hwc_data.obuf_count;
+ if (hwc_data.obuf_count)
+ put_line (hwc_data.obuf, hwc_data.obuf_count);
+ hwc_data.obuf_start = 0;
+
+ hwc_data.obuf_cursor = 0;
+ hwc_data.obuf_count = 0;
+
+ write_event_data_1 ();
+
+ spin_unlock_irqrestore (&hwc_data.lock, flags);
+}
+
+static int
+do_hwc_write (
+ int from_user,
+ unsigned char *msg,
+ unsigned int count,
+ unsigned char code,
+ unsigned char write_time)
+{
+ unsigned int i_msg = 0;
+ unsigned short int spaces = 0;
+ unsigned int processed_characters = 0;
+ unsigned char ch, orig_ch;
+ unsigned short int obuf_count;
+ unsigned short int obuf_cursor;
+ unsigned short int obuf_columns;
+
+ if (hwc_data.obuf_start) {
+ obuf_cursor = 0;
+ obuf_count = 0;
+ obuf_columns = MIN (hwc_data.ioctls.columns,
+ MAX_MESSAGE_SIZE - hwc_data.obuf_start);
+ } else {
+ obuf_cursor = hwc_data.obuf_cursor;
+ obuf_count = hwc_data.obuf_count;
+ obuf_columns = hwc_data.ioctls.columns;
+ }
+
+ for (i_msg = 0; i_msg < count; i_msg++) {
+
+ if (from_user)
+ get_user (orig_ch, msg + i_msg);
+ else
+ orig_ch = msg[i_msg];
+ if (code == CODE_EBCDIC)
+ ch = _ebcasc[orig_ch];
+ else
+ ch = orig_ch;
+
+ processed_characters++;
+
+ if ((obuf_cursor == obuf_columns) &&
+
+ (ch != '\n') &&
+
+ (ch != '\t')) {
+ put_line (&hwc_data.obuf[hwc_data.obuf_start],
+ obuf_columns);
+ obuf_cursor = 0;
+ obuf_count = 0;
+ }
+ switch (ch) {
+
+ case '\n':
+
+ put_line (&hwc_data.obuf[hwc_data.obuf_start],
+ obuf_count);
+ obuf_cursor = 0;
+ obuf_count = 0;
+ break;
+
+ case '\a':
+
+ hwc_data.obuf_start += obuf_count;
+ set_alarm ();
+ hwc_data.obuf_start -= obuf_count;
+
+ break;
+
+ case '\t':
+
+ do {
+ if (obuf_cursor < obuf_columns) {
+ hwc_data.obuf[hwc_data.obuf_start +
+ obuf_cursor]
+ = 0x20;
+ obuf_cursor++;
+ } else
+ break;
+ } while (obuf_cursor % hwc_data.ioctls.width_htab);
+
+ break;
+
+ case '\f':
+ case '\v':
+
+ spaces = obuf_cursor;
+ put_line (&hwc_data.obuf[hwc_data.obuf_start],
+ obuf_count);
+ obuf_count = obuf_cursor;
+ while (spaces) {
+ hwc_data.obuf[hwc_data.obuf_start +
+ obuf_cursor - spaces]
+ = 0x20;
+ spaces--;
+ }
+
+ break;
+
+ case '\b':
+
+ if (obuf_cursor)
+ obuf_cursor--;
+ break;
+
+ case '\r':
+
+ obuf_cursor = 0;
+ break;
+
+ case 0x00:
+
+ put_line (&hwc_data.obuf[hwc_data.obuf_start],
+ obuf_count);
+ obuf_cursor = 0;
+ obuf_count = 0;
+ goto out;
+
+ default:
+
+ if (isprint (ch))
+ hwc_data.obuf[hwc_data.obuf_start +
+ obuf_cursor++]
+ = (code == CODE_ASCII) ?
+ _ascebc[orig_ch] : orig_ch;
+ }
+ if (obuf_cursor > obuf_count)
+ obuf_count = obuf_cursor;
+ }
+
+ if (obuf_cursor) {
+
+ if (hwc_data.obuf_start ||
+ (hwc_data.ioctls.final_nl == 0)) {
+
+ put_line (&hwc_data.obuf[hwc_data.obuf_start],
+ obuf_count);
+ obuf_cursor = 0;
+ obuf_count = 0;
+ } else {
+
+ if (hwc_data.ioctls.final_nl > 0) {
+
+ if (hwc_data.flags & HWC_TIMER_RUNS) {
+
+ hwc_data.write_timer.expires =
+ jiffies +
+ hwc_data.ioctls.final_nl * HZ / 10;
+ } else {
+
+ init_timer (&hwc_data.write_timer);
+ hwc_data.write_timer.function =
+ hwc_write_timeout;
+ hwc_data.write_timer.data =
+ (unsigned long) NULL;
+ hwc_data.write_timer.expires =
+ jiffies +
+ hwc_data.ioctls.final_nl * HZ / 10;
+ add_timer (&hwc_data.write_timer);
+ hwc_data.flags |= HWC_TIMER_RUNS;
+ }
+ } else;
+
+ }
+ } else;
+
+ out:
+
+ if (!hwc_data.obuf_start) {
+ hwc_data.obuf_cursor = obuf_cursor;
+ hwc_data.obuf_count = obuf_count;
+ }
+ if (write_time == IMMEDIATE_WRITE)
+ write_event_data_1 ();
+
+ return processed_characters;
+}
+
+signed int
+hwc_write (int from_user, const unsigned char *msg, unsigned int count)
+{
+ unsigned long flags;
+ int retval;
+
+ spin_lock_irqsave (&hwc_data.lock, flags);
+
+ retval = do_hwc_write (from_user, msg, count, hwc_data.ioctls.code,
+ IMMEDIATE_WRITE);
+
+ spin_unlock_irqrestore (&hwc_data.lock, flags);
+
+ return retval;
+}
+
+unsigned int
+hwc_chars_in_buffer (unsigned char flag)
+{
+ unsigned short int number = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave (&hwc_data.lock, flags);
+
+ if (flag & IN_HWCB)
+ number += ALL_HWCB_CHAR;
+
+ if (flag & IN_WRITE_BUF)
+ number += hwc_data.obuf_cursor;
+
+ spin_unlock_irqrestore (&hwc_data.lock, flags);
+
+ return number;
+}
+
+static inline int
+nr_setbits (kmem_pages_t arg)
+{
+ int i;
+ int nr = 0;
+
+ for (i = 0; i < (sizeof (arg) << 3); i++) {
+ if (arg & 1)
+ nr++;
+ arg >>= 1;
+ }
+
+ return nr;
+}
+
+unsigned int
+hwc_write_room (unsigned char flag)
+{
+ unsigned int number = 0;
+ unsigned long flags;
+ write_hwcb_t *hwcb;
+
+ spin_lock_irqsave (&hwc_data.lock, flags);
+
+ if (flag & IN_HWCB) {
+
+ if (BUF_HWCB) {
+ hwcb = (write_hwcb_t *) BUF_HWCB;
+ number += MAX_HWCB_ROOM - hwcb->length;
+ }
+ number += (hwc_data.ioctls.kmem_hwcb -
+ nr_setbits (hwc_data.kmem_pages)) *
+ (MAX_HWCB_ROOM -
+ (sizeof (write_hwcb_t) + sizeof (mto_t)));
+ }
+ if (flag & IN_WRITE_BUF)
+ number += MAX_HWCB_ROOM - hwc_data.obuf_cursor;
+
+ spin_unlock_irqrestore (&hwc_data.lock, flags);
+
+ return number;
+}
+
+void
+hwc_flush_buffer (unsigned char flag)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave (&hwc_data.lock, flags);
+
+ if (flag & IN_HWCB) {
+ if (hwc_data.current_servc != HWC_CMDW_WRITEDATA)
+ flush_hwcbs ();
+ else
+ hwc_data.flags |= FLUSH_HWCBS;
+ }
+ if (flag & IN_WRITE_BUF) {
+ hwc_data.obuf_cursor = 0;
+ hwc_data.obuf_count = 0;
+ }
+ spin_unlock_irqrestore (&hwc_data.lock, flags);
+}
+
+unsigned short int
+seperate_cases (unsigned char *buf, unsigned short int count)
+{
+
+ unsigned short int i_in;
+
+ unsigned short int i_out = 0;
+
+ unsigned char _case = 0;
+
+ for (i_in = 0; i_in < count; i_in++) {
+
+ if (buf[i_in] == hwc_data.ioctls.delim) {
+
+ if ((i_in + 1 < count) &&
+ (buf[i_in + 1] == hwc_data.ioctls.delim)) {
+
+ buf[i_out] = hwc_data.ioctls.delim;
+
+ i_out++;
+
+ i_in++;
+
+ } else
+ _case = ~_case;
+
+ } else {
+
+ if (_case) {
+
+ if (hwc_data.ioctls.tolower)
+ buf[i_out] = _ebc_toupper[buf[i_in]];
+
+ else
+ buf[i_out] = _ebc_tolower[buf[i_in]];
+
+ } else
+ buf[i_out] = buf[i_in];
+
+ i_out++;
+ }
+ }
+
+ return i_out;
+}
+
+#ifdef DUMP_HWCB_INPUT
+
+static int
+gds_vector_name (u16 id, unsigned char name[])
+{
+ int retval = 0;
+
+ switch (id) {
+ case GDS_ID_MDSMU:
+ name = "Multiple Domain Support Message Unit";
+ break;
+ case GDS_ID_MDSRouteInfo:
+ name = "MDS Routing Information";
+ break;
+ case GDS_ID_AgUnWrkCorr:
+ name = "Agent Unit of Work Correlator";
+ break;
+ case GDS_ID_SNACondReport:
+ name = "SNA Condition Report";
+ break;
+ case GDS_ID_CPMSU:
+ name = "CP Management Services Unit";
+ break;
+ case GDS_ID_RoutTargInstr:
+ name = "Routing and Targeting Instructions";
+ break;
+ case GDS_ID_OpReq:
+ name = "Operate Request";
+ break;
+ case GDS_ID_TextCmd:
+ name = "Text Command";
+ break;
+
+ default:
+ name = "unknown GDS variable";
+ retval = -EINVAL;
+ }
+
+ return retval;
+}
+#endif
+
+inline static gds_vector_t *
+find_gds_vector (
+ gds_vector_t * start, void *end, u16 id)
+{
+ gds_vector_t *vec;
+ gds_vector_t *retval = NULL;
+
+ vec = start;
+
+ while (((void *) vec) < end) {
+ if (vec->gds_id == id) {
+
+#ifdef DUMP_HWCB_INPUT
+ int retval_name;
+ unsigned char name[64];
+
+ retval_name = gds_vector_name (id, name);
+ internal_print (
+ DELAYED_WRITE,
+ HWC_RW_PRINT_HEADER
+ "%s at 0x%x up to 0x%x, length: %d",
+ name,
+ (unsigned long) vec,
+ ((unsigned long) vec) + vec->length - 1,
+ vec->length);
+ if (retval_name < 0)
+ internal_print (
+ IMMEDIATE_WRITE,
+ ", id: 0x%x\n",
+ vec->gds_id);
+ else
+ internal_print (
+ IMMEDIATE_WRITE,
+ "\n");
+#endif
+
+ retval = vec;
+ break;
+ }
+ vec = (gds_vector_t *) (((unsigned long) vec) + vec->length);
+ }
+
+ return retval;
+}
+
+inline static gds_subvector_t *
+find_gds_subvector (
+ gds_subvector_t * start, void *end, u8 key)
+{
+ gds_subvector_t *subvec;
+ gds_subvector_t *retval = NULL;
+
+ subvec = start;
+
+ while (((void *) subvec) < end) {
+ if (subvec->key == key) {
+ retval = subvec;
+ break;
+ }
+ subvec = (gds_subvector_t *)
+ (((unsigned long) subvec) + subvec->length);
+ }
+
+ return retval;
+}
+
+inline static int
+get_input (void *start, void *end)
+{
+ int count;
+
+ count = ((unsigned long) end) - ((unsigned long) start);
+
+ if (hwc_data.ioctls.tolower)
+ EBC_TOLOWER (start, count);
+
+ if (hwc_data.ioctls.delim)
+ count = seperate_cases (start, count);
+
+ if (hwc_data.ioctls.echo)
+ do_hwc_write (0, start, count, CODE_EBCDIC, IMMEDIATE_WRITE);
+
+ if (hwc_data.ioctls.code == CODE_ASCII)
+ EBCASC (start, count);
+
+ store_hwc_input (start, count);
+
+ return count;
+}
+
+inline static int
+eval_selfdeftextmsg (gds_subvector_t * start, void *end)
+{
+ gds_subvector_t *subvec;
+ void *subvec_data;
+ void *subvec_end;
+ int retval = 0;
+
+ subvec = start;
+
+ while (((void *) subvec) < end) {
+ subvec = find_gds_subvector (subvec, end, 0x30);
+ if (!subvec)
+ break;
+ subvec_data = (void *)
+ (((unsigned long) subvec) +
+ sizeof (gds_subvector_t));
+ subvec_end = (void *)
+ (((unsigned long) subvec) + subvec->length);
+ retval += get_input (subvec_data, subvec_end);
+ subvec = (gds_subvector_t *) subvec_end;
+ }
+
+ return retval;
+}
+
+inline static int
+eval_textcmd (gds_subvector_t * start, void *end)
+{
+ gds_subvector_t *subvec;
+ gds_subvector_t *subvec_data;
+ void *subvec_end;
+ int retval = 0;
+
+ subvec = start;
+
+ while (((void *) subvec) < end) {
+ subvec = find_gds_subvector (
+ subvec, end, GDS_KEY_SelfDefTextMsg);
+ if (!subvec)
+ break;
+ subvec_data = (gds_subvector_t *)
+ (((unsigned long) subvec) +
+ sizeof (gds_subvector_t));
+ subvec_end = (void *)
+ (((unsigned long) subvec) + subvec->length);
+ retval += eval_selfdeftextmsg (subvec_data, subvec_end);
+ subvec = (gds_subvector_t *) subvec_end;
+ }
+
+ return retval;
+}
+
+inline static int
+eval_cpmsu (gds_vector_t * start, void *end)
+{
+ gds_vector_t *vec;
+ gds_subvector_t *vec_data;
+ void *vec_end;
+ int retval = 0;
+
+ vec = start;
+
+ while (((void *) vec) < end) {
+ vec = find_gds_vector (vec, end, GDS_ID_TextCmd);
+ if (!vec)
+ break;
+ vec_data = (gds_subvector_t *)
+ (((unsigned long) vec) + sizeof (gds_vector_t));
+ vec_end = (void *) (((unsigned long) vec) + vec->length);
+ retval += eval_textcmd (vec_data, vec_end);
+ vec = (gds_vector_t *) vec_end;
+ }
+
+ return retval;
+}
+
+inline static int
+eval_mdsmu (gds_vector_t * start, void *end)
+{
+ gds_vector_t *vec;
+ gds_vector_t *vec_data;
+ void *vec_end;
+ int retval = 0;
+
+ vec = find_gds_vector (start, end, GDS_ID_CPMSU);
+ if (vec) {
+ vec_data = (gds_vector_t *)
+ (((unsigned long) vec) + sizeof (gds_vector_t));
+ vec_end = (void *) (((unsigned long) vec) + vec->length);
+ retval = eval_cpmsu (vec_data, vec_end);
+ }
+ return retval;
+}
+
+inline static int
+eval_evbuf (gds_vector_t * start, void *end)
+{
+ gds_vector_t *vec;
+ gds_vector_t *vec_data;
+ void *vec_end;
+ int retval = 0;
+
+ vec = find_gds_vector (start, end, GDS_ID_MDSMU);
+ if (vec) {
+ vec_data = (gds_vector_t *)
+ (((unsigned long) vec) + sizeof (gds_vector_t));
+ vec_end = (void *) (((unsigned long) vec) + vec->length);
+ retval = eval_mdsmu (vec_data, vec_end);
+ }
+ return retval;
+}
+
+static int
+process_evbufs (void *start, void *end)
+{
+ int retval = 0;
+ evbuf_t *evbuf;
+ void *evbuf_end;
+ gds_vector_t *evbuf_data;
+
+ evbuf = (evbuf_t *) start;
+ while (((void *) evbuf) < end) {
+ evbuf_data = (gds_vector_t *)
+ (((unsigned long) evbuf) + sizeof (evbuf_t));
+ evbuf_end = (void *) (((unsigned long) evbuf) + evbuf->length);
+ switch (evbuf->type) {
+ case ET_OpCmd:
+ case ET_CntlProgOpCmd:
+ case ET_PMsgCmd:
+#ifdef DUMP_HWCB_INPUT
+
+ internal_print (
+ DELAYED_WRITE,
+ HWC_RW_PRINT_HEADER
+ "event buffer "
+ "at 0x%x up to 0x%x, length: %d\n",
+ (unsigned long) evbuf,
+ (unsigned long) (evbuf_end - 1),
+ evbuf->length);
+ dump_storage_area ((void *) evbuf, evbuf->length);
+#endif
+ retval += eval_evbuf (evbuf_data, evbuf_end);
+ break;
+ case ET_StateChange:
+
+ retval = -ENOSYS;
+ break;
+ default:
+ printk (
+ KERN_WARNING
+ HWC_RW_PRINT_HEADER
+ "unconditional read: "
+ "unknown event buffer found, "
+ "type 0x%x",
+ evbuf->type);
+ retval = -ENOSYS;
+ }
+ evbuf = (evbuf_t *) evbuf_end;
+ }
+ return retval;
+}
+
+static int
+unconditional_read_1 (void)
+{
+ unsigned short int condition_code;
+ read_hwcb_t *hwcb = (read_hwcb_t *) hwc_data.page;
+ int retval;
+
+ if ((!hwc_data.read_prio) && (!hwc_data.read_nonprio))
+ return -EOPNOTSUPP;
+
+ if (hwc_data.current_servc)
+ return -EBUSY;
+
+ memset (hwcb, 0x00, PAGE_SIZE);
+ memcpy (hwcb, &read_hwcb_template, sizeof (read_hwcb_t));
+
+ condition_code = service_call (HWC_CMDW_READDATA, hwc_data.page);
+
+#ifdef DUMP_HWC_READ_ERROR
+ if (condition_code == HWC_NOT_OPERATIONAL)
+ __asm__ ("LHI 1,0xe40\n\t"
+ "L 2,0(0,%0)\n\t"
+ "LRA 3,0(0,%1)\n\t"
+ "J .+0 \n\t"
+ :
+ : "a" (&condition_code), "a" (hwc_data.page)
+ : "1", "2", "3");
+#endif
+
+ switch (condition_code) {
+ case HWC_COMMAND_INITIATED:
+ hwc_data.current_servc = HWC_CMDW_READDATA;
+ hwc_data.current_hwcb = hwc_data.page;
+ retval = condition_code;
+ break;
+ case HWC_BUSY:
+ retval = -EBUSY;
+ break;
+ default:
+ retval = -EIO;
+ }
+
+ return retval;
+}
+
+static int
+unconditional_read_2 (void)
+{
+ read_hwcb_t *hwcb = (read_hwcb_t *) hwc_data.page;
+
+#ifdef DUMP_HWC_READ_ERROR
+ if ((hwcb->response_code != 0x0020) &&
+ (hwcb->response_code != 0x0220) &&
+ (hwcb->response_code != 0x60F0) &&
+ (hwcb->response_code != 0x62F0))
+ __asm__ ("LHI 1,0xe41\n\t"
+ "LRA 2,0(0,%0)\n\t"
+ "L 3,0(0,%1)\n\t"
+ "J .+0\n\t"
+ :
+ : "a" (hwc_data.page), "a" (&(hwcb->response_code))
+ : "1", "2", "3");
+#endif
+
+ hwc_data.current_servc = 0;
+ hwc_data.current_hwcb = NULL;
+
+ switch (hwcb->response_code) {
+
+ case 0x0020:
+ case 0x0220:
+ return process_evbufs (
+ (void *) (((unsigned long) hwcb) + sizeof (read_hwcb_t)),
+ (void *) (((unsigned long) hwcb) + hwcb->length));
+
+ case 0x60F0:
+ case 0x62F0:
+ return 0;
+
+ case 0x0100:
+ internal_print (
+ IMMEDIATE_WRITE,
+ HWC_RW_PRINT_HEADER
+ "unconditional read: HWCB boundary violation - this "
+ "must not occur in a correct driver, please contact "
+ "author\n");
+ return -EIO;
+
+ case 0x0300:
+ internal_print (
+ IMMEDIATE_WRITE,
+ HWC_RW_PRINT_HEADER
+ "unconditional read: "
+ "insufficient HWCB length - this must not occur in a "
+ "correct driver, please contact author\n");
+ return -EIO;
+
+ case 0x01F0:
+ internal_print (
+ IMMEDIATE_WRITE,
+ HWC_RW_PRINT_HEADER
+ "unconditional read: "
+ "invalid command - this must not occur in a correct "
+ "driver, please contact author\n");
+ return -EIO;
+
+ case 0x40F0:
+ internal_print (
+ IMMEDIATE_WRITE,
+ HWC_RW_PRINT_HEADER
+ "unconditional read: invalid function code - this "
+ "must not occur in a correct driver, please contact "
+ "author\n");
+ return -EIO;
+
+ case 0x70F0:
+ internal_print (
+ IMMEDIATE_WRITE,
+ HWC_RW_PRINT_HEADER
+ "unconditional read: invalid selection mask - this "
+ "must not occur in a correct driver, please contact "
+ "author\n");
+ return -EIO;
+
+ case 0x0040:
+ internal_print (
+ IMMEDIATE_WRITE,
+ HWC_RW_PRINT_HEADER
+ "unconditional read: HWC equipment check - don't "
+ "know how to handle this case\n");
+ return -EIO;
+
+ default:
+ internal_print (
+ IMMEDIATE_WRITE,
+ HWC_RW_PRINT_HEADER
+ "unconditional read: invalid response code %x - this "
+ "must not occur in a correct driver, please contact "
+ "author\n",
+ hwcb->response_code);
+ return -EIO;
+ }
+}
+
+static int
+write_event_mask_1 (void)
+{
+ unsigned int condition_code;
+ int retval;
+
+ memcpy (hwc_data.page, &init_hwcb_template, sizeof (init_hwcb_t));
+
+ condition_code = service_call (HWC_CMDW_WRITEMASK, hwc_data.page);
+
+#ifdef DUMP_HWC_INIT_ERROR
+
+ if (condition_code != HWC_COMMAND_INITIATED)
+ __asm__ ("LHI 1,0xe10\n\t"
+ "L 2,0(0,%0)\n\t"
+ "LRA 3,0(0,%1)\n\t"
+ "J .+0\n\t"
+ :
+ : "a" (&condition_code), "a" (hwc_data.page)
+ : "1", "2", "3");
+#endif
+
+ switch (condition_code) {
+ case HWC_COMMAND_INITIATED:
+ hwc_data.current_servc = HWC_CMDW_WRITEMASK;
+ hwc_data.current_hwcb = hwc_data.page;
+ retval = condition_code;
+ break;
+ case HWC_BUSY:
+ retval = -EBUSY;
+ break;
+ default:
+ retval = -EIO;
+ }
+
+ return retval;
+}
+
+static int
+write_event_mask_2 (void)
+{
+ init_hwcb_t *hwcb = (init_hwcb_t *) hwc_data.page;
+ int retval = 0;
+
+ if (hwcb->hwc_receive_mask & ET_Msg_Mask)
+ hwc_data.write_nonprio = 1;
+
+ if (hwcb->hwc_receive_mask & ET_PMsgCmd_Mask)
+ hwc_data.write_prio = 1;
+
+ if (hwcb->hwc_send_mask & ET_OpCmd_Mask)
+ hwc_data.read_nonprio = 1;
+
+ if (hwcb->hwc_send_mask & ET_PMsgCmd_Mask)
+ hwc_data.read_nonprio = 1;
+
+ if ((hwcb->response_code != 0x0020) ||
+ (!hwc_data.write_nonprio) ||
+ ((!hwc_data.read_nonprio) && (!hwc_data.read_prio)))
+#ifdef DUMP_HWC_INIT_ERROR
+ __asm__ ("LHI 1,0xe11\n\t"
+ "LRA 2,0(0,%0)\n\t"
+ "L 3,0(0,%1)\n\t"
+ "J .+0\n\t"
+ :
+ : "a" (hwcb), "a" (&(hwcb->response_code))
+ : "1", "2", "3");
+#else
+ retval = -EIO
+#endif
+
+ hwc_data.current_servc = 0;
+ hwc_data.current_hwcb = NULL;
+
+ return retval;
+}
+
+static int
+set_hwc_ioctls (hwc_ioctls_t * ioctls, char correct)
+{
+ int retval = 0;
+ hwc_ioctls_t tmp;
+
+ if (ioctls->width_htab > MAX_MESSAGE_SIZE) {
+ if (correct)
+ tmp.width_htab = MAX_MESSAGE_SIZE;
+ else
+ retval = -EINVAL;
+ } else
+ tmp.width_htab = ioctls->width_htab;
+
+ tmp.echo = ioctls->echo;
+
+ if (ioctls->columns > MAX_MESSAGE_SIZE) {
+ if (correct)
+ tmp.columns = MAX_MESSAGE_SIZE;
+ else
+ retval = -EINVAL;
+ } else
+ tmp.columns = ioctls->columns;
+
+ switch (ioctls->code) {
+ case CODE_EBCDIC:
+ case CODE_ASCII:
+ tmp.code = ioctls->code;
+ break;
+ default:
+ if (correct)
+ tmp.code = CODE_ASCII;
+ else
+ retval = -EINVAL;
+ }
+
+ tmp.final_nl = ioctls->final_nl;
+
+ if (ioctls->max_hwcb < 2) {
+ if (correct)
+ tmp.max_hwcb = 2;
+ else
+ retval = -EINVAL;
+ } else
+ tmp.max_hwcb = ioctls->max_hwcb;
+
+ tmp.tolower = ioctls->tolower;
+
+ if (ioctls->kmem_hwcb > ioctls->max_hwcb) {
+ if (correct)
+ tmp.kmem_hwcb = ioctls->max_hwcb;
+ else
+ retval = -EINVAL;
+ } else
+ tmp.kmem_hwcb = ioctls->kmem_hwcb;
+
+ if (ioctls->kmem_hwcb > MAX_KMEM_PAGES) {
+ if (correct)
+ ioctls->kmem_hwcb = MAX_KMEM_PAGES;
+ else
+ retval = -EINVAL;
+ }
+ if (ioctls->kmem_hwcb < 2) {
+ if (correct)
+ ioctls->kmem_hwcb = 2;
+ else
+ retval = -EINVAL;
+ }
+ tmp.delim = ioctls->delim;
+
+ if (!(retval < 0))
+ hwc_data.ioctls = tmp;
+
+ return retval;
+}
+
+int
+hwc_init (void)
+{
+ int retval;
+#ifdef BUFFER_STRESS_TEST
+
+ init_hwcb_t *hwcb;
+ int i;
+
+#endif
+
+#ifdef CONFIG_3215
+ if (MACHINE_IS_VM)
+ return 0;
+#endif
+
+ spin_lock_init (&hwc_data.lock);
+
+ retval = write_event_mask_1 ();
+ if (retval < 0)
+ return retval;
+
+#ifdef USE_VM_DETECTION
+
+ if (MACHINE_IS_VM) {
+
+ if (hwc_data.init_ioctls.columns > 76)
+ hwc_data.init_ioctls.columns = 76;
+ hwc_data.init_ioctls.tolower = 1;
+ if (!hwc_data.init_ioctls.delim)
+ hwc_data.init_ioctls.delim = DEFAULT_CASE_DELIMITER;
+ } else {
+ hwc_data.init_ioctls.tolower = 0;
+ hwc_data.init_ioctls.delim = 0;
+ }
+#endif
+ retval = set_hwc_ioctls (&hwc_data.init_ioctls, 1);
+
+ hwc_data.kmem_start = (unsigned long)
+ alloc_bootmem_pages (hwc_data.ioctls.kmem_hwcb * PAGE_SIZE);
+ hwc_data.kmem_end = hwc_data.kmem_start +
+ hwc_data.ioctls.kmem_hwcb * PAGE_SIZE - 1;
+
+ ctl_set_bit (0, 9);
+
+#ifdef BUFFER_STRESS_TEST
+
+ internal_print (
+ DELAYED_WRITE,
+ HWC_RW_PRINT_HEADER
+ "use %i bytes for buffering.\n",
+ hwc_data.ioctls.kmem_hwcb * PAGE_SIZE);
+ for (i = 0; i < 500; i++) {
+ hwcb = (init_hwcb_t *) BUF_HWCB;
+ internal_print (
+ DELAYED_WRITE,
+ HWC_RW_PRINT_HEADER
+ "This is stress test message #%i, free: %i bytes\n",
+ i,
+ MAX_HWCB_ROOM - (hwcb->length + sizeof (mto_t)));
+ }
+
+#endif
+
+ return retval;
+}
+
+void
+do_hwc_interrupt (void)
+{
+
+ spin_lock (&hwc_data.lock);
+
+ if (!hwc_data.current_servc) {
+
+ unconditional_read_1 ();
+
+ } else {
+
+ switch (hwc_data.current_servc) {
+
+ case HWC_CMDW_WRITEMASK:
+
+ write_event_mask_2 ();
+ break;
+
+ case HWC_CMDW_WRITEDATA:
+
+ write_event_data_2 ();
+ break;
+
+ case HWC_CMDW_READDATA:
+
+ unconditional_read_2 ();
+ break;
+ }
+
+ write_event_data_1 ();
+ }
+
+ wake_up_hwc_tty ();
+
+ spin_unlock (&hwc_data.lock);
+}
+
+int
+hwc_ioctl (unsigned int cmd, unsigned long arg)
+{
+ hwc_ioctls_t tmp = hwc_data.ioctls;
+ int retval = 0;
+ unsigned long flags;
+ unsigned int obuf;
+
+ spin_lock_irqsave (&hwc_data.lock, flags);
+
+ switch (cmd) {
+
+ case TIOCHWCSHTAB:
+ if (get_user (tmp.width_htab, (ioctl_htab_t *) arg))
+ goto fault;
+ break;
+
+ case TIOCHWCSECHO:
+ if (get_user (tmp.echo, (ioctl_echo_t *) arg))
+ goto fault;
+ break;
+
+ case TIOCHWCSCOLS:
+ if (get_user (tmp.columns, (ioctl_cols_t *) arg))
+ goto fault;
+ break;
+
+ case TIOCHWCSCODE:
+ if (get_user (tmp.code, (ioctl_code_t *) arg))
+ goto fault;
+
+ break;
+
+ case TIOCHWCSNL:
+ if (get_user (tmp.final_nl, (ioctl_nl_t *) arg))
+ goto fault;
+ break;
+
+ case TIOCHWCSOBUF:
+ if (get_user (obuf, (unsigned int *) arg))
+ goto fault;
+ if (obuf & 0xFFF)
+ tmp.max_hwcb = (((obuf | 0xFFF) + 1) >> 12);
+ else
+ tmp.max_hwcb = (obuf >> 12);
+ break;
+
+ case TIOCHWCSCASE:
+ if (get_user (tmp.tolower, (ioctl_case_t *) arg))
+ goto fault;
+ break;
+
+ case TIOCHWCSDELIM:
+ if (get_user (tmp.delim, (ioctl_delim_t *) arg))
+ goto fault;
+ break;
+
+ case TIOCHWCSINIT:
+ retval = set_hwc_ioctls (&hwc_data.init_ioctls, 1);
+ break;
+
+ case TIOCHWCGHTAB:
+ if (put_user (tmp.width_htab, (ioctl_htab_t *) arg))
+ goto fault;
+ break;
+
+ case TIOCHWCGECHO:
+ if (put_user (tmp.echo, (ioctl_echo_t *) arg))
+ goto fault;
+ break;
+
+ case TIOCHWCGCOLS:
+ if (put_user (tmp.columns, (ioctl_cols_t *) arg))
+ goto fault;
+ break;
+
+ case TIOCHWCGCODE:
+ if (put_user (tmp.code, (ioctl_code_t *) arg))
+ goto fault;
+
+ break;
+
+ case TIOCHWCGNL:
+ if (put_user (tmp.final_nl, (ioctl_nl_t *) arg))
+ goto fault;
+ break;
+
+ case TIOCHWCGOBUF:
+ if (put_user (tmp.max_hwcb, (ioctl_obuf_t *) arg))
+ goto fault;
+ break;
+
+ case TIOCHWCGKBUF:
+ if (put_user (tmp.kmem_hwcb, (ioctl_obuf_t *) arg))
+ goto fault;
+ break;
+
+ case TIOCHWCGCASE:
+ if (put_user (tmp.tolower, (ioctl_case_t *) arg))
+ goto fault;
+ break;
+
+ case TIOCHWCGDELIM:
+ if (put_user (tmp.delim, (ioctl_delim_t *) arg))
+ goto fault;
+ break;
+#if 0
+
+ case TIOCHWCGINIT:
+ if (put_user (&hwc_data.init_ioctls, (hwc_ioctls_t *) arg))
+ goto fault;
+ break;
+
+ case TIOCHWCGCURR:
+ if (put_user (&hwc_data.ioctls, (hwc_ioctls_t *) arg))
+ goto fault;
+ break;
+#endif
+
+ default:
+ goto noioctlcmd;
+ }
+
+ if (_IOC_DIR (cmd) == _IOC_WRITE)
+ retval = set_hwc_ioctls (&tmp, 0);
+
+ goto out;
+
+ fault:
+ retval = -EFAULT;
+ goto out;
+ noioctlcmd:
+ retval = -ENOIOCTLCMD;
+ out:
+ spin_unlock_irqrestore (&hwc_data.lock, flags);
+ return retval;
+}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)