1 2 /* 3 * IBM ASM Service Processor Device Driver 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 * 19 * Copyright (C) IBM Corporation, 2004 20 * 21 * Author: Max Asböck <amax@us.ibm.com> 22 * 23 */ 24 25 #include <linux/sched.h> 26 #include <linux/slab.h> 27 #include "ibmasm.h" 28 #include "lowlevel.h" 29 30 /* 31 * ASM service processor event handling routines. 32 * 33 * Events are signalled to the device drivers through interrupts. 34 * They have the format of dot commands, with the type field set to 35 * sp_event. 36 * The driver does not interpret the events, it simply stores them in a 37 * circular buffer. 38 */ 39 40 static void wake_up_event_readers(struct service_processor *sp) 41 { 42 struct event_reader *reader; 43 44 list_for_each_entry(reader, &sp->event_buffer->readers, node) 45 wake_up_interruptible(&reader->wait); 46 } 47 48 /** 49 * receive_event 50 * Called by the interrupt handler when a dot command of type sp_event is 51 * received. 52 * Store the event in the circular event buffer, wake up any sleeping 53 * event readers. 54 * There is no reader marker in the buffer, therefore readers are 55 * responsible for keeping up with the writer, or they will lose events. 56 */ 57 void ibmasm_receive_event(struct service_processor *sp, void *data, unsigned int data_size) 58 { 59 struct event_buffer *buffer = sp->event_buffer; 60 struct ibmasm_event *event; 61 unsigned long flags; 62 63 data_size = min(data_size, IBMASM_EVENT_MAX_SIZE); 64 65 spin_lock_irqsave(&sp->lock, flags); 66 /* copy the event into the next slot in the circular buffer */ 67 event = &buffer->events[buffer->next_index]; 68 memcpy_fromio(event->data, data, data_size); 69 event->data_size = data_size; 70 event->serial_number = buffer->next_serial_number; 71 72 /* advance indices in the buffer */ 73 buffer->next_index = (buffer->next_index + 1) % IBMASM_NUM_EVENTS; 74 buffer->next_serial_number++; 75 spin_unlock_irqrestore(&sp->lock, flags); 76 77 wake_up_event_readers(sp); 78 } 79 80 static inline int event_available(struct event_buffer *b, struct event_reader *r) 81 { 82 return (r->next_serial_number < b->next_serial_number); 83 } 84 85 /** 86 * get_next_event 87 * Called by event readers (initiated from user space through the file 88 * system). 89 * Sleeps until a new event is available. 90 */ 91 int ibmasm_get_next_event(struct service_processor *sp, struct event_reader *reader) 92 { 93 struct event_buffer *buffer = sp->event_buffer; 94 struct ibmasm_event *event; 95 unsigned int index; 96 unsigned long flags; 97 98 reader->cancelled = 0; 99 100 if (wait_event_interruptible(reader->wait, 101 event_available(buffer, reader) || reader->cancelled)) 102 return -ERESTARTSYS; 103 104 if (!event_available(buffer, reader)) 105 return 0; 106 107 spin_lock_irqsave(&sp->lock, flags); 108 109 index = buffer->next_index; 110 event = &buffer->events[index]; 111 while (event->serial_number < reader->next_serial_number) { 112 index = (index + 1) % IBMASM_NUM_EVENTS; 113 event = &buffer->events[index]; 114 } 115 memcpy(reader->data, event->data, event->data_size); 116 reader->data_size = event->data_size; 117 reader->next_serial_number = event->serial_number + 1; 118 119 spin_unlock_irqrestore(&sp->lock, flags); 120 121 return event->data_size; 122 } 123 124 void ibmasm_cancel_next_event(struct event_reader *reader) 125 { 126 reader->cancelled = 1; 127 wake_up_interruptible(&reader->wait); 128 } 129 130 void ibmasm_event_reader_register(struct service_processor *sp, struct event_reader *reader) 131 { 132 unsigned long flags; 133 134 reader->next_serial_number = sp->event_buffer->next_serial_number; 135 init_waitqueue_head(&reader->wait); 136 spin_lock_irqsave(&sp->lock, flags); 137 list_add(&reader->node, &sp->event_buffer->readers); 138 spin_unlock_irqrestore(&sp->lock, flags); 139 } 140 141 void ibmasm_event_reader_unregister(struct service_processor *sp, struct event_reader *reader) 142 { 143 unsigned long flags; 144 145 spin_lock_irqsave(&sp->lock, flags); 146 list_del(&reader->node); 147 spin_unlock_irqrestore(&sp->lock, flags); 148 } 149 150 int ibmasm_event_buffer_init(struct service_processor *sp) 151 { 152 struct event_buffer *buffer; 153 struct ibmasm_event *event; 154 int i; 155 156 buffer = kmalloc(sizeof(struct event_buffer), GFP_KERNEL); 157 if (!buffer) 158 return -ENOMEM; 159 160 buffer->next_index = 0; 161 buffer->next_serial_number = 1; 162 163 event = buffer->events; 164 for (i=0; i<IBMASM_NUM_EVENTS; i++, event++) 165 event->serial_number = 0; 166 167 INIT_LIST_HEAD(&buffer->readers); 168 169 sp->event_buffer = buffer; 170 171 return 0; 172 } 173 174 void ibmasm_event_buffer_exit(struct service_processor *sp) 175 { 176 kfree(sp->event_buffer); 177 } 178