sclp.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 854 行 · 第 1/2 页
C
854 行
/* * drivers/s390/char/sclp.c * core function to access sclp interface * * S390 version * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation * Author(s): Martin Peschke <mpeschke@de.ibm.com> * Martin Schwidefsky <schwidefsky@de.ibm.com> */#include <linux/config.h>#include <linux/module.h>#include <linux/kmod.h>#include <linux/bootmem.h>#include <linux/err.h>#include <linux/ptrace.h>#include <linux/slab.h>#include <linux/spinlock.h>#include <linux/interrupt.h>#include <linux/timer.h>#include <linux/init.h>#include <linux/cpumask.h>#include <linux/reboot.h>#include <asm/s390_ext.h>#include <asm/processor.h>#include "sclp.h"#define SCLP_CORE_PRINT_HEADER "sclp low level driver: "/* Structure for register_early_external_interrupt. */static ext_int_info_t ext_int_info_hwc;/* spinlock to protect global variables of sclp_core */static spinlock_t sclp_lock;/* Mask of valid sclp events */static sccb_mask_t sclp_receive_mask;static sccb_mask_t sclp_send_mask;/* List of registered event types */static struct list_head sclp_reg_list;/* sccb queue */static struct list_head sclp_req_queue;/* sccb for unconditional read */static struct sclp_req sclp_read_req;static char sclp_read_sccb[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE)));/* sccb for write mask sccb */static char sclp_init_sccb[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE)));/* Timer for init mask retries. */static struct timer_list retry_timer;/* Timer for busy retries. */static struct timer_list sclp_busy_timer;static volatile unsigned long sclp_status = 0;/* some status flags */#define SCLP_INIT 0#define SCLP_RUNNING 1#define SCLP_READING 2#define SCLP_SHUTDOWN 3#define SCLP_INIT_POLL_INTERVAL 1#define SCLP_BUSY_POLL_INTERVAL 1#define SCLP_COMMAND_INITIATED 0#define SCLP_BUSY 2#define SCLP_NOT_OPERATIONAL 3/* * assembler instruction for Service Call */static int__service_call(sclp_cmdw_t command, void *sccb){ int cc; /* * Mnemonic: SERVC Rx, Ry [RRE] * * Rx: SCLP command word * Ry: address of SCCB */ __asm__ __volatile__( " .insn rre,0xb2200000,%1,%2\n" /* servc %1,%2 */ " ipm %0\n" " srl %0,28" : "=&d" (cc) : "d" (command), "a" (__pa(sccb)) : "cc", "memory" ); /* * cc == 0: Service Call succesful initiated * cc == 2: SCLP busy, new Service Call not initiated, * new SCCB unchanged * cc == 3: SCLP function not operational */ if (cc == SCLP_NOT_OPERATIONAL) return -EIO; if (cc == SCLP_BUSY) return -EBUSY; return 0;}static voidsclp_start_request(void){ struct sclp_req *req; int rc; unsigned long flags; spin_lock_irqsave(&sclp_lock, flags); /* quick exit if sclp is already in use */ if (test_bit(SCLP_RUNNING, &sclp_status)) { spin_unlock_irqrestore(&sclp_lock, flags); return; } /* Try to start requests from the request queue. */ while (!list_empty(&sclp_req_queue)) { req = list_entry(sclp_req_queue.next, struct sclp_req, list); rc = __service_call(req->command, req->sccb); if (rc == 0) { /* Sucessfully started request. */ req->status = SCLP_REQ_RUNNING; /* Request active. Set running indication. */ set_bit(SCLP_RUNNING, &sclp_status); break; } if (rc == -EBUSY) { /** * SCLP is busy but no request is running. * Try again later. */ if (!timer_pending(&sclp_busy_timer) || !mod_timer(&sclp_busy_timer, jiffies + SCLP_BUSY_POLL_INTERVAL*HZ)) { sclp_busy_timer.function = (void *) sclp_start_request; sclp_busy_timer.expires = jiffies + SCLP_BUSY_POLL_INTERVAL*HZ; add_timer(&sclp_busy_timer); } break; } /* Request failed. */ req->status = SCLP_REQ_FAILED; list_del(&req->list); if (req->callback) { spin_unlock_irqrestore(&sclp_lock, flags); req->callback(req, req->callback_data); spin_lock_irqsave(&sclp_lock, flags); } } spin_unlock_irqrestore(&sclp_lock, flags);}static intsclp_process_evbufs(struct sccb_header *sccb){ int result; unsigned long flags; struct evbuf_header *evbuf; struct list_head *l; struct sclp_register *t; spin_lock_irqsave(&sclp_lock, flags); evbuf = (struct evbuf_header *) (sccb + 1); result = 0; while ((addr_t) evbuf < (addr_t) sccb + sccb->length) { /* check registered event */ t = NULL; list_for_each(l, &sclp_reg_list) { t = list_entry(l, struct sclp_register, list); if (t->receive_mask & (1 << (32 - evbuf->type))) { if (t->receiver_fn != NULL) { spin_unlock_irqrestore(&sclp_lock, flags); t->receiver_fn(evbuf); spin_lock_irqsave(&sclp_lock, flags); } break; } else t = NULL; } /* Check for unrequested event buffer */ if (t == NULL) result = -ENOSYS; evbuf = (struct evbuf_header *) ((addr_t) evbuf + evbuf->length); } spin_unlock_irqrestore(&sclp_lock, flags); return result;}char *sclp_error_message(u16 rc){ static struct { u16 code; char *msg; } sclp_errors[] = { { 0x0000, "No response code stored (machine malfunction)" }, { 0x0020, "Normal Completion" }, { 0x0040, "SCLP equipment check" }, { 0x0100, "SCCB boundary violation" }, { 0x01f0, "Invalid command" }, { 0x0220, "Normal Completion; suppressed buffers pending" }, { 0x0300, "Insufficient SCCB length" }, { 0x0340, "Contained SCLP equipment check" }, { 0x05f0, "Target resource in improper state" }, { 0x40f0, "Invalid function code/not installed" }, { 0x60f0, "No buffers stored" }, { 0x62f0, "No buffers stored; suppressed buffers pending" }, { 0x70f0, "Invalid selection mask" }, { 0x71f0, "Event buffer exceeds available space" }, { 0x72f0, "Inconsistent lengths" }, { 0x73f0, "Event buffer syntax error" } }; int i; for (i = 0; i < sizeof(sclp_errors)/sizeof(sclp_errors[0]); i++) if (rc == sclp_errors[i].code) return sclp_errors[i].msg; return "Invalid response code";}/* * postprocessing of unconditional read service call */static voidsclp_unconditional_read_cb(struct sclp_req *read_req, void *data){ struct sccb_header *sccb; sccb = read_req->sccb; if (sccb->response_code == 0x0020 || sccb->response_code == 0x0220) { if (sclp_process_evbufs(sccb) != 0) printk(KERN_WARNING SCLP_CORE_PRINT_HEADER "unconditional read: " "unrequested event buffer received.\n"); } if (sccb->response_code != 0x0020) printk(KERN_WARNING SCLP_CORE_PRINT_HEADER "unconditional read: %s (response code=0x%x).\n", sclp_error_message(sccb->response_code), sccb->response_code); clear_bit(SCLP_READING, &sclp_status);}/* * Function to queue Read Event Data/Unconditional Read */static void__sclp_unconditional_read(void){ struct sccb_header *sccb; struct sclp_req *read_req; /* * Don't try to initiate Unconditional Read if we are not able to * receive anything */ if (sclp_receive_mask == 0) return; /* Don't try reading if a read is already outstanding */ if (test_and_set_bit(SCLP_READING, &sclp_status)) return; /* Initialize read sccb */ sccb = (struct sccb_header *) sclp_read_sccb; clear_page(sccb); sccb->length = PAGE_SIZE; sccb->function_code = 0; /* unconditional read */ sccb->control_mask[2] = 0x80; /* variable length response */ /* Initialize request structure */ read_req = &sclp_read_req; read_req->command = SCLP_CMDW_READDATA; read_req->status = SCLP_REQ_QUEUED; read_req->callback = sclp_unconditional_read_cb; read_req->sccb = sccb; /* Add read request to the head of queue */ list_add(&read_req->list, &sclp_req_queue);}/* Bit masks to interpret external interruption parameter contents. */#define EXT_INT_SCCB_MASK 0xfffffff8#define EXT_INT_STATECHANGE_PENDING 0x00000002#define EXT_INT_EVBUF_PENDING 0x00000001/* * Handler for service-signal external interruptions */static voidsclp_interrupt_handler(struct pt_regs *regs, __u16 code){ u32 ext_int_param, finished_sccb, evbuf_pending; struct list_head *l; struct sclp_req *req, *tmp; spin_lock(&sclp_lock); /* * Only process interrupt if sclp is initialized. * This avoids strange effects for a pending request * from before the last re-ipl. */ if (!test_bit(SCLP_INIT, &sclp_status)) { /* Now clear the running bit */ clear_bit(SCLP_RUNNING, &sclp_status); spin_unlock(&sclp_lock); return; } ext_int_param = S390_lowcore.ext_params; finished_sccb = ext_int_param & EXT_INT_SCCB_MASK; evbuf_pending = ext_int_param & (EXT_INT_EVBUF_PENDING | EXT_INT_STATECHANGE_PENDING); req = NULL; if (finished_sccb != 0U) { list_for_each(l, &sclp_req_queue) { tmp = list_entry(l, struct sclp_req, list); if (finished_sccb == (u32)(addr_t) tmp->sccb) { list_del(&tmp->list); req = tmp; break; } } } spin_unlock(&sclp_lock); /* Perform callback */ if (req != NULL) { req->status = SCLP_REQ_DONE; if (req->callback != NULL) req->callback(req, req->callback_data); } spin_lock(&sclp_lock); /* Head queue a read sccb if an event buffer is pending */ if (evbuf_pending) __sclp_unconditional_read(); /* Now clear the running bit if SCLP indicated a finished SCCB */ if (finished_sccb != 0U) clear_bit(SCLP_RUNNING, &sclp_status); spin_unlock(&sclp_lock); /* and start next request on the queue */ sclp_start_request();}/* * Wait synchronously for external interrupt of sclp. We may not receive * any other external interrupt, so we disable all other external interrupts * in control register 0. */voidsclp_sync_wait(void){ unsigned long psw_mask; unsigned long cr0, cr0_sync; /* Prevent BH from executing. */ local_bh_disable(); /* * save cr0 * enable service signal external interruption (cr0.22) * disable cr0.20-21, cr0.25, cr0.27, cr0.30-31 * don't touch any other bit in cr0 */ __ctl_store(cr0, 0, 0); cr0_sync = cr0; cr0_sync |= 0x00000200; cr0_sync &= 0xFFFFF3AC; __ctl_load(cr0_sync, 0, 0); /* enable external interruptions (PSW-mask.7) */ asm volatile ("STOSM 0(%1),0x01" : "=m" (psw_mask) : "a" (&psw_mask) : "memory"); /* wait until ISR signals receipt of interrupt */ while (test_bit(SCLP_RUNNING, &sclp_status)) { barrier(); cpu_relax(); } /* disable external interruptions */ asm volatile ("SSM 0(%0)" : : "a" (&psw_mask) : "memory"); /* restore cr0 */ __ctl_load(cr0, 0, 0); __local_bh_enable();}/* * Queue an SCLP request. Request will immediately be processed if queue is * empty. */voidsclp_add_request(struct sclp_req *req){ unsigned long flags; if (!test_bit(SCLP_INIT, &sclp_status)) { req->status = SCLP_REQ_FAILED; if (req->callback != NULL) req->callback(req, req->callback_data); return; } spin_lock_irqsave(&sclp_lock, flags); /* queue the request */ req->status = SCLP_REQ_QUEUED; list_add_tail(&req->list, &sclp_req_queue); spin_unlock_irqrestore(&sclp_lock, flags); /* try to start the first request on the queue */ sclp_start_request();}/* state change notification */struct sclp_statechangebuf { struct evbuf_header header; u8 validity_sclp_active_facility_mask : 1; u8 validity_sclp_receive_mask : 1; u8 validity_sclp_send_mask : 1; u8 validity_read_data_function_mask : 1; u16 _zeros : 12; u16 mask_length; u64 sclp_active_facility_mask; sccb_mask_t sclp_receive_mask;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?