/* * event.c * * MontaVista IPMI code for dealing with events. * * Author: MontaVista Software, Inc. * Corey Minyard * source@mvista.com * * Copyright 2002,2003 MontaVista Software Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include struct ipmi_event_s { ipmi_mcid_t mcid; /* The MC this event is stored in. */ ipmi_lock_t *lock; unsigned int refcount; unsigned int record_id; unsigned int type; ipmi_time_t timestamp; unsigned int data_len; unsigned char old; unsigned char data[0]; }; 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) { ipmi_event_t *rv; rv = ipmi_mem_alloc(sizeof(ipmi_event_t) + data_len); if (!rv) return NULL; if (ipmi_create_global_lock(&rv->lock)) { ipmi_mem_free(rv); return NULL; } rv->mcid = mcid; rv->record_id = record_id; rv->type = type; rv->timestamp = timestamp; rv->data_len = data_len; rv->old = 0; if (data_len) memcpy(rv->data, data, data_len); rv->refcount = 1; return rv; } ipmi_event_t * ipmi_event_dup(ipmi_event_t *event) { if (!event) return NULL; ipmi_lock(event->lock); event->refcount++; ipmi_unlock(event->lock); return event; } void ipmi_event_free(ipmi_event_t *event) { if (!event) return; ipmi_lock(event->lock); event->refcount--; if (event->refcount == 0) { ipmi_unlock(event->lock); ipmi_destroy_lock(event->lock); ipmi_mem_free(event); return; } ipmi_unlock(event->lock); } ipmi_mcid_t ipmi_event_get_mcid(const ipmi_event_t *event) { return event->mcid; } void ipmi_event_set_mcid(ipmi_event_t *event, ipmi_mcid_t mcid) { event->mcid = mcid; } unsigned int ipmi_event_get_record_id(const ipmi_event_t *event) { return event->record_id; } unsigned int ipmi_event_get_type(const ipmi_event_t *event) { return event->type; } ipmi_time_t ipmi_event_get_timestamp(const ipmi_event_t *event) { return event->timestamp; } unsigned int ipmi_event_get_data_len(const ipmi_event_t *event) { return event->data_len; } unsigned int ipmi_event_get_data(const ipmi_event_t *event, unsigned char *data, unsigned int offset, unsigned int len) { if (offset > event->data_len) return 0; if (offset+len > event->data_len) len = event->data_len - offset; memcpy(data, event->data+offset, len); return len; } const unsigned char * ipmi_event_get_data_ptr(const ipmi_event_t *event) { return event->data; } int ipmi_event_is_old(const ipmi_event_t *event) { return event->old; } void ipmi_event_set_is_old(ipmi_event_t *event, int val) { event->old = val; } typedef struct del_event_info_s { ipmi_event_t *event; ipmi_domain_cb done_handler; void *cb_data; int rv; } del_event_info_t; static void mc_del_event_done(ipmi_mc_t *mc, int err, void *cb_data) { del_event_info_t *info = cb_data; if (info->done_handler) { ipmi_domain_t *domain = NULL; if (mc) domain = ipmi_mc_get_domain(mc); info->done_handler(domain, err, info->cb_data); } ipmi_mem_free(info); } static void del_event_handler(ipmi_mc_t *mc, void *cb_data) { del_event_info_t *info = cb_data; del_event_info_t *ninfo; ninfo = ipmi_mem_alloc(sizeof(*ninfo)); if (!ninfo) { info->rv = ENOMEM; return; } *ninfo = *info; info->rv = ipmi_mc_del_event(mc, info->event, mc_del_event_done, ninfo); if (info->rv) ipmi_mem_free(ninfo); } int ipmi_event_delete(ipmi_event_t *event, ipmi_domain_cb done_handler, void *cb_data) { int rv; del_event_info_t info; ipmi_mcid_t mcid = ipmi_event_get_mcid(event); info.event = event; info.done_handler = done_handler; info.cb_data = cb_data; info.rv = 0; rv = ipmi_mc_pointer_cb(mcid, del_event_handler, &info); if (!rv) rv = info.rv; return rv; } ipmi_mc_t * i_ipmi_event_get_generating_mc(ipmi_domain_t *domain, ipmi_mc_t *sel_mc, const ipmi_event_t *event) { ipmi_ipmb_addr_t addr; const unsigned char *data; unsigned int type = ipmi_event_get_type(event); if (type != 0x02) /* It's not a standard IPMI event. */ return NULL; data = ipmi_event_get_data_ptr(event); addr.addr_type = IPMI_IPMB_ADDR_TYPE; /* See if the MC has an OEM handler for this. */ if (data[6] == 0x03) { addr.channel = 0; } else { addr.channel = data[5] >> 4; } if ((data[4] & 0x01) == 0) { addr.slave_addr = data[4]; } else if (sel_mc) { /* A software ID, assume it comes from the MC where we go it. */ ipmi_addr_t iaddr; ipmi_mc_get_ipmi_address(sel_mc, &iaddr, NULL); addr.slave_addr = ipmi_addr_get_slave_addr(&iaddr); if (addr.slave_addr == 0) /* A system interface, just assume it's the BMC. */ addr.slave_addr = 0x20; } else { return NULL; } addr.lun = 0; return i_ipmi_find_mc_by_addr(domain, (ipmi_addr_t *) &addr, sizeof(addr)); } ipmi_sensor_id_t ipmi_event_get_generating_sensor_id(ipmi_domain_t *domain, ipmi_mc_t *sel_mc, const ipmi_event_t *event) { ipmi_sensor_id_t id; ipmi_mc_t *mc; const unsigned char *data; unsigned int type = ipmi_event_get_type(event); if (type != 0x02) /* It's not a standard IPMI event. */ goto out_invalid; mc = i_ipmi_event_get_generating_mc(domain, sel_mc, event); if (!mc) goto out_invalid; data = ipmi_event_get_data_ptr(event); id.mcid = ipmi_mc_convert_to_id(mc); id.lun = data[5] & 0x3; id.sensor_num = data[8]; i_ipmi_mc_put(mc); return id; out_invalid: ipmi_sensor_id_set_invalid(&id); return id; } struct ipmi_event_handlers_s { ipmi_sensor_threshold_event_cb threshold; ipmi_sensor_discrete_event_cb discrete; }; ipmi_event_handlers_t * ipmi_event_handlers_alloc(void) { ipmi_event_handlers_t *rv; rv = ipmi_mem_alloc(sizeof(*rv)); if (!rv) return NULL; memset(rv, 0, sizeof(*rv)); return rv; } void ipmi_event_handlers_free(ipmi_event_handlers_t *handlers) { ipmi_mem_free(handlers); } void ipmi_event_handlers_set_threshold(ipmi_event_handlers_t *handlers, ipmi_sensor_threshold_event_cb handler) { handlers->threshold = handler; } void ipmi_event_handlers_set_discrete(ipmi_event_handlers_t *handlers, ipmi_sensor_discrete_event_cb handler) { handlers->discrete = handler; } typedef struct event_call_handlers_s { ipmi_domain_t *domain; ipmi_event_handlers_t *handlers; ipmi_event_t *event; int rv; void *cb_data; } event_call_handlers_t; static void sensor_event_call(ipmi_sensor_t *sensor, void *cb_data) { event_call_handlers_t *info = cb_data; int rv; if (ipmi_sensor_get_event_reading_type(sensor) == IPMI_EVENT_READING_TYPE_THRESHOLD) { enum ipmi_event_dir_e dir; enum ipmi_thresh_e threshold; enum ipmi_event_value_dir_e high_low; enum ipmi_value_present_e value_present; unsigned int raw_value; double value; const unsigned char *data; data = ipmi_event_get_data_ptr(info->event); dir = data[9] >> 7; threshold = (data[10] >> 1) & 0x07; high_low = data[10] & 1; raw_value = data[11]; value = 0.0; if ((data[10] >> 6) == 1) { rv = ipmi_sensor_convert_from_raw(sensor, raw_value, &value); if (rv) value_present = IPMI_RAW_VALUE_PRESENT; else value_present = IPMI_BOTH_VALUES_PRESENT; } else { value_present = IPMI_NO_VALUES_PRESENT; } if (info->handlers->threshold) info->handlers->threshold(sensor, dir, threshold, high_low, value_present, raw_value, value, info->cb_data, info->event); else info->rv = EAGAIN; } else { enum ipmi_event_dir_e dir; int offset; int severity = 0; int prev_severity = 0; const unsigned char *data; data = ipmi_event_get_data_ptr(info->event); dir = data[9] >> 7; offset = data[10] & 0x0f; if ((data[10] >> 6) == 2) { severity = data[11] >> 4; prev_severity = data[11] & 0xf; if (severity == 0xf) severity = -1; if (prev_severity == 0xf) prev_severity = -1; } if (info->handlers->discrete) info->handlers->discrete(sensor, dir, offset, severity, prev_severity, info->cb_data, info->event); else info->rv = EAGAIN; } } static void sel_mc_handler(ipmi_mc_t *mc, void *cb_data) { ipmi_sensor_id_t sensor_id; event_call_handlers_t *info = cb_data; int rv; sensor_id = ipmi_event_get_generating_sensor_id(info->domain, mc, info->event); rv = ipmi_sensor_pointer_cb(sensor_id, sensor_event_call, info); if (rv) info->rv = rv; } int ipmi_event_call_handler(ipmi_domain_t *domain, ipmi_event_handlers_t *handlers, ipmi_event_t *event, void *cb_data) { ipmi_sensor_id_t sensor_id; event_call_handlers_t info; int rv = 0; ipmi_mcid_t mc_id; info.domain = domain; info.handlers = handlers; info.event = event; info.rv = 0; info.cb_data = cb_data; /* We try first to get the MC the event is stored in. If that doesn't work, then just attempt to do the sensor without an MC. */ mc_id = ipmi_event_get_mcid(event); if (ipmi_mc_pointer_cb(mc_id, sel_mc_handler, &info) != 0) { sensor_id = ipmi_event_get_generating_sensor_id(domain, NULL, event); rv = ipmi_sensor_pointer_cb(sensor_id, sensor_event_call, &info); } if (!rv) rv = info.rv; return rv; }