1 /*
2  * event.c
3  *
4  * MontaVista IPMI code for dealing with events.
5  *
6  * Author: MontaVista Software, Inc.
7  *         Corey Minyard <minyard@mvista.com>
8  *         source@mvista.com
9  *
10  * Copyright 2002,2003 MontaVista Software Inc.
11  *
12  *  This program is free software; you can redistribute it and/or
13  *  modify it under the terms of the GNU Lesser General Public License
14  *  as published by the Free Software Foundation; either version 2 of
15  *  the License, or (at your option) any later version.
16  *
17  *
18  *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
19  *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
20  *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21  *  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
23  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
24  *  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25  *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
26  *  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
27  *  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  *
29  *  You should have received a copy of the GNU Lesser General Public
30  *  License along with this program; if not, write to the Free
31  *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
32  */
33 
34 #include <string.h>
35 #include <errno.h>
36 
37 #include <OpenIPMI/ipmiif.h>
38 
39 #include <OpenIPMI/internal/ipmi_event.h>
40 #include <OpenIPMI/internal/ipmi_int.h>
41 #include <OpenIPMI/internal/ipmi_mc.h>
42 #include <OpenIPMI/internal/ipmi_domain.h>
43 
44 struct ipmi_event_s
45 {
46     ipmi_mcid_t   mcid; /* The MC this event is stored in. */
47 
48     ipmi_lock_t   *lock;
49     unsigned int  refcount;
50     unsigned int  record_id;
51     unsigned int  type;
52     ipmi_time_t   timestamp;
53     unsigned int  data_len;
54     unsigned char old;
55     unsigned char data[0];
56 };
57 
58 ipmi_event_t *
ipmi_event_alloc(ipmi_mcid_t mcid,unsigned int record_id,unsigned int type,ipmi_time_t timestamp,unsigned char * data,unsigned int data_len)59 ipmi_event_alloc(ipmi_mcid_t   mcid,
60 		 unsigned int  record_id,
61 		 unsigned int  type,
62 		 ipmi_time_t   timestamp,
63 		 unsigned char *data,
64 		 unsigned int  data_len)
65 {
66     ipmi_event_t *rv;
67 
68     rv = ipmi_mem_alloc(sizeof(ipmi_event_t) + data_len);
69     if (!rv)
70 	return NULL;
71 
72     if (ipmi_create_global_lock(&rv->lock)) {
73 	ipmi_mem_free(rv);
74 	return NULL;
75     }
76     rv->mcid = mcid;
77     rv->record_id = record_id;
78     rv->type = type;
79     rv->timestamp = timestamp;
80     rv->data_len = data_len;
81     rv->old = 0;
82     if (data_len)
83 	memcpy(rv->data, data, data_len);
84 
85     rv->refcount = 1;
86     return rv;
87 }
88 
89 ipmi_event_t *
ipmi_event_dup(ipmi_event_t * event)90 ipmi_event_dup(ipmi_event_t *event)
91 {
92     if (!event)
93 	return NULL;
94     ipmi_lock(event->lock);
95     event->refcount++;
96     ipmi_unlock(event->lock);
97     return event;
98 }
99 
100 void
ipmi_event_free(ipmi_event_t * event)101 ipmi_event_free(ipmi_event_t *event)
102 {
103     if (!event)
104 	return;
105     ipmi_lock(event->lock);
106     event->refcount--;
107     if (event->refcount == 0) {
108 	ipmi_unlock(event->lock);
109 	ipmi_destroy_lock(event->lock);
110 	ipmi_mem_free(event);
111 	return;
112     }
113     ipmi_unlock(event->lock);
114 }
115 
116 ipmi_mcid_t
ipmi_event_get_mcid(const ipmi_event_t * event)117 ipmi_event_get_mcid(const ipmi_event_t *event)
118 {
119     return event->mcid;
120 }
121 
122 void
ipmi_event_set_mcid(ipmi_event_t * event,ipmi_mcid_t mcid)123 ipmi_event_set_mcid(ipmi_event_t *event, ipmi_mcid_t mcid)
124 {
125     event->mcid = mcid;
126 }
127 
128 unsigned int
ipmi_event_get_record_id(const ipmi_event_t * event)129 ipmi_event_get_record_id(const ipmi_event_t *event)
130 {
131     return event->record_id;
132 }
133 
134 unsigned int
ipmi_event_get_type(const ipmi_event_t * event)135 ipmi_event_get_type(const ipmi_event_t *event)
136 {
137     return event->type;
138 }
139 
140 ipmi_time_t
ipmi_event_get_timestamp(const ipmi_event_t * event)141 ipmi_event_get_timestamp(const ipmi_event_t *event)
142 {
143     return event->timestamp;
144 }
145 
146 unsigned int
ipmi_event_get_data_len(const ipmi_event_t * event)147 ipmi_event_get_data_len(const ipmi_event_t *event)
148 {
149     return event->data_len;
150 }
151 
152 unsigned int
ipmi_event_get_data(const ipmi_event_t * event,unsigned char * data,unsigned int offset,unsigned int len)153 ipmi_event_get_data(const ipmi_event_t *event, unsigned char *data,
154 		    unsigned int offset, unsigned int len)
155 {
156     if (offset > event->data_len)
157 	return 0;
158 
159     if (offset+len > event->data_len)
160 	len = event->data_len - offset;
161 
162     memcpy(data, event->data+offset, len);
163 
164     return len;
165 }
166 
167 const unsigned char *
ipmi_event_get_data_ptr(const ipmi_event_t * event)168 ipmi_event_get_data_ptr(const ipmi_event_t *event)
169 {
170     return event->data;
171 }
172 
173 int
ipmi_event_is_old(const ipmi_event_t * event)174 ipmi_event_is_old(const ipmi_event_t *event)
175 {
176     return event->old;
177 }
178 
179 void
ipmi_event_set_is_old(ipmi_event_t * event,int val)180 ipmi_event_set_is_old(ipmi_event_t *event, int val)
181 {
182     event->old = val;
183 }
184 
185 typedef struct del_event_info_s
186 {
187     ipmi_event_t   *event;
188     ipmi_domain_cb done_handler;
189     void           *cb_data;
190     int            rv;
191 } del_event_info_t;
192 
193 static void
mc_del_event_done(ipmi_mc_t * mc,int err,void * cb_data)194 mc_del_event_done(ipmi_mc_t *mc, int err, void *cb_data)
195 {
196     del_event_info_t *info = cb_data;
197 
198     if (info->done_handler) {
199 	ipmi_domain_t *domain = NULL;
200 	if (mc)
201 	    domain = ipmi_mc_get_domain(mc);
202 	info->done_handler(domain, err, info->cb_data);
203     }
204     ipmi_mem_free(info);
205 }
206 
207 static void
del_event_handler(ipmi_mc_t * mc,void * cb_data)208 del_event_handler(ipmi_mc_t *mc, void *cb_data)
209 {
210     del_event_info_t *info = cb_data;
211     del_event_info_t *ninfo;
212 
213     ninfo = ipmi_mem_alloc(sizeof(*ninfo));
214     if (!ninfo) {
215 	info->rv = ENOMEM;
216 	return;
217     }
218     *ninfo = *info;
219 
220     info->rv = ipmi_mc_del_event(mc, info->event, mc_del_event_done, ninfo);
221     if (info->rv)
222 	ipmi_mem_free(ninfo);
223 }
224 
225 int
ipmi_event_delete(ipmi_event_t * event,ipmi_domain_cb done_handler,void * cb_data)226 ipmi_event_delete(ipmi_event_t   *event,
227 		  ipmi_domain_cb done_handler,
228 		  void           *cb_data)
229 {
230     int              rv;
231     del_event_info_t info;
232     ipmi_mcid_t      mcid = ipmi_event_get_mcid(event);
233 
234     info.event = event;
235     info.done_handler = done_handler;
236     info.cb_data = cb_data;
237     info.rv = 0;
238     rv = ipmi_mc_pointer_cb(mcid, del_event_handler, &info);
239     if (!rv)
240 	rv = info.rv;
241 
242     return rv;
243 }
244 
245 ipmi_mc_t *
i_ipmi_event_get_generating_mc(ipmi_domain_t * domain,ipmi_mc_t * sel_mc,const ipmi_event_t * event)246 i_ipmi_event_get_generating_mc(ipmi_domain_t      *domain,
247 			       ipmi_mc_t          *sel_mc,
248 			       const ipmi_event_t *event)
249 {
250     ipmi_ipmb_addr_t    addr;
251     const unsigned char *data;
252     unsigned int        type = ipmi_event_get_type(event);
253 
254     if (type != 0x02)
255 	/* It's not a standard IPMI event. */
256 	return NULL;
257 
258     data = ipmi_event_get_data_ptr(event);
259     addr.addr_type = IPMI_IPMB_ADDR_TYPE;
260     /* See if the MC has an OEM handler for this. */
261     if (data[6] == 0x03) {
262 	addr.channel = 0;
263     } else {
264 	addr.channel = data[5] >> 4;
265     }
266     if ((data[4] & 0x01) == 0) {
267 	addr.slave_addr = data[4];
268     } else if (sel_mc) {
269 	/* A software ID, assume it comes from the MC where we go it. */
270 	ipmi_addr_t iaddr;
271 
272 	ipmi_mc_get_ipmi_address(sel_mc, &iaddr, NULL);
273 	addr.slave_addr = ipmi_addr_get_slave_addr(&iaddr);
274 	if (addr.slave_addr == 0)
275 	    /* A system interface, just assume it's the BMC. */
276 	    addr.slave_addr = 0x20;
277     } else {
278 	return NULL;
279     }
280     addr.lun = 0;
281 
282     return i_ipmi_find_mc_by_addr(domain, (ipmi_addr_t *) &addr, sizeof(addr));
283 }
284 
285 ipmi_sensor_id_t
ipmi_event_get_generating_sensor_id(ipmi_domain_t * domain,ipmi_mc_t * sel_mc,const ipmi_event_t * event)286 ipmi_event_get_generating_sensor_id(ipmi_domain_t       *domain,
287 			            ipmi_mc_t           *sel_mc,
288 			            const ipmi_event_t  *event)
289 {
290     ipmi_sensor_id_t    id;
291     ipmi_mc_t           *mc;
292     const unsigned char *data;
293     unsigned int        type = ipmi_event_get_type(event);
294 
295 
296     if (type != 0x02)
297 	/* It's not a standard IPMI event. */
298 	goto out_invalid;
299 
300     mc = i_ipmi_event_get_generating_mc(domain, sel_mc, event);
301     if (!mc)
302 	goto out_invalid;
303 
304     data = ipmi_event_get_data_ptr(event);
305     id.mcid = ipmi_mc_convert_to_id(mc);
306     id.lun = data[5] & 0x3;
307     id.sensor_num = data[8];
308 
309     i_ipmi_mc_put(mc);
310 
311     return id;
312 
313  out_invalid:
314     ipmi_sensor_id_set_invalid(&id);
315     return id;
316 }
317 
318 struct ipmi_event_handlers_s
319 {
320     ipmi_sensor_threshold_event_cb threshold;
321     ipmi_sensor_discrete_event_cb  discrete;
322 };
323 
324 ipmi_event_handlers_t *
ipmi_event_handlers_alloc(void)325 ipmi_event_handlers_alloc(void)
326 {
327     ipmi_event_handlers_t *rv;
328     rv = ipmi_mem_alloc(sizeof(*rv));
329     if (!rv)
330 	return NULL;
331     memset(rv, 0, sizeof(*rv));
332     return rv;
333 }
334 
335 void
ipmi_event_handlers_free(ipmi_event_handlers_t * handlers)336 ipmi_event_handlers_free(ipmi_event_handlers_t *handlers)
337 {
338     ipmi_mem_free(handlers);
339 }
340 
341 void
ipmi_event_handlers_set_threshold(ipmi_event_handlers_t * handlers,ipmi_sensor_threshold_event_cb handler)342 ipmi_event_handlers_set_threshold(ipmi_event_handlers_t         *handlers,
343 				  ipmi_sensor_threshold_event_cb handler)
344 {
345     handlers->threshold = handler;
346 }
347 
348 void
ipmi_event_handlers_set_discrete(ipmi_event_handlers_t * handlers,ipmi_sensor_discrete_event_cb handler)349 ipmi_event_handlers_set_discrete(ipmi_event_handlers_t         *handlers,
350 				 ipmi_sensor_discrete_event_cb handler)
351 {
352     handlers->discrete = handler;
353 }
354 
355 typedef struct event_call_handlers_s
356 {
357     ipmi_domain_t         *domain;
358     ipmi_event_handlers_t *handlers;
359     ipmi_event_t          *event;
360     int                   rv;
361     void                  *cb_data;
362 } event_call_handlers_t;
363 
364 static void
sensor_event_call(ipmi_sensor_t * sensor,void * cb_data)365 sensor_event_call(ipmi_sensor_t *sensor, void *cb_data)
366 {
367     event_call_handlers_t *info = cb_data;
368     int                   rv;
369 
370     if (ipmi_sensor_get_event_reading_type(sensor)
371 	== IPMI_EVENT_READING_TYPE_THRESHOLD)
372     {
373 	enum ipmi_event_dir_e       dir;
374 	enum ipmi_thresh_e          threshold;
375 	enum ipmi_event_value_dir_e high_low;
376 	enum ipmi_value_present_e   value_present;
377 	unsigned int                raw_value;
378 	double                      value;
379 	const unsigned char         *data;
380 
381 	data = ipmi_event_get_data_ptr(info->event);
382 	dir = data[9] >> 7;
383 	threshold = (data[10] >> 1) & 0x07;
384 	high_low = data[10] & 1;
385 	raw_value = data[11];
386 	value = 0.0;
387 
388 	if ((data[10] >> 6) == 1) {
389 	    rv = ipmi_sensor_convert_from_raw(sensor, raw_value, &value);
390 	    if (rv)
391 		value_present = IPMI_RAW_VALUE_PRESENT;
392 	    else
393 		value_present = IPMI_BOTH_VALUES_PRESENT;
394 	} else {
395 	    value_present = IPMI_NO_VALUES_PRESENT;
396 	}
397 	if (info->handlers->threshold)
398 	    info->handlers->threshold(sensor, dir,
399 				      threshold,
400 				      high_low,
401 				      value_present,
402 				      raw_value, value,
403 				      info->cb_data,
404 				      info->event);
405 
406 	else
407 	    info->rv = EAGAIN;
408     } else {
409 	enum ipmi_event_dir_e dir;
410 	int                   offset;
411 	int                   severity = 0;
412 	int                   prev_severity = 0;
413 	const unsigned char   *data;
414 
415 	data = ipmi_event_get_data_ptr(info->event);
416 	dir = data[9] >> 7;
417 	offset = data[10] & 0x0f;
418 	if ((data[10] >> 6) == 2) {
419 	    severity = data[11] >> 4;
420 	    prev_severity = data[11] & 0xf;
421 	    if (severity == 0xf)
422 		severity = -1;
423 	    if (prev_severity == 0xf)
424 		prev_severity = -1;
425 	}
426 
427 	if (info->handlers->discrete)
428 	    info->handlers->discrete(sensor, dir, offset,
429 				     severity,
430 				     prev_severity,
431 				     info->cb_data,
432 				     info->event);
433 	else
434 	    info->rv = EAGAIN;
435     }
436 }
437 
438 static void
sel_mc_handler(ipmi_mc_t * mc,void * cb_data)439 sel_mc_handler(ipmi_mc_t *mc, void *cb_data)
440 {
441     ipmi_sensor_id_t      sensor_id;
442     event_call_handlers_t *info = cb_data;
443     int                   rv;
444 
445     sensor_id = ipmi_event_get_generating_sensor_id(info->domain, mc,
446 						    info->event);
447     rv = ipmi_sensor_pointer_cb(sensor_id, sensor_event_call, info);
448     if (rv)
449 	info->rv = rv;
450 }
451 
452 int
ipmi_event_call_handler(ipmi_domain_t * domain,ipmi_event_handlers_t * handlers,ipmi_event_t * event,void * cb_data)453 ipmi_event_call_handler(ipmi_domain_t         *domain,
454 			ipmi_event_handlers_t *handlers,
455 			ipmi_event_t          *event,
456 			void                  *cb_data)
457 {
458 
459 
460     ipmi_sensor_id_t      sensor_id;
461     event_call_handlers_t info;
462     int                   rv = 0;
463     ipmi_mcid_t           mc_id;
464 
465     info.domain = domain;
466     info.handlers = handlers;
467     info.event = event;
468     info.rv = 0;
469     info.cb_data = cb_data;
470 
471     /* We try first to get the MC the event is stored in.  If that
472        doesn't work, then just attempt to do the sensor without an MC. */
473     mc_id = ipmi_event_get_mcid(event);
474     if (ipmi_mc_pointer_cb(mc_id, sel_mc_handler, &info) != 0) {
475 	sensor_id = ipmi_event_get_generating_sensor_id(domain, NULL, event);
476 	rv = ipmi_sensor_pointer_cb(sensor_id, sensor_event_call, &info);
477     }
478     if (!rv)
479 	rv = info.rv;
480     return rv;
481 }
482