Viewing file: hwc_rw.c (46.79 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
/* * 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 <mpeschke@de.ibm.com> * * * * * * */
#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 <linux/module.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> #include <asm/s390_ext.h> #include <asm/irq.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 '%'
#undef DUMP_HWC_INIT_ERROR
#undef DUMP_HWC_WRITE_ERROR
#undef DUMP_HWC_WRITE_LIST_ERROR
#undef 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 unsigned long kmem_pages_t;
#define MAX_KMEM_PAGES (sizeof(kmem_pages_t) << 3)
#define HWC_WTIMER_RUNS 1 #define HWC_FLUSH 2 #define HWC_INIT 4 #define HWC_BROKEN 8 #define HWC_INTERRUPT 16 #define HWC_PTIMER_RUNS 32
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 read_statechange:1;
unsigned char flags;
hwc_high_level_calls_t *calls;
hwc_request_t *request;
spinlock_t lock;
struct timer_list write_timer;
struct timer_list poll_timer; } hwc_data = { { }, { 8, 0, 80, 1, MAX_KMEM_PAGES, 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, 0, NULL, NULL
};
static unsigned long cr0 __attribute__ ((aligned (8))); static unsigned long cr0_save __attribute__ ((aligned (8))); static unsigned char psw_mask __attribute__ ((aligned (8)));
#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 hwc_ip_buf[512];
static asmlinkage int internal_print (char write_time, char *fmt,...) { va_list args; int i;
va_start (args, fmt); i = vsprintf (hwc_ip_buf, fmt, args); va_end (args); return do_hwc_write (0, hwc_ip_buf, i, write_time); }
int hwc_printk (const char *fmt,...) { va_list args; int i; unsigned long flags; int retval;
spin_lock_irqsave (&hwc_data.lock, flags);
i = vsprintf (hwc_ip_buf, fmt, args); va_end (args); retval = do_hwc_write (0, hwc_ip_buf, i, 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) \n\t" "LRA 2, 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 long hwc_ext_int_param (void) { u32 param;
__asm__ __volatile__ ("L %0,128\n\t" :"=r" (param));
return (unsigned long) 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));
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) \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%lx. 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 long) 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)\n\t" "LRA 3,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 | GFP_DMA);
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);
static void do_poll_hwc (unsigned long data) { unsigned long flags;
spin_lock_irqsave (&hwc_data.lock, flags);
write_event_data_1 ();
spin_unlock_irqrestore (&hwc_data.lock, flags); }
void start_poll_hwc (void) { init_timer (&hwc_data.poll_timer); hwc_data.poll_timer.function = do_poll_hwc; hwc_data.poll_timer.data = (unsigned long) NULL; hwc_data.poll_timer.expires = jiffies + 2 * HZ; add_timer (&hwc_data.poll_timer); hwc_data.flags |= HWC_PTIMER_RUNS; }
static int write_event_data_1 (void) { unsigned short int condition_code; int retval; write_hwcb_t *hwcb = (write_hwcb_t *) OUT_HWCB;
if ((!hwc_data.write_prio) && (!hwc_data.write_nonprio) && hwc_data.read_statechange) return -EOPNOTSUPP;
if (hwc_data.current_servc) return -EBUSY;
retval = sane_write_hwcb (); if (retval < 0) return -EIO;
if (!OUT_HWCB_MTO) return -ENODATA;
if (!hwc_data.write_nonprio && hwc_data.write_prio) hwcb->msgbuf.type = ET_PMsgCmd; else hwcb->msgbuf.type = ET_Msg;
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)\n\t" "LRA 3,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; case HWC_NOT_OPERATIONAL: start_poll_hwc (); 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 &= ~HWC_FLUSH; }
static int write_event_data_2 (u32 ext_int_param) { write_hwcb_t *hwcb; int retval = 0;
#ifdef DUMP_HWC_WRITE_ERROR if ((ext_int_param & HWC_EXT_INT_PARAM_ADDR) != (unsigned long) hwc_data.current_hwcb) { internal_print ( DELAYED_WRITE, HWC_RW_PRINT_HEADER "write_event_data_2 : " "HWCB address does not fit " "(expected: 0x%lx, got: 0x%lx).\n", (unsigned long) hwc_data.current_hwcb, ext_int_param); return -EINVAL; } #endif
hwcb = (write_hwcb_t *) OUT_HWCB;
#ifdef DUMP_HWC_WRITE_LIST_ERROR if (((unsigned char *) hwcb) != hwc_data.current_hwcb) { __asm__ ("LHI 1,0xe22\n\t" "LRA 2,0(%0)\n\t" "LRA 3,0(%1)\n\t" "LRA 4,0(%2)\n\t" "LRA 5,0(%3)\n\t" "J .+0 \n\t" : : "a" (OUT_HWCB), "a" (hwc_data.current_hwcb), "a" (BUF_HWCB), "a" (hwcb) : "1", "2", "3", "4", "5"); } #endif
#ifdef DUMP_HWC_WRITE_ERROR if (hwcb->response_code != 0x0020) { __asm__ ("LHI 1,0xe21\n\t" "LRA 2,0(%0)\n\t" "LRA 3,0(%1)\n\t" "LRA 4,0(%2)\n\t" "LH 5,0(%3)\n\t" "SRL 5,8\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
switch (hwcb->response_code) { case 0x0020:
retval = OUT_HWCB_CHAR; release_write_hwcb (); break; case 0x0040: case 0x0340: case 0x40F0: if (!hwc_data.read_statechange) { hwcb->response_code = 0; start_poll_hwc (); } retval = -EIO; break; default: internal_print ( DELAYED_WRITE, HWC_RW_PRINT_HEADER "write_event_data_2 : " "failed operation " "(response code: 0x%x " "HWCB address: 0x%x).\n", hwcb->response_code, hwcb); retval = -EIO; }
if (retval == -EIO) {
hwcb->control_mask[0] = 0; hwcb->control_mask[1] = 0; hwcb->control_mask[2] = 0; hwcb->response_code = 0; } hwc_data.current_servc = 0; hwc_data.current_hwcb = NULL;
if (hwc_data.flags & HWC_FLUSH) 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)\n\t" "L 3,0(%1)\n\t" "LRA 4,0(%2)\n\t" "LRA 5,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_WTIMER_RUNS)) { del_timer (&hwc_data.write_timer); hwc_data.flags &= ~HWC_WTIMER_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 write_time) { unsigned int i_msg = 0; unsigned short int spaces = 0; unsigned int processed_characters = 0; unsigned char 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 (ch, msg + i_msg); else ch = msg[i_msg];
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] = HWC_ASCEBC (' '); 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] = HWC_ASCEBC (' '); 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++] = HWC_ASCEBC (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_WTIMER_RUNS) {
mod_timer (&hwc_data.write_timer, 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_WTIMER_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, (unsigned char *) msg, count, 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 |= HWC_FLUSH; } 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);
HWC_EBCASC_STR (start, count);
if (hwc_data.ioctls.echo) do_hwc_write (0, start, count, IMMEDIATE_WRITE);
if (hwc_data.calls != NULL) if (hwc_data.calls->move_input != NULL) (hwc_data.calls->move_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; }
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 inline int eval_hwc_receive_mask (_hwcb_mask_t mask) {
hwc_data.write_nonprio = ((mask & ET_Msg_Mask) == ET_Msg_Mask);
hwc_data.write_prio = ((mask & ET_PMsgCmd_Mask) == ET_PMsgCmd_Mask);
if (hwc_data.write_prio || hwc_data.write_nonprio) { internal_print ( DELAYED_WRITE, HWC_RW_PRINT_HEADER "can write messages\n"); return 0; } else { internal_print ( DELAYED_WRITE, HWC_RW_PRINT_HEADER "can not write messages\n"); return -1; } }
static inline int eval_hwc_send_mask (_hwcb_mask_t mask) {
hwc_data.read_statechange = ((mask & ET_StateChange_Mask) == ET_StateChange_Mask); if (hwc_data.read_statechange) internal_print ( DELAYED_WRITE, HWC_RW_PRINT_HEADER "can read state change notifications\n"); else internal_print ( DELAYED_WRITE, HWC_RW_PRINT_HEADER "can not read state change notifications\n");
hwc_data.read_nonprio = ((mask & ET_OpCmd_Mask) == ET_OpCmd_Mask); if (hwc_data.read_nonprio) internal_print ( DELAYED_WRITE, HWC_RW_PRINT_HEADER "can read commands\n");
hwc_data.read_prio = ((mask & ET_PMsgCmd_Mask) == ET_PMsgCmd_Mask); if (hwc_data.read_prio) internal_print ( DELAYED_WRITE, HWC_RW_PRINT_HEADER "can read priority commands\n");
if (hwc_data.read_prio || hwc_data.read_nonprio) { return 0; } else { internal_print ( DELAYED_WRITE, HWC_RW_PRINT_HEADER "can not read commands from operator\n"); return -1; } }
static int eval_statechangebuf (statechangebuf_t * scbuf) { int retval = 0;
internal_print ( DELAYED_WRITE, HWC_RW_PRINT_HEADER "HWC state change detected\n");
if (scbuf->validity_hwc_active_facility_mask) {
} if (scbuf->validity_hwc_receive_mask) {
if (scbuf->mask_length != 4) { #ifdef DUMP_HWC_INIT_ERROR __asm__ ("LHI 1,0xe50\n\t" "LRA 2,0(%0)\n\t" "J .+0 \n\t" : : "a" (scbuf) : "1", "2"); #endif } else {
retval += eval_hwc_receive_mask (scbuf->hwc_receive_mask); } } if (scbuf->validity_hwc_send_mask) {
if (scbuf->mask_length != 4) { #ifdef DUMP_HWC_INIT_ERROR __asm__ ("LHI 1,0xe51\n\t" "LRA 2,0(%0)\n\t" "J .+0 \n\t" : : "a" (scbuf) : "1", "2"); #endif } else {
retval += eval_hwc_send_mask (scbuf->hwc_send_mask); } } if (scbuf->validity_read_data_function_mask) {
} 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 += eval_statechangebuf ((statechangebuf_t *) evbuf); break; default: internal_print ( DELAYED_WRITE, 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 0
if ((!hwc_data.read_prio) && (!hwc_data.read_nonprio)) return -EOPNOTSUPP;
if (hwc_data.current_servc) return -EBUSY; #endif
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)\n\t" "LRA 3,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 (u32 ext_int_param) { 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)\n\t" "L 3,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: internal_print ( IMMEDIATE_WRITE, HWC_RW_PRINT_HEADER "unconditional read: " "got interrupt and tried to read input, " "but nothing found (response code=0x%x).\n", hwcb->response_code); 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\n"); return -EIO;
case 0x70F0: internal_print ( IMMEDIATE_WRITE, HWC_RW_PRINT_HEADER "unconditional read: invalid selection mask\n"); return -EIO;
case 0x0040: internal_print ( IMMEDIATE_WRITE, HWC_RW_PRINT_HEADER "unconditional read: HWC equipment check\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;
condition_code = service_call (HWC_CMDW_WRITEMASK, hwc_data.page);
#ifdef DUMP_HWC_INIT_ERROR
if (condition_code == HWC_NOT_OPERATIONAL) __asm__ ("LHI 1,0xe10\n\t" "L 2,0(%0)\n\t" "LRA 3,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 (u32 ext_int_param) { init_hwcb_t *hwcb = (init_hwcb_t *) hwc_data.page; int retval = 0;
if (hwcb->response_code != 0x0020) { #ifdef DUMP_HWC_INIT_ERROR __asm__ ("LHI 1,0xe11\n\t" "LRA 2,0(%0)\n\t" "L 3,0(%1)\n\t" "J .+0\n\t" : : "a" (hwcb), "a" (&(hwcb->response_code)) : "1", "2", "3"); #else retval = -1; #endif } else { if (hwcb->mask_length != 4) { #ifdef DUMP_HWC_INIT_ERROR __asm__ ("LHI 1,0xe52\n\t" "LRA 2,0(%0)\n\t" "J .+0 \n\t" : : "a" (hwcb) : "1", "2"); #endif } else { retval += eval_hwc_receive_mask (hwcb->hwc_receive_mask); retval += eval_hwc_send_mask (hwcb->hwc_send_mask); } }
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;
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 do_hwc_init (void) { int retval;
memcpy (hwc_data.page, &init_hwcb_template, sizeof (init_hwcb_t));
do {
retval = write_event_mask_1 ();
if (retval == -EBUSY) {
hwc_data.flags |= HWC_INIT;
__ctl_store (cr0, 0, 0); cr0_save = cr0; cr0 |= 0x00000200; cr0 &= 0xFFFFF3AC; __ctl_load (cr0, 0, 0);
asm volatile ("STOSM %0,0x01" :"=m" (psw_mask)::"memory");
while (!(hwc_data.flags & HWC_INTERRUPT)) barrier ();
asm volatile ("STNSM %0,0xFE" :"=m" (psw_mask)::"memory");
__ctl_load (cr0_save, 0, 0);
hwc_data.flags &= ~HWC_INIT; } } while (retval == -EBUSY);
if (retval == -EIO) { hwc_data.flags |= HWC_BROKEN; printk (HWC_RW_PRINT_HEADER "HWC not operational\n"); } return retval; }
void hwc_interrupt_handler (struct pt_regs *regs, __u16 code);
int hwc_init (void) { int retval;
#ifdef BUFFER_STRESS_TEST
init_hwcb_t *hwcb; int i;
#endif
if (register_external_interrupt (0x2401, hwc_interrupt_handler) != 0) panic ("Couldn't request external interrupts 0x2401");
spin_lock_init (&hwc_data.lock);
#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_low_pages (hwc_data.ioctls.kmem_hwcb * PAGE_SIZE); hwc_data.kmem_end = hwc_data.kmem_start + hwc_data.ioctls.kmem_hwcb * PAGE_SIZE - 1;
retval = do_hwc_init ();
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 */ 0; }
signed int hwc_register_calls (hwc_high_level_calls_t * calls) { if (calls == NULL) return -EINVAL;
if (hwc_data.calls != NULL) return -EBUSY;
hwc_data.calls = calls; return 0; }
signed int hwc_unregister_calls (hwc_high_level_calls_t * calls) { if (hwc_data.calls == NULL) return -EINVAL;
if (calls != hwc_data.calls) return -EINVAL;
hwc_data.calls = NULL; return 0; }
int hwc_send (hwc_request_t * req) { unsigned long flags; int retval; int cc;
spin_lock_irqsave (&hwc_data.lock, flags); if (!req || !req->callback || !req->block) { retval = -EINVAL; goto unlock; } if (hwc_data.request) { retval = -ENOTSUPP; goto unlock; } cc = service_call (req->word, req->block); switch (cc) { case 0: hwc_data.request = req; hwc_data.current_servc = req->word; hwc_data.current_hwcb = req->block; retval = 0; break; case 2: retval = -EBUSY; break; default: retval = -ENOSYS;
} unlock: spin_unlock_irqrestore (&hwc_data.lock, flags); return retval; }
EXPORT_SYMBOL (hwc_send);
void do_hwc_callback (u32 ext_int_param) { if (!hwc_data.request || !hwc_data.request->callback) return; if ((ext_int_param & HWC_EXT_INT_PARAM_ADDR) != (unsigned long) hwc_data.request->block) return; hwc_data.request->callback (hwc_data.request); hwc_data.request = NULL; hwc_data.current_hwcb = NULL; hwc_data.current_servc = 0; }
void hwc_do_interrupt (u32 ext_int_param) { u32 finished_hwcb = ext_int_param & HWC_EXT_INT_PARAM_ADDR; u32 evbuf_pending = ext_int_param & HWC_EXT_INT_PARAM_PEND;
if (hwc_data.flags & HWC_PTIMER_RUNS) { del_timer (&hwc_data.poll_timer); hwc_data.flags &= ~HWC_PTIMER_RUNS; } if (finished_hwcb) {
if ((unsigned long) hwc_data.current_hwcb != finished_hwcb) { internal_print ( DELAYED_WRITE, HWC_RW_PRINT_HEADER "interrupt: mismatch: " "ext. int param. (0x%x) vs. " "current HWCB (0x%x)\n", ext_int_param, hwc_data.current_hwcb); } else { if (hwc_data.request) {
do_hwc_callback (ext_int_param); } else {
switch (hwc_data.current_servc) {
case HWC_CMDW_WRITEMASK:
write_event_mask_2 (ext_int_param); break;
case HWC_CMDW_WRITEDATA:
write_event_data_2 (ext_int_param); break;
case HWC_CMDW_READDATA:
unconditional_read_2 (ext_int_param); break; default: } } } } else {
if (hwc_data.current_hwcb) { internal_print ( DELAYED_WRITE, HWC_RW_PRINT_HEADER "interrupt: mismatch: " "ext. int. param. (0x%x) vs. " "current HWCB (0x%x)\n", ext_int_param, hwc_data.current_hwcb); } }
if (evbuf_pending) {
unconditional_read_1 (); } else {
write_event_data_1 (); }
if (!hwc_data.calls || !hwc_data.calls->wake_up) return; (hwc_data.calls->wake_up) (); }
void hwc_interrupt_handler (struct pt_regs *regs, __u16 code) { int cpu = smp_processor_id ();
u32 ext_int_param = hwc_ext_int_param ();
irq_enter (cpu, 0x2401);
if (hwc_data.flags & HWC_INIT) {
hwc_data.flags |= HWC_INTERRUPT; } else if (hwc_data.flags & HWC_BROKEN) {
if (!do_hwc_init ()) { hwc_data.flags &= ~HWC_BROKEN; internal_print (DELAYED_WRITE, HWC_RW_PRINT_HEADER "delayed HWC setup after" " temporary breakdown" " (ext. int. parameter=0x%x)\n", ext_int_param); } } else { spin_lock (&hwc_data.lock); hwc_do_interrupt (ext_int_param); spin_unlock (&hwc_data.lock); } irq_exit (cpu, 0x2401); }
void hwc_unblank (void) {
spin_lock (&hwc_data.lock); spin_unlock (&hwc_data.lock);
__ctl_store (cr0, 0, 0); cr0_save = cr0; cr0 |= 0x00000200; cr0 &= 0xFFFFF3AC; __ctl_load (cr0, 0, 0);
asm volatile ("STOSM %0,0x01":"=m" (psw_mask)::"memory");
while (ALL_HWCB_CHAR) barrier ();
asm volatile ("STNSM %0,0xFE":"=m" (psw_mask)::"memory");
__ctl_load (cr0_save, 0, 0); }
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 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 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; }
|