diff options
Diffstat (limited to 'drivers/misc/ibmasm/event.c')
-rw-r--r-- | drivers/misc/ibmasm/event.c | 169 |
1 files changed, 169 insertions, 0 deletions
diff --git a/drivers/misc/ibmasm/event.c b/drivers/misc/ibmasm/event.c new file mode 100644 index 000000000000..e100f34f1587 --- /dev/null +++ b/drivers/misc/ibmasm/event.c | |||
@@ -0,0 +1,169 @@ | |||
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 "ibmasm.h" | ||
26 | |||
27 | /* | ||
28 | * ASM service processor event handling routines. | ||
29 | * | ||
30 | * Events are signalled to the device drivers through interrupts. | ||
31 | * They have the format of dot commands, with the type field set to | ||
32 | * sp_event. | ||
33 | * The driver does not interpret the events, it simply stores them in a | ||
34 | * circular buffer. | ||
35 | */ | ||
36 | |||
37 | |||
38 | static void wake_up_event_readers(struct service_processor *sp) | ||
39 | { | ||
40 | struct event_reader *reader; | ||
41 | |||
42 | list_for_each_entry(reader, &sp->event_buffer->readers, node) | ||
43 | wake_up_interruptible(&reader->wait); | ||
44 | } | ||
45 | |||
46 | /** | ||
47 | * receive_event | ||
48 | * Called by the interrupt handler when a dot command of type sp_event is | ||
49 | * received. | ||
50 | * Store the event in the circular event buffer, wake up any sleeping | ||
51 | * event readers. | ||
52 | * There is no reader marker in the buffer, therefore readers are | ||
53 | * responsible for keeping up with the writer, or they will loose events. | ||
54 | */ | ||
55 | void ibmasm_receive_event(struct service_processor *sp, void *data, unsigned int data_size) | ||
56 | { | ||
57 | struct event_buffer *buffer = sp->event_buffer; | ||
58 | struct ibmasm_event *event; | ||
59 | unsigned long flags; | ||
60 | |||
61 | data_size = min(data_size, IBMASM_EVENT_MAX_SIZE); | ||
62 | |||
63 | spin_lock_irqsave(&sp->lock, flags); | ||
64 | /* copy the event into the next slot in the circular buffer */ | ||
65 | event = &buffer->events[buffer->next_index]; | ||
66 | memcpy(event->data, data, data_size); | ||
67 | event->data_size = data_size; | ||
68 | event->serial_number = buffer->next_serial_number; | ||
69 | |||
70 | /* advance indices in the buffer */ | ||
71 | buffer->next_index = (buffer->next_index + 1) % IBMASM_NUM_EVENTS; | ||
72 | buffer->next_serial_number++; | ||
73 | spin_unlock_irqrestore(&sp->lock, flags); | ||
74 | |||
75 | wake_up_event_readers(sp); | ||
76 | } | ||
77 | |||
78 | static inline int event_available(struct event_buffer *b, struct event_reader *r) | ||
79 | { | ||
80 | return (r->next_serial_number < b->next_serial_number); | ||
81 | } | ||
82 | |||
83 | /** | ||
84 | * get_next_event | ||
85 | * Called by event readers (initiated from user space through the file | ||
86 | * system). | ||
87 | * Sleeps until a new event is available. | ||
88 | */ | ||
89 | int ibmasm_get_next_event(struct service_processor *sp, struct event_reader *reader) | ||
90 | { | ||
91 | struct event_buffer *buffer = sp->event_buffer; | ||
92 | struct ibmasm_event *event; | ||
93 | unsigned int index; | ||
94 | unsigned long flags; | ||
95 | |||
96 | if (wait_event_interruptible(reader->wait, event_available(buffer, reader))) | ||
97 | return -ERESTARTSYS; | ||
98 | |||
99 | if (!event_available(buffer, reader)) | ||
100 | return 0; | ||
101 | |||
102 | spin_lock_irqsave(&sp->lock, flags); | ||
103 | |||
104 | index = buffer->next_index; | ||
105 | event = &buffer->events[index]; | ||
106 | while (event->serial_number < reader->next_serial_number) { | ||
107 | index = (index + 1) % IBMASM_NUM_EVENTS; | ||
108 | event = &buffer->events[index]; | ||
109 | } | ||
110 | memcpy(reader->data, event->data, event->data_size); | ||
111 | reader->data_size = event->data_size; | ||
112 | reader->next_serial_number = event->serial_number + 1; | ||
113 | |||
114 | spin_unlock_irqrestore(&sp->lock, flags); | ||
115 | |||
116 | return event->data_size; | ||
117 | } | ||
118 | |||
119 | void ibmasm_event_reader_register(struct service_processor *sp, struct event_reader *reader) | ||
120 | { | ||
121 | unsigned long flags; | ||
122 | |||
123 | reader->next_serial_number = sp->event_buffer->next_serial_number; | ||
124 | init_waitqueue_head(&reader->wait); | ||
125 | spin_lock_irqsave(&sp->lock, flags); | ||
126 | list_add(&reader->node, &sp->event_buffer->readers); | ||
127 | spin_unlock_irqrestore(&sp->lock, flags); | ||
128 | } | ||
129 | |||
130 | void ibmasm_event_reader_unregister(struct service_processor *sp, struct event_reader *reader) | ||
131 | { | ||
132 | unsigned long flags; | ||
133 | |||
134 | wake_up_interruptible(&reader->wait); | ||
135 | |||
136 | spin_lock_irqsave(&sp->lock, flags); | ||
137 | list_del(&reader->node); | ||
138 | spin_unlock_irqrestore(&sp->lock, flags); | ||
139 | } | ||
140 | |||
141 | int ibmasm_event_buffer_init(struct service_processor *sp) | ||
142 | { | ||
143 | struct event_buffer *buffer; | ||
144 | struct ibmasm_event *event; | ||
145 | int i; | ||
146 | |||
147 | buffer = kmalloc(sizeof(struct event_buffer), GFP_KERNEL); | ||
148 | if (!buffer) | ||
149 | return 1; | ||
150 | |||
151 | buffer->next_index = 0; | ||
152 | buffer->next_serial_number = 1; | ||
153 | |||
154 | event = buffer->events; | ||
155 | for (i=0; i<IBMASM_NUM_EVENTS; i++, event++) | ||
156 | event->serial_number = 0; | ||
157 | |||
158 | INIT_LIST_HEAD(&buffer->readers); | ||
159 | |||
160 | sp->event_buffer = buffer; | ||
161 | |||
162 | return 0; | ||
163 | } | ||
164 | |||
165 | void ibmasm_event_buffer_exit(struct service_processor *sp) | ||
166 | { | ||
167 | wake_up_event_readers(sp); | ||
168 | kfree(sp->event_buffer); | ||
169 | } | ||