/* * ipmi_entity.c * * MontaVista IPMI code for handling entities * * 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 #include #include #include #include #include #include #include #include #include #include /* These are the versions of IPMI we write to the SDR repository */ #define IPMI_MAJOR_NUM_SDR 1 #define IPMI_MINOR_NUM_SDR 5 #define ENTITY_ID_LEN 32 /* Uniquely identifies a device in the system. If all the values are zero, then it is not used (it's in the system-relative range). */ typedef struct ipmi_device_num_s { unsigned char channel; unsigned char address; } ipmi_device_num_t; typedef struct dlr_ref_s { ipmi_device_num_t device_num; uint8_t entity_id; uint8_t entity_instance; } dlr_ref_t; typedef struct dlr_info_s { enum ipmi_dlr_type_e type; entity_sdr_add_cb output_handler; ipmi_device_num_t device_num; /* Key fields. */ uint8_t access_address; /* Valid for FRU and Generic */ uint8_t fru_device_id; /* Valid for FRU */ uint8_t is_logical_fru; /* Valid for FRU */ uint8_t lun; /* Valid for FRU, MC, and Generic */ uint8_t private_bus_id; /* Valid for FRU and Generic */ uint8_t channel; /* Valid for FRU, MC, and Generic */ uint8_t slave_address; /* Valid for MC and Generic. */ /* General record fields. */ uint8_t oem; uint8_t entity_id; uint8_t entity_instance; uint8_t device_type; /* Not in MC */ uint8_t device_type_modifier; /* Not in MC */ /* Note that the id is *not* nil terminated. */ unsigned int id_len; enum ipmi_str_type_e id_type; char id[ENTITY_ID_LEN]; /* MCDLR-specfic Record fields. */ unsigned int ACPI_system_power_notify_required : 1; unsigned int ACPI_device_power_notify_required : 1; unsigned int controller_logs_init_agent_errors : 1; unsigned int log_init_agent_errors_accessing : 1; unsigned int global_init : 2; unsigned int chassis_device : 1; unsigned int bridge : 1; unsigned int IPMB_event_generator : 1; unsigned int IPMB_event_receiver : 1; unsigned int FRU_inventory_device : 1; unsigned int SEL_device : 1; unsigned int SDR_repository_device : 1; unsigned int sensor_device : 1; /* Generic Record fields. */ uint8_t address_span; /* From an EAR or DREAR */ uint8_t is_list; uint8_t linked; uint8_t is_ranges; uint8_t linked_ear_exists : 1; uint8_t presence_sensor_always_there; dlr_ref_t contained_entities[4]; } dlr_info_t; typedef struct ent_timer_info_s { ipmi_lock_t *lock; ipmi_entity_t *entity; os_hnd_timer_id_t *timer; int destroyed; int running; os_handler_t *os_hnd; } ent_timer_info_t; struct ipmi_entity_s { ipmi_domain_t *domain; ipmi_domain_id_t domain_id; long seq; dlr_ref_t key; /* Lock used for protecting misc data. */ ipmi_lock_t *elock; int usecount; /* Used to tell that the entity destroy is being reported to the user. */ int destroyed; /* Used to know if fully up has already been reported. */ int fully_up; /* Have the SDRs been fully read? We use this to know when we should report entity handling being fully done, because we don't want to report fully done until we know we have everything about the entity read in. */ int sdr_add_done; /* After the entity is added, it will not be reported immediately. Instead, it will wait until the usecount goes to zero before being reported. This marks that the add report is pending */ int add_pending; /* My domain's os handler. */ os_handler_t *os_hnd; /* Info from the DLR. */ dlr_info_t info; /* We don't install DLR info while the entity is in use, we hold it here until the usecount goes to zero. */ dlr_info_t pending_info; int pending_info_ready; /* If the entity has changed and needs to be reported when the entity is no longer in use, mark it here. */ int changed; /* Number of users of this entity (not including sensors, this is mainly for other SDRs that reference this entity). */ unsigned int ref_count; locked_list_t *child_entities; locked_list_t *parent_entities; locked_list_t *sensors; locked_list_t *controls; const char *entity_id_string; /* Function to detect presence. */ ipmi_entity_presence_detect_cb detect_presence; void *detect_presence_data; /* A standard presence sensor. This one overrides everything. */ ipmi_sensor_t *presence_sensor; ipmi_sensor_id_t presence_sensor_id; /* A discrete sensor where one of the bits is used for presence. If one of these exists, it will be used unless there is a presence sensor. */ ipmi_sensor_t *presence_bit_sensor; ipmi_sensor_id_t presence_bit_sensor_id; /* For a standard presence sensor, the offset of the present bit, the absent bit will be !presence_bit. For a presence bit sensor, the offset of the actual bit, it's always a present bit. */ int presence_bit_offset; int present; int presence_possibly_changed; unsigned int presence_event_count; /* Changed when presence events are reported. */ /* Only allow one presence check at a time. */ int in_presence_check; /* If the presence changes while the entity is in use, we store it in here and the count instead of actually changing it. Then we fix it up when the entity is set not in use. */ int curr_present; int present_change_count; /* Physical slot number info */ unsigned int slot_num; int slot_num_present; /* Hot-swap timing. */ ent_timer_info_t *hot_swap_act_info; ipmi_timeout_t hot_swap_act_timeout; ent_timer_info_t *hot_swap_deact_info; ipmi_timeout_t hot_swap_deact_timeout; /* Hot-swap sensors/controls */ ipmi_sensor_t *hot_swap_requester; ipmi_sensor_id_t hot_swap_requester_id; int hot_swap_offset; int hot_swap_requesting_val; enum ipmi_hot_swap_states hot_swap_state; ipmi_control_t *hot_swap_power; ipmi_control_id_t hot_swap_power_id; ipmi_control_t *hot_swap_indicator; ipmi_control_id_t hot_swap_indicator_id; int hot_swap_ind_act; int hot_swap_ind_req_act; int hot_swap_ind_req_deact; int hot_swap_ind_inact; /* A handler for hot-swap. */ locked_list_t *hot_swap_handlers, *hot_swap_handlers_cl; ipmi_entity_info_t *ents; /* Allow only one internal fru fetch at a time. */ int in_fru_fetch; ipmi_fru_t *fru; int hot_swappable; int supports_managed_hot_swap; ipmi_entity_hot_swap_t hs_cb; /* Callbacks for various events on an entity. */ locked_list_t *fru_handlers, *fru_handlers_cl; locked_list_t *fru_handlers_werr, *fru_handlers_werr_cl; locked_list_t *sensor_handlers, *sensor_handlers_cl; locked_list_t *control_handlers, *control_handlers_cl; locked_list_t *presence_handlers, *presence_handlers_cl; locked_list_t *fully_up_handlers, *fully_up_handlers_cl; /* Used for SDR output (not currently supported). */ entity_sdr_add_cb sdr_gen_output; void *sdr_gen_cb_data; /* Queue we use for operations. */ opq_t *waitq; /* When using the FRU device to detect presence. */ int frudev_present; ipmi_mc_t *frudev_mc; /* Note that the MC cannot be destroyed while we have an active monitor on it, so this is safe. */ int frudev_active; int frudev_active_reported; /* OEM info assigned to an entity, for use by plugins. */ void *oem_info; ipmi_entity_cleanup_oem_info_cb oem_info_cleanup_handler; /* Name we use for reporting. We add a ' ' onto the end, thus the +1. */ char name[IPMI_ENTITY_NAME_LEN+1]; /* Cruft */ ipmi_entity_presence_nd_cb cruft_presence_handler; void *cruft_presence_cb_data; ipmi_entity_sensor_cb cruft_sensor_handler; void *cruft_sensor_cb_data; ipmi_entity_control_cb cruft_control_handler; void *cruft_control_cb_data; ipmi_entity_fru_cb cruft_fru_handler; void *cruft_fru_cb_data; }; struct ipmi_entity_info_s { locked_list_t *update_handlers; locked_list_t *update_cl_handlers; ipmi_domain_t *domain; ipmi_domain_id_t domain_id; locked_list_t *entities; }; #define ent_lock(e) ipmi_lock(e->elock) #define ent_unlock(e) ipmi_unlock(e->elock) static void entity_mc_active(ipmi_mc_t *mc, int active, void *cb_data); static void call_presence_handlers(ipmi_entity_t *ent, int present); static void call_fully_up_handlers(ipmi_entity_t *ent); /*********************************************************************** * * The internal hot-swap callbacks. * **********************************************************************/ static int e_get_hot_swap_state(ipmi_entity_t *ent, ipmi_entity_hot_swap_state_cb handler, void *cb_data); static int e_set_auto_activate(ipmi_entity_t *ent, ipmi_timeout_t auto_act, ipmi_entity_cb done, void *cb_data); static int e_get_auto_activate(ipmi_entity_t *ent, ipmi_entity_time_cb handler, void *cb_data); static int e_set_auto_deactivate(ipmi_entity_t *ent, ipmi_timeout_t auto_act, ipmi_entity_cb done, void *cb_data); static int e_get_auto_deactivate(ipmi_entity_t *ent, ipmi_entity_time_cb handler, void *cb_data); static int e_activate(ipmi_entity_t *ent, ipmi_entity_cb done, void *cb_data); static int e_deactivate(ipmi_entity_t *ent, ipmi_entity_cb done, void *cb_data); static int e_get_hot_swap_indicator(ipmi_entity_t *ent, ipmi_entity_val_cb handler, void *cb_data); static int e_set_hot_swap_indicator(ipmi_entity_t *ent, int val, ipmi_entity_cb done, void *cb_data); static int e_get_hot_swap_requester(ipmi_entity_t *ent, ipmi_entity_val_cb handler, void *cb_data); static int e_check_hot_swap_state(ipmi_entity_t *ent); static ipmi_entity_hot_swap_t internal_hs_cb = { .get_hot_swap_state = e_get_hot_swap_state, .set_auto_activate = e_set_auto_activate, .get_auto_activate = e_get_auto_activate, .set_auto_deactivate = e_set_auto_deactivate, .get_auto_deactivate = e_get_auto_deactivate, .activate = e_activate, .deactivate = e_deactivate, .get_hot_swap_indicator = e_get_hot_swap_indicator, .set_hot_swap_indicator = e_set_hot_swap_indicator, .get_hot_swap_requester = e_get_hot_swap_requester, .check_hot_swap_state = e_check_hot_swap_state, }; /*********************************************************************** * * Entity iteration helpers * **********************************************************************/ static void entities_lock(void *cb_data) { ipmi_domain_t *domain = cb_data; i_ipmi_domain_entity_lock(domain); } static void entities_unlock(void *cb_data) { ipmi_domain_t *domain = cb_data; i_ipmi_domain_entity_unlock(domain); } static int iterate_entity_prefunc(void *cb_data, void *item1, void *item2) { ipmi_entity_t *ent = item1; i_ipmi_entity_get(ent); return LOCKED_LIST_ITER_CONTINUE; } /*********************************************************************** * * Entity allocation/destruction * **********************************************************************/ int ipmi_entity_info_alloc(ipmi_domain_t *domain, ipmi_entity_info_t **new_info) { ipmi_entity_info_t *ents; ents = ipmi_mem_alloc(sizeof(*ents)); if (!ents) return ENOMEM; ents->domain = domain; ents->domain_id = ipmi_domain_convert_to_id(domain); ents->entities = locked_list_alloc_my_lock(entities_lock, entities_unlock, domain); if (! ents->entities) { ipmi_mem_free(ents); return ENOMEM; } ents->update_handlers = locked_list_alloc(ipmi_domain_get_os_hnd(domain)); if (! ents->update_handlers) { locked_list_destroy(ents->entities); ipmi_mem_free(ents); return ENOMEM; } ents->update_cl_handlers = locked_list_alloc(ipmi_domain_get_os_hnd(domain)); if (! ents->update_cl_handlers) { locked_list_destroy(ents->update_handlers); locked_list_destroy(ents->entities); ipmi_mem_free(ents); return ENOMEM; } *new_info = ents; return 0; } static void entity_start_timer(ent_timer_info_t *info, ipmi_timeout_t timeout, os_timed_out_t handler) { /* Need to time the operation. */ struct timeval tv; if (timeout == IPMI_TIMEOUT_FOREVER) return; tv.tv_sec = timeout / 1000000000; tv.tv_usec = (timeout % 1000000000) / 1000; ipmi_lock(info->lock); if (!info->running) { info->os_hnd->start_timer(info->os_hnd, info->timer, &tv, handler, info); info->running = 1; } ipmi_unlock(info->lock); } static int entity_alloc_timer(ipmi_entity_t *entity, ent_timer_info_t **rinfo) { ent_timer_info_t *info; int rv; info = ipmi_mem_alloc(sizeof(*info)); if (!info) return ENOMEM; memset(info, 0, sizeof(*info)); info->os_hnd = entity->os_hnd; info->entity = entity; rv = info->os_hnd->alloc_timer(info->os_hnd, &info->timer); if (rv) { ipmi_mem_free(info); return rv; } rv = ipmi_create_lock(entity->domain, &info->lock); if (rv) { info->os_hnd->free_timer(info->os_hnd, info->timer); ipmi_mem_free(info); return rv; } *rinfo = info; return 0; } static void entity_destroy_timer(ent_timer_info_t *info) { int rv; ipmi_lock(info->lock); if (info->running) { rv = info->os_hnd->stop_timer(info->os_hnd, info->timer); if (!rv) { info->destroyed = 1; ipmi_unlock(info->lock); return; } } ipmi_unlock(info->lock); info->os_hnd->free_timer(info->os_hnd, info->timer); ipmi_destroy_lock(info->lock); ipmi_mem_free(info); } typedef struct hot_swap_cl_info_s { ipmi_entity_hot_swap_cb handler; void *handler_data; } hot_swap_cl_info_t; static int iterate_hot_swap_cl(void *cb_data, void *item1, void *item2) { hot_swap_cl_info_t *info = cb_data; ipmi_entity_hot_swap_cl_cb handler = item1; handler(info->handler, info->handler_data, item2); return LOCKED_LIST_ITER_CONTINUE; } static int hot_swap_cleanup(void *cb_data, void *item1, void *item2) { ipmi_entity_t *ent = cb_data; hot_swap_cl_info_t info; info.handler = item1; info.handler_data = item2; locked_list_iterate(ent->hot_swap_handlers_cl, iterate_hot_swap_cl, &info); return LOCKED_LIST_ITER_CONTINUE; } typedef struct presence_cl_info_s { ipmi_entity_presence_change_cb handler; void *handler_data; } presence_cl_info_t; static int iterate_presence_cl(void *cb_data, void *item1, void *item2) { presence_cl_info_t *info = cb_data; ipmi_entity_presence_cl_cb handler = item1; handler(info->handler, info->handler_data, item2); return LOCKED_LIST_ITER_CONTINUE; } static int presence_cleanup(void *cb_data, void *item1, void *item2) { ipmi_entity_t *ent = cb_data; presence_cl_info_t info; info.handler = item1; info.handler_data = item2; locked_list_iterate(ent->presence_handlers_cl, iterate_presence_cl, &info); return LOCKED_LIST_ITER_CONTINUE; } typedef struct fully_up_cl_info_s { ipmi_entity_ptr_cb handler; void *handler_data; } fully_up_cl_info_t; static int iterate_fully_up_cl(void *cb_data, void *item1, void *item2) { fully_up_cl_info_t *info = cb_data; ipmi_entity_fully_up_cl_cb handler = item1; handler(info->handler, info->handler_data, item2); return LOCKED_LIST_ITER_CONTINUE; } static int fully_up_cleanup(void *cb_data, void *item1, void *item2) { ipmi_entity_t *ent = cb_data; fully_up_cl_info_t info; info.handler = item1; info.handler_data = item2; locked_list_iterate(ent->fully_up_handlers_cl, iterate_fully_up_cl, &info); return LOCKED_LIST_ITER_CONTINUE; } typedef struct fru_cl_info_s { ipmi_entity_fru_cb handler; void *handler_data; } fru_cl_info_t; static int iterate_fru_cl(void *cb_data, void *item1, void *item2) { fru_cl_info_t *info = cb_data; ipmi_entity_fru_cl_cb handler = item1; handler(info->handler, info->handler_data, item2); return LOCKED_LIST_ITER_CONTINUE; } static int fru_cleanup(void *cb_data, void *item1, void *item2) { ipmi_entity_t *ent = cb_data; fru_cl_info_t info; info.handler = item1; info.handler_data = item2; locked_list_iterate(ent->fru_handlers_cl, iterate_fru_cl, &info); return LOCKED_LIST_ITER_CONTINUE; } typedef struct fru_werr_cl_info_s { ipmi_entity_fru_werr_cb handler; void *handler_data; } fru_werr_cl_info_t; static int iterate_fru_werr_cl(void *cb_data, void *item1, void *item2) { fru_werr_cl_info_t *info = cb_data; ipmi_entity_fru_werr_cl_cb handler = item1; handler(info->handler, info->handler_data, item2); return LOCKED_LIST_ITER_CONTINUE; } static int fru_werr_cleanup(void *cb_data, void *item1, void *item2) { ipmi_entity_t *ent = cb_data; fru_werr_cl_info_t info; info.handler = item1; info.handler_data = item2; locked_list_iterate(ent->fru_handlers_werr_cl, iterate_fru_werr_cl, &info); return LOCKED_LIST_ITER_CONTINUE; } typedef struct control_cl_info_s { ipmi_entity_control_cb handler; void *handler_data; } control_cl_info_t; static int iterate_control_cl(void *cb_data, void *item1, void *item2) { control_cl_info_t *info = cb_data; ipmi_entity_control_cl_cb handler = item1; handler(info->handler, info->handler_data, item2); return LOCKED_LIST_ITER_CONTINUE; } static int control_cleanup(void *cb_data, void *item1, void *item2) { ipmi_entity_t *ent = cb_data; control_cl_info_t info; info.handler = item1; info.handler_data = item2; locked_list_iterate(ent->control_handlers_cl, iterate_control_cl, &info); return LOCKED_LIST_ITER_CONTINUE; } typedef struct sensor_cl_info_s { ipmi_entity_sensor_cb handler; void *handler_data; } sensor_cl_info_t; static int iterate_sensor_cl(void *cb_data, void *item1, void *item2) { sensor_cl_info_t *info = cb_data; ipmi_entity_sensor_cl_cb handler = item1; handler(info->handler, info->handler_data, item2); return LOCKED_LIST_ITER_CONTINUE; } static int sensor_cleanup(void *cb_data, void *item1, void *item2) { ipmi_entity_t *ent = cb_data; sensor_cl_info_t info; info.handler = item1; info.handler_data = item2; locked_list_iterate(ent->sensor_handlers_cl, iterate_sensor_cl, &info); return LOCKED_LIST_ITER_CONTINUE; } static int destroy_entity(void *cb_data, void *item1, void *item2) { ipmi_entity_t *ent = (ipmi_entity_t *) item1; entity_destroy_timer(ent->hot_swap_act_info); entity_destroy_timer(ent->hot_swap_deact_info); if (ent->frudev_present) { i_ipmi_domain_mc_lock(ent->domain); i_ipmi_mc_get(ent->frudev_mc); i_ipmi_domain_mc_unlock(ent->domain); ipmi_mc_remove_active_handler(ent->frudev_mc, entity_mc_active, ent); i_ipmi_mc_release(ent->frudev_mc); i_ipmi_mc_put(ent->frudev_mc); } if (ent->oem_info_cleanup_handler) ent->oem_info_cleanup_handler(ent, ent->oem_info); if (ent->fru) ipmi_fru_destroy_internal(ent->fru, NULL, NULL); if (ent->waitq) opq_destroy(ent->waitq); locked_list_destroy(ent->parent_entities); locked_list_destroy(ent->child_entities); locked_list_destroy(ent->sensors); locked_list_destroy(ent->controls); locked_list_iterate(ent->hot_swap_handlers, hot_swap_cleanup, ent); locked_list_destroy(ent->hot_swap_handlers); locked_list_destroy(ent->hot_swap_handlers_cl); locked_list_iterate(ent->presence_handlers, presence_cleanup, ent); locked_list_destroy(ent->presence_handlers); locked_list_destroy(ent->presence_handlers_cl); locked_list_iterate(ent->fully_up_handlers, fully_up_cleanup, ent); locked_list_destroy(ent->fully_up_handlers); locked_list_destroy(ent->fully_up_handlers_cl); locked_list_iterate(ent->fru_handlers, fru_cleanup, ent); locked_list_iterate(ent->fru_handlers_werr, fru_werr_cleanup, ent); locked_list_destroy(ent->fru_handlers); locked_list_destroy(ent->fru_handlers_cl); locked_list_destroy(ent->fru_handlers_werr); locked_list_destroy(ent->fru_handlers_werr_cl); locked_list_iterate(ent->control_handlers, control_cleanup, ent); locked_list_destroy(ent->control_handlers); locked_list_destroy(ent->control_handlers_cl); locked_list_iterate(ent->sensor_handlers, sensor_cleanup, ent); locked_list_destroy(ent->sensor_handlers); locked_list_destroy(ent->sensor_handlers_cl); ipmi_destroy_lock(ent->elock); ipmi_mem_free(ent); return LOCKED_LIST_ITER_CONTINUE; } typedef struct entity_update_cl_info_s { ipmi_domain_entity_cb handler; void *handler_data; } entity_update_cl_info_t; static int iterate_entity_update_cl(void *cb_data, void *item1, void *item2) { entity_update_cl_info_t *info = cb_data; ipmi_domain_entity_cl_cb handler = item1; handler(info->handler, info->handler_data, item2); return LOCKED_LIST_ITER_CONTINUE; } static int entity_update_cleanup(void *cb_data, void *item1, void *item2) { entity_update_cl_info_t info; ipmi_entity_info_t *ents = cb_data; info.handler = item1; info.handler_data = item2; locked_list_iterate(ents->update_cl_handlers, iterate_entity_update_cl, &info); return LOCKED_LIST_ITER_CONTINUE; } int ipmi_entity_info_destroy(ipmi_entity_info_t *ents) { locked_list_iterate(ents->update_handlers, entity_update_cleanup, ents); locked_list_destroy(ents->update_handlers); locked_list_destroy(ents->update_cl_handlers); locked_list_iterate(ents->entities, destroy_entity, NULL); locked_list_destroy(ents->entities); ipmi_mem_free(ents); return 0; } typedef struct ent_info_update_handler_info_s { enum ipmi_update_e op; ipmi_domain_t *domain; ipmi_entity_t *entity; } ent_info_update_handler_info_t; static int call_entity_info_update_handler(void *cb_data, void *item1, void *item2) { ent_info_update_handler_info_t *info = cb_data; ipmi_domain_entity_cb handler = item1; handler(info->op, info->domain, info->entity, item2); return LOCKED_LIST_ITER_CONTINUE; } static void call_entity_update_handlers(ipmi_entity_t *ent, enum ipmi_update_e op) { ent_info_update_handler_info_t info; info.op = op; info.entity = ent; info.domain = ent->domain; locked_list_iterate(ent->ents->update_handlers, call_entity_info_update_handler, &info); } /* Returns true if the entity was really deleted, false if not. Must be called with the domain entity lock, unlocks it before return if it destroys the entity. */ static int cleanup_entity(ipmi_entity_t *ent) { /* First see if the entity is ready for cleanup. */ if ((ent->ref_count) || opq_stuff_in_progress(ent->waitq) || (locked_list_num_entries_nolock(ent->child_entities) != 0) || (locked_list_num_entries_nolock(ent->parent_entities) != 0) || (locked_list_num_entries_nolock(ent->sensors) != 0) || (locked_list_num_entries_nolock(ent->controls) != 0)) { return 0; } ent->destroyed = 1; i_ipmi_domain_entity_unlock(ent->domain); /* Tell the user I was destroyed. */ /* Call the update handler list. */ call_entity_update_handlers(ent, IPMI_DELETED); i_ipmi_domain_entity_lock(ent->domain); if (ent->destroyed) { /* The entity could have been resurrected, so don't do this unless it has remained destroyed. */ /* Remove it from the entities list. */ locked_list_remove_nolock(ent->ents->entities, ent, NULL); /* The sensor, control, parent, and child lists should be empty now, we can just destroy it. */ destroy_entity(NULL, ent, NULL); return 1; } else { return 0; } } void ipmi_entity_set_oem_info(ipmi_entity_t *entity, void *oem_info, ipmi_entity_cleanup_oem_info_cb cleanup_handler) { entity->oem_info = oem_info; entity->oem_info_cleanup_handler = cleanup_handler; } void * ipmi_entity_get_oem_info(ipmi_entity_t *entity) { CHECK_ENTITY_LOCK(entity); return entity->oem_info; } static void entity_set_name(ipmi_entity_t *entity) { int length = sizeof(entity->name); ent_lock(entity); length = ipmi_domain_get_name(entity->domain, entity->name, length); entity->name[length] = '('; length++; if (entity->key.entity_instance >= 0x60) { length += snprintf(entity->name+length, IPMI_ENTITY_NAME_LEN-length-3, "r%d.%d.%d.%d", entity->key.device_num.channel, entity->key.device_num.address, entity->key.entity_id, entity->key.entity_instance - 0x60); } else { length += snprintf(entity->name+length, IPMI_ENTITY_NAME_LEN-length-3, "%d.%d", entity->key.entity_id, entity->key.entity_instance); } entity->name[length] = ')'; length++; entity->name[length] = ' '; length++; entity->name[length] = '\0'; length++; ent_unlock(entity); } const char * i_ipmi_entity_name(const ipmi_entity_t *entity) { return entity->name; } static void entity_get_name_cb(ipmi_entity_t *entity, void *cb_data) { char **name = cb_data; *name = entity->name; } const char * i_ipmi_entity_id_name(ipmi_entity_id_t entity_id) { char *name = ""; ipmi_entity_pointer_cb(entity_id, entity_get_name_cb, &name); return name; } int ipmi_entity_get_name(ipmi_entity_t *ent, char *name, int length) { int slen; if (length <= 0) return 0; /* Never changes, no lock needed. */ slen = strlen(ent->name); if (slen == 0) { if (name) *name = '\0'; goto out; } slen -= 1; /* Remove the trailing ' ' */ if (slen >= length) slen = length - 1; if (name) { memcpy(name, ent->name, slen); name[slen] = '\0'; } out: return slen; } static int ent_use_frudev_for_presence(ipmi_entity_t *ent) { return ((!ent->presence_sensor) && (!ent->presence_bit_sensor) && (locked_list_num_entries_nolock(ent->controls) == 0) && (locked_list_num_entries_nolock(ent->sensors) == 0) && (locked_list_num_entries_nolock(ent->child_entities) == 0)); } /*********************************************************************** * * Handling of adding/removing/searching entities, parents, and * children. * **********************************************************************/ static void internal_fru_fetch_done(ipmi_entity_t *ent, void *cb_data) { ent_lock(ent); ent->in_fru_fetch = 0; ent_unlock(ent); } /* Must be called with the i_ipmi_domain_entity_lock() held. */ int i_ipmi_entity_get(ipmi_entity_t *ent) { ent->usecount++; return 0; } void i_ipmi_entity_put(ipmi_entity_t *ent) { ipmi_domain_t *domain = ent->domain; int entity_fru_fetch = 0; int report_fully_up = 0; i_ipmi_domain_entity_lock(domain); retry: if (ent->usecount == 1) { if (ent->pending_info_ready) { int was_fru = ipmi_entity_get_is_fru(ent); ent->info = ent->pending_info; /* If the entity became a fru and is present, get its fru info. */ if (!was_fru && ipmi_entity_get_is_fru(ent) && ent->present) entity_fru_fetch = 1; entity_set_name(ent); ent->pending_info_ready = 0; } if (ent->add_pending) { ent->add_pending = 0; ent->changed = 0; i_ipmi_domain_entity_unlock(domain); call_entity_update_handlers(ent, IPMI_ADDED); i_ipmi_domain_entity_lock(domain); /* Something grabbed the entity while the lock wasn't held, don't attempt the cleanup. */ if (ent->usecount != 1) goto out; } /* If the entity has changed, report it to the user. */ if (ent->changed) { ent->changed = 0; i_ipmi_domain_entity_unlock(domain); call_entity_update_handlers(ent, IPMI_CHANGED); i_ipmi_domain_entity_lock(domain); /* Something grabbed the entity while the lock wasn't held, don't attempt the cleanup. */ if (ent->usecount != 1) goto out; } if (ent->presence_possibly_changed) { i_ipmi_domain_entity_unlock(domain); ipmi_detect_entity_presence_change(ent, 0); i_ipmi_domain_entity_lock(domain); if (ent->usecount != 1) goto out; } while (ent->present_change_count) { int present; ent->present = !ent->present; present = ent->present; ent->present_change_count--; i_ipmi_domain_entity_unlock(domain); call_presence_handlers(ent, present); i_ipmi_domain_entity_lock(domain); if (ipmi_entity_get_is_fru(ent) && ent->present) entity_fru_fetch = 1; /* Something grabbed the entity while the lock wasn't held, don't attempt the cleanup. */ if (ent->usecount != 1) goto out; } if (cleanup_entity(ent)) goto out2; repend: if ((ent->add_pending) || (ent->changed) || (ent->present_change_count)) { /* Something happened to the entity while we were working, retry the operation to report the new thing. */ goto retry; } } out: /* Wait till here to start fetching FRUs, as we want to report the entity first before we start the fetch. */ ent_lock(ent); if (ent->present && !ent->in_fru_fetch && entity_fru_fetch) { int rv; entity_fru_fetch = 0; ent->in_fru_fetch = 1; ent_unlock(ent); rv = ipmi_entity_fetch_frus_cb(ent, internal_fru_fetch_done, NULL); if (rv) { ent_lock(ent); ent->in_fru_fetch = 0; ent_unlock(ent); } goto repend; } if (ent->usecount == 1 && !ent->fully_up && ent->sdr_add_done && !ent->in_presence_check && !ent->in_fru_fetch && !(ent_use_frudev_for_presence(ent) && !ent->frudev_active_reported)) { ent->fully_up = 1; report_fully_up = 1; } ent_unlock(ent); if (report_fully_up) { i_ipmi_domain_entity_unlock(domain); call_fully_up_handlers(ent); i_ipmi_domain_entity_lock(domain); if (ent->usecount != 1) goto retry; } ent->usecount--; out2: i_ipmi_domain_entity_unlock(domain); } int i_ipmi_entity_add_ref(ipmi_entity_t *ent) { ent_lock(ent); ent->ref_count++; ent_unlock(ent); return 0; } int i_ipmi_entity_remove_ref(ipmi_entity_t *ent) { ent_lock(ent); ent->ref_count--; ent_unlock(ent); return 0; } int ipmi_entity_info_add_update_handler(ipmi_entity_info_t *ents, ipmi_domain_entity_cb handler, void *cb_data) { if (locked_list_add(ents->update_handlers, handler, cb_data)) return 0; else return ENOMEM; } int ipmi_entity_info_remove_update_handler(ipmi_entity_info_t *ents, ipmi_domain_entity_cb handler, void *cb_data) { if (locked_list_remove(ents->update_handlers, handler, cb_data)) return 0; else return EINVAL; } int ipmi_entity_info_add_update_handler_cl(ipmi_entity_info_t *ents, ipmi_domain_entity_cl_cb handler, void *cb_data) { if (locked_list_add(ents->update_cl_handlers, handler, cb_data)) return 0; else return ENOMEM; } int ipmi_entity_info_remove_update_handler_cl(ipmi_entity_info_t *ents, ipmi_domain_entity_cl_cb handler, void *cb_data) { if (locked_list_remove(ents->update_cl_handlers, handler, cb_data)) return 0; else return EINVAL; } typedef struct search_info_s { dlr_ref_t key; ipmi_entity_t *ent; } search_info_t; static int search_entity(void *cb_data, void *item1, void *item2) { ipmi_entity_t *ent = (ipmi_entity_t *) item1; search_info_t *info = (search_info_t *) cb_data; int same; same = ((ent->key.device_num.channel == info->key.device_num.channel) && (ent->key.device_num.address == info->key.device_num.address) && (ent->key.entity_id == info->key.entity_id) && (ent->key.entity_instance == info->key.entity_instance)); if (same) { info->ent = ent; return LOCKED_LIST_ITER_STOP; } return LOCKED_LIST_ITER_CONTINUE; } static int entity_find(ipmi_entity_info_t *ents, ipmi_device_num_t device_num, int entity_id, int entity_instance, ipmi_entity_t **found_ent) { search_info_t info = { {device_num, entity_id, entity_instance}, NULL}; int rv = 0; locked_list_iterate_nolock(ents->entities, search_entity, &info); if (info.ent == NULL) { rv = ENOENT; } else { info.ent->usecount++; if (found_ent) *found_ent = info.ent; } return rv; } int ipmi_entity_find(ipmi_entity_info_t *ents, ipmi_mc_t *mc, int entity_id, int entity_instance, ipmi_entity_t **found_ent) { ipmi_device_num_t device_num; int rv; ipmi_entity_t *ent; CHECK_DOMAIN_LOCK(ents->domain); if (mc && entity_instance >= 0x60) { device_num.channel = ipmi_mc_get_channel(mc); device_num.address = ipmi_mc_get_address(mc); } else { device_num.channel = 0; device_num.address = 0; } i_ipmi_domain_entity_lock(ents->domain); rv = entity_find(ents, device_num, entity_id, entity_instance, &ent); if (!rv) { if (ent->destroyed) rv = ENOENT; else *found_ent = ent; } i_ipmi_domain_entity_unlock(ents->domain); return rv; } /* Must be called with i_ipmi_domain_entity_lock(), this will release the lock. */ static int entity_add(ipmi_entity_info_t *ents, ipmi_device_num_t device_num, int entity_id, int entity_instance, entity_sdr_add_cb sdr_gen_output, void *sdr_gen_cb_data, ipmi_entity_t **new_ent) { int rv; ipmi_entity_t *ent; os_handler_t *os_hnd; rv = entity_find(ents, device_num, entity_id, entity_instance, &ent); if (! rv) { if (ent->destroyed) { /* A destroy is currently being reported on the entity, resurrect it. */ ent->destroyed = 0; ent->add_pending = 1; } i_ipmi_domain_entity_unlock(ents->domain); if (sdr_gen_output != NULL) { ent->sdr_gen_output = sdr_gen_output; ent->sdr_gen_cb_data = sdr_gen_cb_data; } *new_ent = ent; return 0; } ent = ipmi_mem_alloc(sizeof(*ent)); if (!ent) return ENOMEM; memset(ent, 0, sizeof(*ent)); os_hnd = ipmi_domain_get_os_hnd(ents->domain); ent->sdr_gen_output = sdr_gen_output; ent->sdr_gen_cb_data = sdr_gen_cb_data; ent->domain = ents->domain; ent->os_hnd = ipmi_domain_get_os_hnd(ent->domain); ent->domain_id = ents->domain_id; ent->seq = ipmi_get_seq(); ent->child_entities = locked_list_alloc_my_lock(entities_lock, entities_unlock, ents->domain); if (!ent->child_entities) goto out_err; ent->parent_entities = locked_list_alloc_my_lock(entities_lock, entities_unlock, ents->domain); if (!ent->parent_entities) goto out_err; ent->sensors = locked_list_alloc_my_lock(entities_lock, entities_unlock, ents->domain); if (!ent->sensors) goto out_err; ent->controls = locked_list_alloc_my_lock(entities_lock, entities_unlock, ents->domain); if (!ent->controls) goto out_err; ent->hot_swap_handlers_cl = locked_list_alloc(ent->os_hnd); if (!ent->hot_swap_handlers_cl) goto out_err; ent->hot_swap_handlers = locked_list_alloc(ent->os_hnd); if (!ent->hot_swap_handlers) goto out_err; ent->presence_handlers_cl = locked_list_alloc(ent->os_hnd); if (!ent->presence_handlers_cl) goto out_err; ent->presence_handlers = locked_list_alloc(ent->os_hnd); if (!ent->presence_handlers) goto out_err; ent->fully_up_handlers_cl = locked_list_alloc(ent->os_hnd); if (!ent->fully_up_handlers_cl) goto out_err; ent->fully_up_handlers = locked_list_alloc(ent->os_hnd); if (!ent->fully_up_handlers) goto out_err; ent->waitq = opq_alloc(os_hnd); if (! ent->waitq) return ENOMEM; ent->fru_handlers_cl = locked_list_alloc(ent->os_hnd); if (!ent->fru_handlers_cl) goto out_err; ent->fru_handlers = locked_list_alloc(ent->os_hnd); if (!ent->fru_handlers) goto out_err; ent->fru_handlers_werr_cl = locked_list_alloc(ent->os_hnd); if (!ent->fru_handlers_werr_cl) goto out_err; ent->fru_handlers_werr = locked_list_alloc(ent->os_hnd); if (!ent->fru_handlers_werr) goto out_err; ent->sensor_handlers_cl = locked_list_alloc(ent->os_hnd); if (!ent->sensor_handlers_cl) goto out_err; ent->sensor_handlers = locked_list_alloc(ent->os_hnd); if (!ent->sensor_handlers) goto out_err; ent->control_handlers_cl = locked_list_alloc(ent->os_hnd); if (!ent->control_handlers_cl) goto out_err; ent->control_handlers = locked_list_alloc(ent->os_hnd); if (!ent->control_handlers) goto out_err; rv = ipmi_create_lock(ent->domain, &ent->elock); if (rv) goto out_err; rv = entity_alloc_timer(ent, &ent->hot_swap_act_info); if (rv) goto out_err; rv = entity_alloc_timer(ent, &ent->hot_swap_deact_info); if (rv) goto out_err; ent->presence_sensor = NULL; ent->presence_bit_sensor = NULL; ent->present = 0; ent->presence_possibly_changed = 1; ent->hot_swap_act_timeout = IPMI_TIMEOUT_FOREVER; ent->hot_swap_deact_timeout = IPMI_TIMEOUT_FOREVER; ent->ents = ents; ent->info.type = IPMI_ENTITY_UNKNOWN; ent->key.device_num = device_num; ent->key.entity_id = entity_id; ent->key.entity_instance = entity_instance; ent->info.id_type = IPMI_ASCII_STR; ent->entity_id_string = ipmi_get_entity_id_string(entity_id); ent->usecount = 1; entity_set_name(ent); if (! locked_list_add_nolock(ents->entities, ent, NULL)) goto out_err; i_ipmi_domain_entity_unlock(ent->domain); /* Report the add when the entity is put. */ ent->add_pending = 1; if (new_ent) *new_ent = ent; return 0; out_err: i_ipmi_domain_entity_unlock(ent->domain); if (ent->hot_swap_act_info) entity_destroy_timer(ent->hot_swap_act_info); if (ent->hot_swap_deact_info) entity_destroy_timer(ent->hot_swap_deact_info); if (ent->elock) ipmi_destroy_lock(ent->elock); if (ent->presence_handlers) locked_list_destroy(ent->presence_handlers); if (ent->presence_handlers_cl) locked_list_destroy(ent->presence_handlers_cl); if (ent->fully_up_handlers) locked_list_destroy(ent->fully_up_handlers); if (ent->fully_up_handlers_cl) locked_list_destroy(ent->fully_up_handlers_cl); if (ent->waitq) opq_destroy(ent->waitq); if (ent->fru_handlers) locked_list_destroy(ent->fru_handlers); if (ent->fru_handlers_cl) locked_list_destroy(ent->fru_handlers_cl); if (ent->fru_handlers_werr) locked_list_destroy(ent->fru_handlers_werr); if (ent->fru_handlers_werr_cl) locked_list_destroy(ent->fru_handlers_werr_cl); if (ent->control_handlers) locked_list_destroy(ent->control_handlers); if (ent->control_handlers_cl) locked_list_destroy(ent->control_handlers_cl); if (ent->sensor_handlers) locked_list_destroy(ent->sensor_handlers); if (ent->sensor_handlers_cl) locked_list_destroy(ent->sensor_handlers_cl); if (ent->hot_swap_handlers) locked_list_destroy(ent->hot_swap_handlers); if (ent->hot_swap_handlers_cl) locked_list_destroy(ent->hot_swap_handlers_cl); if (ent->controls) locked_list_destroy(ent->controls); if (ent->sensors) locked_list_destroy(ent->sensors); if (ent->parent_entities) locked_list_destroy(ent->parent_entities); if (ent->child_entities) locked_list_destroy(ent->child_entities); ipmi_mem_free(ent); return ENOMEM; } int ipmi_entity_add(ipmi_entity_info_t *ents, ipmi_domain_t *domain, unsigned char mc_channel, unsigned char mc_slave_addr, int lun, int entity_id, int entity_instance, char *id, enum ipmi_str_type_e id_type, unsigned int id_len, entity_sdr_add_cb sdr_gen_output, void *sdr_gen_cb_data, ipmi_entity_t **new_ent) { ipmi_device_num_t device_num; int rv; ipmi_entity_t *ent; CHECK_DOMAIN_LOCK(domain); if (entity_instance >= 0x60) { device_num.channel = mc_channel; device_num.address = mc_slave_addr; } else { device_num.channel = 0; device_num.address = 0; } i_ipmi_domain_entity_lock(domain); /* This will release the lock. */ rv = entity_add(ents, device_num, entity_id, entity_instance, sdr_gen_output, sdr_gen_cb_data, &ent); if (!rv) { ent_lock(ent); ent->sdr_add_done = 1; /* Not added by SDR, so fudge. */ ent_unlock(ent); if (!ent->info.id_len) ipmi_entity_set_id(ent, id, id_type, id_len); if (new_ent) *new_ent = ent; } return rv; } /* Must be called with both the child and parent entities used and the domain entity lock held. */ static void add_child(ipmi_entity_t *ent, ipmi_entity_t *child, locked_list_entry_t *entry1, locked_list_entry_t *entry2) { locked_list_add_entry_nolock(ent->child_entities, child, NULL, entry1); locked_list_add_entry_nolock(child->parent_entities, ent, NULL, entry2); ent->presence_possibly_changed = 1; } int ipmi_entity_add_child(ipmi_entity_t *ent, ipmi_entity_t *child) { locked_list_entry_t *entry1; locked_list_entry_t *entry2; int rv = 0; CHECK_ENTITY_LOCK(ent); CHECK_ENTITY_LOCK(child); i_ipmi_domain_entity_lock(ent->domain); entry1 = locked_list_alloc_entry(); if (!entry1) { rv = ENOMEM; goto out_unlock; } entry2 = locked_list_alloc_entry(); if (!entry2) { locked_list_free_entry(entry1); rv = ENOMEM; goto out_unlock; } add_child(ent, child, entry1, entry2); ent->changed = 1; child->changed = 1; i_ipmi_domain_entity_unlock(ent->domain); return 0; out_unlock: i_ipmi_domain_entity_unlock(ent->domain); return rv; } static int ipmi_entity_remove_child_internal(ipmi_entity_t *ent, ipmi_entity_t *child) { int rv = 0; CHECK_ENTITY_LOCK(ent); CHECK_ENTITY_LOCK(child); if (! locked_list_remove_nolock(ent->child_entities, child, NULL)) rv = EINVAL; locked_list_remove_nolock(child->parent_entities, ent, NULL); ent->presence_possibly_changed = 1; if (!rv) { ent->changed = 1; child->changed = 1; } return rv; } int ipmi_entity_remove_child(ipmi_entity_t *ent, ipmi_entity_t *child) { int rv = 0; CHECK_ENTITY_LOCK(ent); CHECK_ENTITY_LOCK(child); i_ipmi_domain_entity_lock(ent->domain); if (! locked_list_remove_nolock(ent->child_entities, child, NULL)) rv = EINVAL; locked_list_remove_nolock(child->parent_entities, ent, NULL); ent->presence_possibly_changed = 1; i_ipmi_domain_entity_unlock(ent->domain); if (!rv) { ent->changed = 1; child->changed = 1; } return rv; } typedef struct iterate_child_info_s { ipmi_entity_t *ent; ipmi_entity_iterate_child_cb handler; void *cb_data; } iterate_child_info_t; static int iterate_child_handler(void *cb_data, void *item1, void *item2) { iterate_child_info_t *info = cb_data; ipmi_entity_t *ent = item1; info->handler(info->ent, item1, info->cb_data); i_ipmi_entity_put(ent); return LOCKED_LIST_ITER_CONTINUE; } void ipmi_entity_iterate_children(ipmi_entity_t *ent, ipmi_entity_iterate_child_cb handler, void *cb_data) { iterate_child_info_t info = { ent, handler, cb_data }; locked_list_iterate_prefunc(ent->child_entities, iterate_entity_prefunc, iterate_child_handler, &info); } typedef struct iterate_parent_info_s { ipmi_entity_t *ent; ipmi_entity_iterate_parent_cb handler; void *cb_data; } iterate_parent_info_t; static int iterate_parent_handler(void *cb_data, void *item1, void *item2) { iterate_parent_info_t *info = cb_data; ipmi_entity_t *ent = item1; info->handler(info->ent, item1, info->cb_data); i_ipmi_entity_put(ent); return LOCKED_LIST_ITER_CONTINUE; } void ipmi_entity_iterate_parents(ipmi_entity_t *ent, ipmi_entity_iterate_parent_cb handler, void *cb_data) { iterate_parent_info_t info = { ent, handler, cb_data }; CHECK_ENTITY_LOCK(ent); locked_list_iterate_prefunc(ent->parent_entities, iterate_entity_prefunc, iterate_parent_handler, &info); } /*********************************************************************** * * Entity presence handling. * **********************************************************************/ static int handle_hot_swap_presence(ipmi_entity_t *ent, int present, ipmi_event_t *event); int ipmi_entity_add_presence_handler(ipmi_entity_t *ent, ipmi_entity_presence_change_cb handler, void *cb_data) { CHECK_ENTITY_LOCK(ent); if (locked_list_add(ent->presence_handlers, handler, cb_data)) return 0; else return ENOMEM; } int ipmi_entity_remove_presence_handler(ipmi_entity_t *ent, ipmi_entity_presence_change_cb handler, void *cb_data) { CHECK_ENTITY_LOCK(ent); if (locked_list_remove(ent->presence_handlers, handler, cb_data)) return 0; else return EINVAL; } int ipmi_entity_add_presence_handler_cl(ipmi_entity_t *ent, ipmi_entity_presence_cl_cb handler, void *cb_data) { CHECK_ENTITY_LOCK(ent); if (locked_list_add(ent->presence_handlers_cl, handler, cb_data)) return 0; else return ENOMEM; } int ipmi_entity_remove_presence_handler_cl(ipmi_entity_t *ent, ipmi_entity_presence_cl_cb handler, void *cb_data) { CHECK_ENTITY_LOCK(ent); if (locked_list_remove(ent->presence_handlers_cl, handler, cb_data)) return 0; else return EINVAL; } typedef struct presence_handler_info_s { ipmi_entity_t *ent; int present; } presence_handler_info_t; static int call_presence_handler(void *cb_data, void *item1, void *item2) { presence_handler_info_t *info = cb_data; ipmi_entity_presence_change_cb handler = item1; handler(info->ent, info->present, item2, NULL); return LOCKED_LIST_ITER_CONTINUE; } static void call_presence_handlers(ipmi_entity_t *ent, int present) { presence_handler_info_t info; info.ent = ent; info.present = present; ent_lock(ent); if (ent->cruft_presence_handler) { ipmi_entity_presence_nd_cb handler = ent->cruft_presence_handler; void *cb_data = ent->cruft_presence_cb_data; ent_unlock(ent); handler(ent, info.present, cb_data, NULL); } else ent_unlock(ent); locked_list_iterate(ent->presence_handlers, call_presence_handler, &info); } /* This is for iterating the parents when a sensor's presence changes. The parent's presence may depend on it's childrens' presence, if it has no sensors. */ static void presence_parent_handler(ipmi_entity_t *ent, ipmi_entity_t *parent, void *cb_data) { /* We only set the entity to check presence. We can't use the child directly because the algorithm is unfortunately complicated. */ parent->presence_possibly_changed = 1; } static void presence_changed(ipmi_entity_t *ent, int present) { ipmi_fru_t *fru; ipmi_domain_t *domain = ent->domain; int entity_fru_fetch = 0; ent->presence_event_count++; if (present != ent->curr_present) { ent->curr_present = present; if (ent->hot_swappable &&(ent->hs_cb.get_hot_swap_state == e_get_hot_swap_state)) { /* Do internal presence handling if we have the internal hot-swap machine installed. */ handle_hot_swap_presence(ent, present, NULL); } /* When the entity becomes present or absent, fetch or destroy its FRU info. */ if (ipmi_entity_get_is_fru(ent)) { if (present) { entity_fru_fetch = 1; } else if (ent->fru != NULL) { fru = ent->fru; ent->fru = NULL; ipmi_fru_destroy_internal(fru, NULL, NULL); i_ipmi_entity_call_fru_handlers(ent, IPMI_DELETED, 0); } } /* Subtle stuff, we only report presence if no one else is using the entity. If someone else is, we save it for later. */ i_ipmi_domain_entity_lock(domain); if (ent->usecount == 1) { ent->present = !ent->present; i_ipmi_domain_entity_unlock(domain); call_presence_handlers(ent, present); i_ipmi_domain_entity_lock(domain); while ((ent->usecount == 1) && (ent->present_change_count)) { ent->present = !ent->present; present = ent->present; ent->present_change_count--; i_ipmi_domain_entity_unlock(domain); call_presence_handlers(ent, present); i_ipmi_domain_entity_lock(domain); } } else { ent->present_change_count++; } /* Wait till here to start fetching FRUs, as we want to report the entity first before we start the fetch. */ ent_lock(ent); if (ent->present && !ent->in_fru_fetch && entity_fru_fetch) { int rv; entity_fru_fetch = 0; ent->in_fru_fetch = 1; ent_unlock(ent); rv = ipmi_entity_fetch_frus_cb(ent, internal_fru_fetch_done, NULL); if (rv) { ent_lock(ent); ent->in_fru_fetch = 0; ent_unlock(ent); } } else { ent_unlock(ent); } i_ipmi_domain_entity_unlock(domain); /* If our presence changes, that can affect parents, too. So we rescan them. */ ipmi_entity_iterate_parents(ent, presence_parent_handler, NULL); } } static void presence_child_handler(ipmi_entity_t *ent, ipmi_entity_t *child, void *cb_data) { int *present = cb_data; int p = child->present; if (ent->present_change_count % 2) p = !p; if (p) *present = p; } static int presence_sensor_changed(ipmi_sensor_t *sensor, enum ipmi_event_dir_e dir, int offset, int severity, int prev_severity, void *cb_data, ipmi_event_t *event) { ipmi_entity_t *ent = cb_data; /* zero offset is the "present" offset, 1 or 2 means it absent or disabled, coupled with the assertion/deassertion. */ if (dir == IPMI_ASSERTION) presence_changed(ent, offset == ent->presence_bit_offset); else if (dir == IPMI_DEASSERTION) presence_changed(ent, offset != ent->presence_bit_offset); return IPMI_EVENT_NOT_HANDLED; } static int presence_bit_sensor_changed(ipmi_sensor_t *sensor, enum ipmi_event_dir_e dir, int offset, int severity, int prev_severity, void *cb_data, ipmi_event_t *event) { ipmi_entity_t *ent = cb_data; if (offset != ent->presence_bit_offset) return IPMI_EVENT_NOT_HANDLED; /* Assertion means present. */ if (dir == IPMI_ASSERTION) presence_changed(ent, 1); else if (dir == IPMI_DEASSERTION) presence_changed(ent, 0); return IPMI_EVENT_NOT_HANDLED; } typedef struct ent_detect_info_s { int force; } ent_detect_info_t; typedef struct ent_active_detect_s { ipmi_lock_t *lock; ipmi_entity_id_t ent_id; int try_count; int done_count; int present; unsigned int start_presence_event_count; ipmi_msg_t *msg; } ent_active_detect_t; static void detect_cleanup(ent_active_detect_t *info, ipmi_entity_t *ent, ipmi_domain_t *domain) { ipmi_unlock(info->lock); ipmi_destroy_lock(info->lock); ipmi_mem_free(info); if (ent) { ent_lock(ent); ent->in_presence_check = 0; ent_unlock(ent); } i_ipmi_put_domain_fully_up(domain, "detect_cleanup"); } static void presence_finalize(ipmi_entity_t *ent, const char *source) { ent_lock(ent); ent->in_presence_check = 0; ent_unlock(ent); i_ipmi_put_domain_fully_up(ent->domain, source); } static void detect_done(ipmi_entity_t *ent, ent_active_detect_t *info) { ipmi_unlock(info->lock); presence_changed(ent, info->present); ipmi_destroy_lock(info->lock); ipmi_mem_free(info); presence_finalize(ent, "detect_done"); } static void detect_frudev_handler(ipmi_entity_t *ent, void *cb_data) { ent_active_detect_t *info = cb_data; if (info->start_presence_event_count != ent->presence_event_count) { /* Something else has changed the presence since we started the presence detection process, then don't change the value. There are races where the entity could have been set present and we detect it as not present. However, it is not possible to detect it as present and for something else to set it not present. */ detect_cleanup(info, ent, ent->domain); return; } if (info->msg->data[0] == 0) info->present = 1; detect_done(ent, info); } static void detect_frudev(ipmi_mc_t *mc, ipmi_msg_t *rsp, void *rsp_data) { ent_active_detect_t *info = rsp_data; ipmi_lock(info->lock); info->msg = rsp; if (ipmi_entity_pointer_cb(info->ent_id, detect_frudev_handler, info)) /* We cheat and pull the domain from the entity id. The domain * still has to be around in the place, but we can't rely on the * MC as it may have gone away if it failed or the domain is in * shutdown. */ detect_cleanup(info, NULL, info->ent_id.domain_id.domain); } /* This is the end of the line on checks. We have to report something here. */ static void try_presence_frudev(ipmi_entity_t *ent, ent_active_detect_t *info) { ipmi_msg_t msg; unsigned char data[1]; int rv; if ((!ent->frudev_present) || (!ent->frudev_active)) { detect_done(ent, info); return; } msg.netfn = IPMI_STORAGE_NETFN; msg.cmd = IPMI_GET_FRU_INVENTORY_AREA_INFO_CMD; msg.data = data; data[0] = ent->info.fru_device_id; /* Will be 0 for MCs, so this is ok even though it is not in the MC record. */ msg.data_len = 1; /* Send a message to the FRU device and see if we can get some data. */ i_ipmi_domain_mc_lock(ent->domain); i_ipmi_mc_get(ent->frudev_mc); i_ipmi_domain_mc_unlock(ent->domain); rv = ipmi_mc_send_command(ent->frudev_mc, ent->info.lun, &msg, detect_frudev, info); i_ipmi_mc_put(ent->frudev_mc); if (rv) detect_done(ent, info); else ipmi_unlock(info->lock); } static int try_presence_children(ipmi_entity_t *parent, ent_active_detect_t *info) { if (! ipmi_entity_get_is_parent(parent)) return ENOSYS; ipmi_entity_iterate_children(parent, presence_child_handler, &info->present); detect_done(parent, info); return 0; } static void control_detect_handler(ipmi_entity_t *ent, void *cb_data) { ent_active_detect_t *info = cb_data; if (info->start_presence_event_count != ent->presence_event_count) { /* Something else has changed the presence since we started the presence detection process, then don't change the value. There are races where the entity could have been set present and we detect it as not present. However, it is not possible to detect it as present and for something else to set it not present. */ detect_cleanup(info, ent, ent->domain); return; } if (!info->present) { /* Nothing present from the sensors, try the children. */ if (! try_presence_children(ent, info)) { /* Children got it, nothing to do */ } else { try_presence_frudev(ent, info); } } else { detect_done(ent, info); } } static void detect_control_val(ipmi_control_t *control, int err, int *val, void *cb_data) { ent_active_detect_t *info = cb_data; ipmi_lock(info->lock); if (!err) info->present = 1; info->done_count++; if (info->try_count == info->done_count) { if (ipmi_entity_pointer_cb(info->ent_id, control_detect_handler, info)) detect_cleanup(info, NULL, ipmi_control_get_domain(control)); } else ipmi_unlock(info->lock); } static void detect_control_light(ipmi_control_t *control, int err, ipmi_light_setting_t *settings, void *cb_data) { ent_active_detect_t *info = cb_data; ipmi_lock(info->lock); if (!err) info->present = 1; info->done_count++; if (info->try_count == info->done_count) { if (ipmi_entity_pointer_cb(info->ent_id, control_detect_handler, info)) detect_cleanup(info, NULL, ipmi_control_get_domain(control)); } else ipmi_unlock(info->lock); } static void detect_control_id(ipmi_control_t *control, int err, unsigned char *val, int length, void *cb_data) { ent_active_detect_t *info = cb_data; ipmi_lock(info->lock); if (!err) info->present = 1; info->done_count++; if (info->try_count == info->done_count) { if (ipmi_entity_pointer_cb(info->ent_id, control_detect_handler, info)) detect_cleanup(info, NULL, ipmi_control_get_domain(control)); } else ipmi_unlock(info->lock); } static void detect_control_display(ipmi_control_t *control, int err, char *str, unsigned int len, void *cb_data) { ent_active_detect_t *info = cb_data; ipmi_lock(info->lock); if (!err) info->present = 1; info->done_count++; if (info->try_count == info->done_count) { if (ipmi_entity_pointer_cb(info->ent_id, control_detect_handler, info)) detect_cleanup(info, NULL, ipmi_control_get_domain(control)); } else ipmi_unlock(info->lock); } static void control_detect_send(ipmi_entity_t *ent, ipmi_control_t *control, void *cb_data) { ent_active_detect_t *info = cb_data; int rv; if (ipmi_control_get_ignore_for_presence(control)) /* Control should be ignored for presence. */ return; info->try_count++; ipmi_unlock(info->lock); rv = ipmi_control_get_val(control, detect_control_val, info); if (rv) rv = ipmi_control_get_light(control, detect_control_light, info); if (rv) rv = ipmi_control_identifier_get_val(control, detect_control_id, info); if (rv) rv = ipmi_control_get_display_string(control, 0, 0, 1, detect_control_display, info); ipmi_lock(info->lock); if (rv) info->try_count--; } static int try_presence_controls(ipmi_entity_t *ent, ent_active_detect_t *info) { if (locked_list_num_entries(ent->controls) == 0) return ENOSYS; /* It has sensors, try to see if any of those are active. Start the count at 1 so the callbacks don't do the call before we are done here. */ info->try_count = 1; info->done_count = 0; ipmi_entity_iterate_controls(ent, control_detect_send, info); /* I couldn't message any controls, go on. */ if (info->try_count == 1) return ENOSYS; info->done_count++; if (info->try_count == info->done_count) control_detect_handler(ent, info); else ipmi_unlock(info->lock); return 0; } static void sensor_detect_handler(ipmi_entity_t *ent, void *cb_data) { ent_active_detect_t *info = cb_data; if (info->start_presence_event_count != ent->presence_event_count) { /* Something else has changed the presence since we started the presence detection process, then don't change the value. There are races where the entity could have been set present and we detect it as not present. However, it is not possible to detect it as present and for something else to set it not present. */ detect_cleanup(info, ent, ent->domain); return; } if (!info->present) { /* Nothing present from the sensors, try other stuff. */ if (! try_presence_controls(ent, info)) { /* Controls got it, nothing to do */ } else if (! try_presence_children(ent, info)) { /* Children got it, nothing to do */ } else { try_presence_frudev(ent, info); } } else { detect_done(ent, info); } } static void detect_states_read(ipmi_sensor_t *sensor, int err, ipmi_states_t *states, void *cb_data) { ent_active_detect_t *info = cb_data; ipmi_lock(info->lock); if (!err && ipmi_is_sensor_scanning_enabled(states) && !ipmi_is_initial_update_in_progress(states)) info->present = 1; info->done_count++; if (info->try_count == info->done_count) { if (ipmi_entity_pointer_cb(info->ent_id, sensor_detect_handler, info)) detect_cleanup(info, NULL, ipmi_sensor_get_domain(sensor)); } else ipmi_unlock(info->lock); } static void detect_reading_read(ipmi_sensor_t *sensor, int err, enum ipmi_value_present_e value_present, unsigned int raw_val, double val, ipmi_states_t *states, void *cb_data) { ent_active_detect_t *info = cb_data; ipmi_lock(info->lock); if (!err && ipmi_is_sensor_scanning_enabled(states) && !ipmi_is_initial_update_in_progress(states)) info->present = 1; info->done_count++; if (info->try_count == info->done_count) { if (ipmi_entity_pointer_cb(info->ent_id, sensor_detect_handler, info)) detect_cleanup(info, NULL, ipmi_sensor_get_domain(sensor)); } else ipmi_unlock(info->lock); } static void sensor_detect_send(ipmi_entity_t *ent, ipmi_sensor_t *sensor, void *cb_data) { ent_active_detect_t *info = cb_data; int rv; if (ipmi_sensor_get_ignore_for_presence(sensor)) /* Sensor should be ignored for presence. */ return; info->try_count++; ipmi_unlock(info->lock); rv = ipmi_sensor_get_reading(sensor, detect_reading_read, info); if (rv) rv = ipmi_sensor_get_states(sensor, detect_states_read, info); ipmi_lock(info->lock); if (rv) info->try_count--; } static int try_presence_sensors(ipmi_entity_t *ent, ent_active_detect_t *info) { if (locked_list_num_entries(ent->sensors) == 0) return ENOSYS; /* Start the try count at 1 so the presence does not complete until after we are done here. */ info->try_count = 1; info->done_count = 0; ipmi_entity_iterate_sensors(ent, sensor_detect_send, info); /* I couldn't message any sensors, go on. */ if (info->try_count == 1) return ENOSYS; info->done_count++; if (info->try_count == info->done_count) sensor_detect_handler(ent, info); else ipmi_unlock(info->lock); return 0; } static void detect_no_presence_sensor_presence(ipmi_entity_t *ent) { ent_active_detect_t *detect; int rv; detect = ipmi_mem_alloc(sizeof(*detect)); if (!detect) { presence_finalize(ent, "detect_no_presence_sensor_presence"); return; } rv = ipmi_create_lock(ent->domain, &detect->lock); if (rv) { presence_finalize(ent, "detect_no_presence_sensor_presence(2)"); ipmi_mem_free(detect); return; } detect->start_presence_event_count = ent->presence_event_count; detect->ent_id = ipmi_entity_convert_to_id(ent); detect->present = 0; ipmi_lock(detect->lock); /* The successful one below will unlock the lock and free detect. */ if (! try_presence_sensors(ent, detect)) { /* Success with sensors, nothing to do */ } else if (! try_presence_controls(ent, detect)) { /* Note that controls are not technically part of the spec, but since we have them we use them for presence detection. */ /* Success with controls, nothing to do */ } else if (! try_presence_children(ent, detect)) { /* Success with children, nothing to do */ } else { try_presence_frudev(ent, detect); } } static void states_read(ipmi_sensor_t *sensor, int err, ipmi_states_t *states, void *cb_data) { int present; ipmi_entity_t *ent = cb_data; int val; int rv; if (err) { i_ipmi_domain_entity_lock(ent->domain); i_ipmi_entity_get(ent); i_ipmi_domain_entity_unlock(ent->domain); detect_no_presence_sensor_presence(ent); i_ipmi_entity_put(ent); return; } if (ipmi_is_initial_update_in_progress(states)) /* Sensor did not return valid values, the entity is probably not present. */ present = 0; else { rv = ipmi_sensor_discrete_event_readable(sensor, ent->presence_bit_offset, &val); if (rv || !val) /* The present bit is not supported, so use the absent bit. */ present = !ipmi_is_state_set(states, !ent->presence_bit_offset); else /* The present bit is supported. */ present = ipmi_is_state_set(states, ent->presence_bit_offset); } presence_changed(ent, present); presence_finalize(ent, "states_read"); } static void states_bit_read(ipmi_sensor_t *sensor, int err, ipmi_states_t *states, void *cb_data) { int present; ipmi_entity_t *ent = cb_data; if (err) { i_ipmi_domain_entity_lock(ent->domain); i_ipmi_entity_get(ent); i_ipmi_domain_entity_unlock(ent->domain); detect_no_presence_sensor_presence(ent); i_ipmi_entity_put(ent); return; } if (ipmi_is_initial_update_in_progress(states)) /* Sensor did not return valid values, the entity is probably not present. */ present = 0; else present = ipmi_is_state_set(states, ent->presence_bit_offset); presence_changed(ent, present); presence_finalize(ent, "states_bit_read"); } void ipmi_entity_set_presence_detector(ipmi_entity_t *entity, ipmi_entity_presence_detect_cb handler, void *handler_data) { entity->detect_presence = handler; entity->detect_presence_data = handler_data; entity->presence_possibly_changed = 1; } void ipmi_entity_detector_done(ipmi_entity_t *entity, int present) { presence_changed(entity, present); presence_finalize(entity, "entity_detector_done"); } static void ent_detect_presence_nolock(ipmi_entity_t *ent, void *cb_data) { ent_detect_info_t *info = cb_data; int rv; if (ent->in_presence_check || ((!info->force) && (! ent->presence_possibly_changed))) return; ent->presence_possibly_changed = 0; ent->in_presence_check = 1; if (ent->hot_swappable) { ent_unlock(ent); ipmi_entity_check_hot_swap_state(ent); ent_lock(ent); } i_ipmi_get_domain_fully_up(ent->domain, "ent_detect_presence"); if (ent->detect_presence) { ent_unlock(ent); rv = ent->detect_presence(ent, ent->detect_presence_data); if (rv) presence_finalize(ent, "ent_detect_presence(4)"); ent_lock(ent); } else if (ent->presence_sensor) { /* Presence sensor overrides everything. */ ipmi_sensor_id_t psi = ent->presence_sensor_id; ent_unlock(ent); rv = ipmi_sensor_id_get_states(psi, states_read, ent); if (rv) presence_finalize(ent, "ent_detect_presence(2)"); ent_lock(ent); } else if (ent->presence_bit_sensor) { /* Presence bit sensor overrides everything but a presence sensor. */ ipmi_sensor_id_t psi = ent->presence_bit_sensor_id; ent_unlock(ent); rv = ipmi_sensor_id_get_states(psi, states_bit_read, ent); if (rv) presence_finalize(ent, "ent_detect_presence(3)"); ent_lock(ent); } else { ent_unlock(ent); detect_no_presence_sensor_presence(ent); ent_lock(ent); } } static void ent_detect_presence(ipmi_entity_t *ent, void *cb_data) { ent_lock(ent); ent_detect_presence_nolock(ent, cb_data); ent_unlock(ent); } int ipmi_detect_ents_presence_changes(ipmi_entity_info_t *ents, int force) { ent_detect_info_t info; info.force = force; ipmi_entities_iterate_entities(ents, ent_detect_presence, &info); return 0; } int ipmi_detect_entity_presence_change(ipmi_entity_t *ent, int force) { ent_detect_info_t info; info.force = force; ent_detect_presence(ent, &info); return 0; } static void entity_mc_active(ipmi_mc_t *mc, int active, void *cb_data) { ipmi_entity_t *ent = cb_data; int rv; i_ipmi_domain_entity_lock(ent->domain); rv = i_ipmi_entity_get(ent); if (rv) { i_ipmi_domain_entity_unlock(ent->domain); return; } ent_lock(ent); ent->frudev_active_reported = 1; if (ent->frudev_active != active) { ent->frudev_active = active; /* Only detect with frudev if there are no other presence-detecting things there. */ if (ent_use_frudev_for_presence(ent)) { ent_detect_info_t info; ent_unlock(ent); i_ipmi_domain_entity_unlock(ent->domain); info.force = 1; ent_detect_presence(ent, &info); goto do_put; } } ent_unlock(ent); i_ipmi_domain_entity_unlock(ent->domain); do_put: i_ipmi_entity_put(ent); } /* Must be called with the entity lock held. May release and reclaim it. */ static void handle_new_presence_sensor(ipmi_entity_t *ent, ipmi_sensor_t *sensor) { ipmi_event_state_t events; int event_support; int rv; int val; ent->presence_sensor_id = ipmi_sensor_convert_to_id(sensor); ent->presence_sensor = sensor; /* If we have a presence sensor, remove the presence bit sensor. */ if (ent->presence_bit_sensor) { ent->presence_bit_sensor = NULL; ipmi_sensor_remove_discrete_event_handler(ent->presence_bit_sensor, presence_sensor_changed, ent); } /* * Unfortunately, the present/absent bits in the sensor-specific * verses the generic device presence types are backwards. */ if (ipmi_sensor_get_event_reading_type(sensor) == IPMI_EVENT_READING_TYPE_DISCRETE_DEVICE_PRESENCE) ent->presence_bit_offset = 1; else ent->presence_bit_offset = 0; event_support = ipmi_sensor_get_event_support(sensor); /* Add our own event handler. */ ipmi_sensor_add_discrete_event_handler(sensor, presence_sensor_changed, ent); /* Nothing to do, it will just be on. */ if (event_support == IPMI_EVENT_SUPPORT_GLOBAL_ENABLE) goto out; /* Turn events and scanning on. */ ipmi_event_state_init(&events); ipmi_event_state_set_events_enabled(&events, 1); ipmi_event_state_set_scanning_enabled(&events, 1); if (event_support == IPMI_EVENT_SUPPORT_PER_STATE) { /* Turn on all the event enables that we can. */ rv = ipmi_sensor_discrete_event_supported(sensor, 0, IPMI_ASSERTION, &val); if ((!rv) && (val)) ipmi_discrete_event_set(&events, 0, IPMI_ASSERTION); rv = ipmi_sensor_discrete_event_supported(sensor, 0, IPMI_DEASSERTION, &val); if ((!rv) && (val)) ipmi_discrete_event_set(&events, 0, IPMI_DEASSERTION); rv = ipmi_sensor_discrete_event_supported(sensor, 1, IPMI_ASSERTION, &val); if ((!rv) && (val)) ipmi_discrete_event_set(&events, 1, IPMI_ASSERTION); rv = ipmi_sensor_discrete_event_supported(sensor, 1, IPMI_DEASSERTION, &val); if ((!rv) && (val)) ipmi_discrete_event_set(&events, 1, IPMI_DEASSERTION); } ent_unlock(ent); ipmi_sensor_set_event_enables(sensor, &events, NULL, NULL); ent_lock(ent); out: ent->presence_possibly_changed = 1; if (ent->hs_cb.get_hot_swap_state == NULL) { /* Set the entity hot-swap capable and use our internal state machine. */ ipmi_entity_set_hot_swappable(ent, 1); ent->hs_cb = internal_hs_cb; } } /* Must be called with the entity lock held. May release and reclaim it. */ static void handle_new_presence_bit_sensor(ipmi_entity_t *ent, ipmi_sensor_t *sensor, int bit) { ipmi_event_state_t events; int event_support; ent->presence_bit_sensor = sensor; ent->presence_bit_offset = bit; ent->presence_bit_sensor_id = ipmi_sensor_convert_to_id(sensor); event_support = ipmi_sensor_get_event_support(sensor); /* Add our own event handler. */ ipmi_sensor_add_discrete_event_handler(sensor, presence_bit_sensor_changed, ent); /* Nothing to do, it will just be on. */ if (event_support == IPMI_EVENT_SUPPORT_GLOBAL_ENABLE) goto out; /* Turn events and scanning on. */ ipmi_event_state_init(&events); ipmi_event_state_set_events_enabled(&events, 1); ipmi_event_state_set_scanning_enabled(&events, 1); if (event_support == IPMI_EVENT_SUPPORT_PER_STATE) { int val; int rv; /* Turn on the event enables. */ rv = ipmi_sensor_discrete_event_supported (sensor, ent->presence_bit_offset, IPMI_ASSERTION, &val); if (!rv && val) ipmi_discrete_event_set(&events, ent->presence_bit_offset, IPMI_ASSERTION); rv = ipmi_sensor_discrete_event_supported (sensor, ent->presence_bit_offset, IPMI_DEASSERTION, &val); if (!rv && val) ipmi_discrete_event_set(&events, ent->presence_bit_offset, IPMI_DEASSERTION); } ent_unlock(ent); ipmi_sensor_enable_events(sensor, &events, NULL, NULL); ent_lock(ent); out: ent->presence_possibly_changed = 1; if (ent->hs_cb.get_hot_swap_state == NULL) { /* Set the entity hot-swap capable and use our internal state machine. */ ipmi_entity_set_hot_swappable(ent, 1); ent->hs_cb = internal_hs_cb; } } int ipmi_entity_add_fully_up_handler(ipmi_entity_t *ent, ipmi_entity_ptr_cb handler, void *cb_data) { CHECK_ENTITY_LOCK(ent); if (locked_list_add(ent->fully_up_handlers, handler, cb_data)) return 0; else return ENOMEM; } int ipmi_entity_remove_fully_up_handler(ipmi_entity_t *ent, ipmi_entity_ptr_cb handler, void *cb_data) { CHECK_ENTITY_LOCK(ent); if (locked_list_remove(ent->fully_up_handlers, handler, cb_data)) return 0; else return EINVAL; } int ipmi_entity_add_fully_up_handler_cl(ipmi_entity_t *ent, ipmi_entity_fully_up_cl_cb handler, void *cb_data) { CHECK_ENTITY_LOCK(ent); if (locked_list_add(ent->fully_up_handlers_cl, handler, cb_data)) return 0; else return ENOMEM; } int ipmi_entity_remove_fully_up_handler_cl(ipmi_entity_t *ent, ipmi_entity_fully_up_cl_cb handler, void *cb_data) { CHECK_ENTITY_LOCK(ent); if (locked_list_remove(ent->fully_up_handlers_cl, handler, cb_data)) return 0; else return EINVAL; } static int call_fully_up_handler(void *cb_data, void *item1, void *item2) { ipmi_entity_t *ent = cb_data; ipmi_entity_ptr_cb handler = item1; handler(ent, item2); return LOCKED_LIST_ITER_CONTINUE; } static void call_fully_up_handlers(ipmi_entity_t *ent) { locked_list_iterate(ent->fully_up_handlers, call_fully_up_handler, ent); } static void report_sdrs_read_check(ipmi_entity_t *ent, void *cb_data) { ent_lock(ent); ent->sdr_add_done = 1; ent_unlock(ent); } void i_ipmi_entities_report_sdrs_read(ipmi_entity_info_t *ents) { ipmi_entities_iterate_entities(ents, report_sdrs_read_check, NULL); } static void report_mcs_scanned_check(ipmi_entity_t *ent, void *cb_data) { ent_lock(ent); ent->frudev_active_reported = 1; ent_unlock(ent); } void i_ipmi_entities_report_mcs_scanned(ipmi_entity_info_t *ents) { ipmi_entities_iterate_entities(ents, report_mcs_scanned_check, NULL); } /*********************************************************************** * * Handling of sensor and control addition and removal. * **********************************************************************/ int ipmi_entity_add_sensor_update_handler(ipmi_entity_t *ent, ipmi_entity_sensor_cb handler, void *cb_data) { CHECK_ENTITY_LOCK(ent); if (locked_list_add(ent->sensor_handlers, handler, cb_data)) return 0; else return ENOMEM; } int ipmi_entity_remove_sensor_update_handler(ipmi_entity_t *ent, ipmi_entity_sensor_cb handler, void *cb_data) { CHECK_ENTITY_LOCK(ent); if (locked_list_remove(ent->sensor_handlers, handler, cb_data)) return 0; else return EINVAL; } int ipmi_entity_add_sensor_update_handler_cl(ipmi_entity_t *ent, ipmi_entity_sensor_cl_cb handler, void *cb_data) { CHECK_ENTITY_LOCK(ent); if (locked_list_add(ent->sensor_handlers_cl, handler, cb_data)) return 0; else return ENOMEM; } int ipmi_entity_remove_sensor_update_handler_cl(ipmi_entity_t *ent, ipmi_entity_sensor_cl_cb handler, void *cb_data) { CHECK_ENTITY_LOCK(ent); if (locked_list_remove(ent->sensor_handlers_cl, handler, cb_data)) return 0; else return EINVAL; } typedef struct sensor_handler_s { enum ipmi_update_e op; ipmi_sensor_t *sensor; ipmi_entity_t *entity; } sensor_handler_t; static int call_sensor_handler(void *cb_data, void *item1, void *item2) { sensor_handler_t *info = cb_data; ipmi_entity_sensor_cb handler = item1; handler(info->op, info->entity, info->sensor, item2); return LOCKED_LIST_ITER_CONTINUE; } void i_ipmi_entity_call_sensor_handlers(ipmi_entity_t *ent, ipmi_sensor_t *sensor, enum ipmi_update_e op) { sensor_handler_t info; /* If we are reporting things, make sure the entity they are attached to is already reported. */ i_ipmi_domain_entity_lock(ent->domain); if (ent->add_pending) { ent->add_pending = 0; i_ipmi_domain_entity_unlock(ent->domain); call_entity_update_handlers(ent, IPMI_ADDED); } else { i_ipmi_domain_entity_unlock(ent->domain); } info.op = op; info.entity = ent; info.sensor = sensor; locked_list_iterate(ent->sensor_handlers, call_sensor_handler, &info); } int ipmi_entity_add_control_update_handler(ipmi_entity_t *ent, ipmi_entity_control_cb handler, void *cb_data) { CHECK_ENTITY_LOCK(ent); if (locked_list_add(ent->control_handlers, handler, cb_data)) return 0; else return ENOMEM; } int ipmi_entity_remove_control_update_handler(ipmi_entity_t *ent, ipmi_entity_control_cb handler, void *cb_data) { CHECK_ENTITY_LOCK(ent); if (locked_list_remove(ent->control_handlers, handler, cb_data)) return 0; else return EINVAL; } int ipmi_entity_add_control_update_handler_cl(ipmi_entity_t *ent, ipmi_entity_control_cl_cb handler, void *cb_data) { CHECK_ENTITY_LOCK(ent); if (locked_list_add(ent->control_handlers_cl, handler, cb_data)) return 0; else return ENOMEM; } int ipmi_entity_remove_control_update_handler_cl(ipmi_entity_t *ent, ipmi_entity_control_cl_cb handler, void *cb_data) { CHECK_ENTITY_LOCK(ent); if (locked_list_remove(ent->control_handlers, handler, cb_data)) return 0; else return EINVAL; } typedef struct control_handler_s { enum ipmi_update_e op; ipmi_entity_t *entity; ipmi_control_t *control; } control_handler_t; static int call_control_handler(void *cb_data, void *item1, void *item2) { control_handler_t *info = cb_data; ipmi_entity_control_cb handler = item1; handler(info->op, info->entity, info->control, item2); return LOCKED_LIST_ITER_CONTINUE; } void i_ipmi_entity_call_control_handlers(ipmi_entity_t *ent, ipmi_control_t *control, enum ipmi_update_e op) { control_handler_t info; /* If we are reporting things, make sure the entity they are attached to is already reported. */ i_ipmi_domain_entity_lock(ent->domain); if (ent->add_pending) { ent->add_pending = 0; i_ipmi_domain_entity_unlock(ent->domain); call_entity_update_handlers(ent, IPMI_ADDED); } else { i_ipmi_domain_entity_unlock(ent->domain); } info.op = op; info.entity = ent; info.control = control; locked_list_iterate(ent->control_handlers, call_control_handler, &info); } static void handle_new_hot_swap_requester(ipmi_entity_t *ent, ipmi_sensor_t *sensor); static int is_hot_swap_requester(ipmi_sensor_t *sensor) { if (ipmi_sensor_get_event_reading_type(sensor) == IPMI_EVENT_READING_TYPE_THRESHOLD) return 0; return ipmi_sensor_is_hot_swap_requester(sensor, NULL, NULL); } static void handle_new_hot_swap_power(ipmi_entity_t *ent, ipmi_control_t *control); static int is_hot_swap_power(ipmi_control_t *control) { if (ipmi_control_get_type(control) != IPMI_CONTROL_POWER) return 0; if (ipmi_control_get_num_vals(control) != 1) return 0; return ipmi_control_is_hot_swap_power(control); } static void handle_new_hot_swap_indicator(ipmi_entity_t *ent, ipmi_control_t *control); static int is_hot_swap_indicator(ipmi_control_t *control) { if (ipmi_control_get_type(control) != IPMI_CONTROL_LIGHT) return 0; if (ipmi_control_get_num_vals(control) != 1) return 0; return ipmi_control_is_hot_swap_indicator(control, NULL, NULL, NULL, NULL); } static int is_presence_sensor(ipmi_sensor_t *sensor) { int val, rv; int supports_bit0 = 0; int supports_bit1 = 0; int reading_type; /* Is it the right type (a presence sensor)? */ if (ipmi_sensor_get_sensor_type(sensor) != 0x25) return 0; reading_type = ipmi_sensor_get_event_reading_type(sensor); if (reading_type != IPMI_EVENT_READING_TYPE_SENSOR_SPECIFIC && reading_type != IPMI_EVENT_READING_TYPE_DISCRETE_DEVICE_PRESENCE) /* Don't know how to interpret an other reading type codes. */ return 0; /* Presence sensors that don't generate events are kind of useless. */ if (ipmi_sensor_get_event_support(sensor) == IPMI_EVENT_SUPPORT_NONE) return 0; /* Check present bit */ rv = ipmi_sensor_discrete_event_readable(sensor, 0, &val); if ((!rv) && (val)) supports_bit0 = 1; /* Check absent bit. */ rv = ipmi_sensor_discrete_event_readable(sensor, 1, &val); if ((!rv) && (val)) supports_bit1 = 1; /* What good is this? No support for the proper bits, I need to be able to read them. */ if ((!supports_bit0) && (!supports_bit1)) return 0; return 1; } static int is_presence_bit_sensor(ipmi_sensor_t *sensor, int *bit_offset) { int val, rv; int bit; int sensor_type = ipmi_sensor_get_sensor_type(sensor); int reading_type = ipmi_sensor_get_event_reading_type(sensor); if (reading_type != IPMI_EVENT_READING_TYPE_SENSOR_SPECIFIC) /* All presence bits are in sensor-specific bits, not generic ones */ return 0; /* Is it a sensor with a presence bit? */ switch (sensor_type) { case IPMI_SENSOR_TYPE_POWER_SUPPLY: bit = 0; break; case IPMI_SENSOR_TYPE_BATTERY: bit = 2; break; case IPMI_SENSOR_TYPE_SLOT_CONNECTOR: bit = 2; break; case IPMI_SENSOR_TYPE_PROCESSOR: bit = 7; break; case IPMI_SENSOR_TYPE_DRIVE_SLOT: bit = 0; break; case IPMI_SENSOR_TYPE_MEMORY: bit = 6; break; default: return 0; } /* Presence sensors that don't generate events are kind of useless. */ if (ipmi_sensor_get_event_support(sensor) == IPMI_EVENT_SUPPORT_NONE) return 0; /* Check if the bit is available */ rv = ipmi_sensor_discrete_event_readable(sensor, bit, &val); if (rv || !val) return 0; *bit_offset = bit; return 1; } void ipmi_entity_add_sensor(ipmi_entity_t *ent, ipmi_sensor_t *sensor, void *link) { int bit; CHECK_ENTITY_LOCK(ent); ent_lock(ent); if (is_presence_sensor(sensor) && (ent->presence_sensor == NULL) && (ent->presence_bit_sensor == NULL)) { /* It's the presence sensor and we don't already have one. We keep this special. */ handle_new_presence_sensor(ent, sensor); } else if ((ent->presence_sensor == NULL) && (ent->presence_bit_sensor == NULL) && is_presence_bit_sensor(sensor, &bit)) { /* If it's a sensor with a presence bit, we use it. */ handle_new_presence_bit_sensor(ent, sensor, bit); } if (is_hot_swap_requester(sensor) && (ent->hot_swap_requester == NULL)) { handle_new_hot_swap_requester(ent, sensor); } ent_unlock(ent); locked_list_add_entry(ent->sensors, sensor, NULL, link); ent->presence_possibly_changed = 1; } typedef struct sens_find_presence_s { int is_presence; int bit; ipmi_sensor_t *sensor; ipmi_sensor_t *ignore_sensor; } sens_cmp_info_t; static void sens_cmp_if_presence(ipmi_entity_t *ent, ipmi_sensor_t *sensor, void *cb_data) { sens_cmp_info_t *info = cb_data; if (sensor == info->ignore_sensor) return; if (info->is_presence) return; info->is_presence = is_presence_sensor(sensor); if (info->is_presence) { info->sensor = sensor; handle_new_presence_sensor(ent, sensor); } } static void sens_cmp_if_presence_bit(ipmi_entity_t *ent, ipmi_sensor_t *sensor, void *cb_data) { sens_cmp_info_t *info = cb_data; if (sensor == info->ignore_sensor) return; if (info->is_presence) return; info->is_presence = is_presence_bit_sensor(sensor, &info->bit); if (info->is_presence) { info->sensor = sensor; handle_new_presence_bit_sensor(ent, sensor, info->bit); } } /* Must be called with the entity lock held. May release and reclaim it. */ static void check_for_another_presence_sensor(ipmi_entity_t *ent, ipmi_sensor_t *old) { sens_cmp_info_t info; info.sensor = NULL; info.ignore_sensor = old; info.is_presence = 0; /* See if there is another presence sensor. */ ipmi_entity_iterate_sensors(ent, sens_cmp_if_presence, &info); if (! info.sensor) { /* See if there is a presence bit sensor. */ ent->presence_sensor = NULL; info.ignore_sensor = NULL; info.is_presence = 0; ipmi_entity_iterate_sensors(ent, sens_cmp_if_presence_bit, &info); } } void ipmi_entity_remove_sensor(ipmi_entity_t *ent, ipmi_sensor_t *sensor) { /* Note that you *CANNOT* call ipmi_sensor_convert_to_id() (or any other thing like that) because the MC that the sensor belongs to may have disappeared already. So be careful. */ CHECK_ENTITY_LOCK(ent); ent_lock(ent); if (sensor == ent->presence_sensor) { ent->presence_sensor = NULL; ent->presence_possibly_changed = 1; check_for_another_presence_sensor(ent, sensor); } else if (sensor == ent->presence_bit_sensor) { ent->presence_bit_sensor = NULL; ent->presence_possibly_changed = 1; check_for_another_presence_sensor(ent, sensor); } if (sensor == ent->hot_swap_requester) { ent->hot_swap_requester = NULL; } ent_unlock(ent); if (! locked_list_remove(ent->sensors, sensor, NULL)) { ipmi_log(IPMI_LOG_WARNING, "%sentity.c(ipmi_entity_remove_sensor):" " Removal of a sensor from an entity was requested," " but the sensor was not there", SENSOR_NAME(sensor)); return; } } void ipmi_entity_add_control(ipmi_entity_t *ent, ipmi_control_t *control, void *link) { CHECK_ENTITY_LOCK(ent); ent_lock(ent); if (is_hot_swap_power(control)) handle_new_hot_swap_power(ent, control); if (is_hot_swap_indicator(control)) handle_new_hot_swap_indicator(ent, control); ent_unlock(ent); locked_list_add_entry(ent->controls, control, NULL, link); ent->presence_possibly_changed = 1; } void ipmi_entity_remove_control(ipmi_entity_t *ent, ipmi_control_t *control) { /* Note that you *CANNOT* call ipmi_control_convert_to_id() (or any other thing like that) because the MC that the sensor belongs to may have disappeared already. So be careful. */ CHECK_ENTITY_LOCK(ent); ent_lock(ent); if (control == ent->hot_swap_power) { /* If don't have power control, we can't manage hot-swap. */ ipmi_entity_set_supports_managed_hot_swap(ent, 0); ent->hot_swap_power = NULL; } if (control == ent->hot_swap_indicator) ent->hot_swap_indicator = NULL; ent_unlock(ent); if (! locked_list_remove(ent->controls, control, NULL)) { ipmi_log(IPMI_LOG_WARNING, "%sentity.c(ipmi_entity_remove_control):" " Removal of a control from an entity was requested," " but the control was not there", CONTROL_NAME(control)); return; } ent->presence_possibly_changed = 1; } typedef struct iterate_sensor_info_s { ipmi_entity_t *ent; ipmi_entity_iterate_sensor_cb handler; void *cb_data; int got_failed; } iterate_sensor_info_t; static int iterate_sensor_prefunc(void *cb_data, void *item1, void *item2) { iterate_sensor_info_t *info = cb_data; ipmi_sensor_t *sensor = item1; int rv; ipmi_mc_t *mc = ipmi_sensor_get_mc(sensor); ipmi_domain_t *domain; if (!mc) goto out_err; domain = ipmi_mc_get_domain(mc); i_ipmi_domain_mc_lock(domain); rv = i_ipmi_mc_get(mc); i_ipmi_domain_mc_unlock(domain); if (rv) goto out_err; rv = i_ipmi_sensor_get(sensor); if (rv) { i_ipmi_mc_put(mc); goto out_err; } info->got_failed = 0; return LOCKED_LIST_ITER_CONTINUE; out_err: info->got_failed = 1; return LOCKED_LIST_ITER_CONTINUE; } static int iterate_sensor_handler(void *cb_data, void *item1, void *item2) { iterate_sensor_info_t *info = cb_data; ipmi_sensor_t *sensor = item1; ipmi_mc_t *mc = ipmi_sensor_get_mc(sensor); if (info->got_failed) goto out; info->handler(info->ent, item1, info->cb_data); i_ipmi_sensor_put(sensor); i_ipmi_mc_put(mc); out: return LOCKED_LIST_ITER_CONTINUE; } void ipmi_entity_iterate_sensors(ipmi_entity_t *ent, ipmi_entity_iterate_sensor_cb handler, void *cb_data) { iterate_sensor_info_t info = { ent, handler, cb_data }; CHECK_ENTITY_LOCK(ent); locked_list_iterate_prefunc(ent->sensors, iterate_sensor_prefunc, iterate_sensor_handler, &info); } typedef struct iterate_control_info_s { ipmi_entity_t *ent; ipmi_entity_iterate_control_cb handler; void *cb_data; int got_failed; } iterate_control_info_t; static int iterate_control_prefunc(void *cb_data, void *item1, void *item2) { iterate_control_info_t *info = cb_data; ipmi_control_t *control = item1; int rv; ipmi_mc_t *mc = ipmi_control_get_mc(control); ipmi_domain_t *domain = ipmi_mc_get_domain(mc); if (!mc) goto out_err; i_ipmi_domain_mc_lock(domain); rv = i_ipmi_mc_get(mc); i_ipmi_domain_mc_unlock(domain); if (rv) goto out_err; rv = i_ipmi_control_get(control); if (rv) { i_ipmi_mc_put(mc); goto out_err; } info->got_failed = 0; return LOCKED_LIST_ITER_CONTINUE; out_err: info->got_failed = 1; return LOCKED_LIST_ITER_CONTINUE; } static int iterate_control_handler(void *cb_data, void *item1, void *item2) { iterate_control_info_t *info = cb_data; ipmi_control_t *control = item1; ipmi_mc_t *mc = ipmi_control_get_mc(control); if (info->got_failed) goto out; info->handler(info->ent, item1, info->cb_data); i_ipmi_control_put(control); i_ipmi_mc_put(mc); out: return LOCKED_LIST_ITER_CONTINUE; } void ipmi_entity_iterate_controls(ipmi_entity_t *ent, ipmi_entity_iterate_control_cb handler, void *cb_data) { iterate_control_info_t info = { ent, handler, cb_data }; CHECK_ENTITY_LOCK(ent); locked_list_iterate_prefunc(ent->controls, iterate_control_prefunc, iterate_control_handler, &info); } /*********************************************************************** * * Handling of sensor data records for entities. * **********************************************************************/ static int decode_ear(ipmi_sdr_t *sdr, dlr_info_t *info, ipmi_mc_t *mc) { int i; int pos; info->type = IPMI_ENTITY_EAR; info->output_handler = NULL; info->device_num.channel = 0; info->device_num.address = 0; info->entity_id = sdr->data[0]; info->entity_instance = sdr->data[1]; info->linked_ear_exists = (sdr->data[2] & 0x40) == 0x40; info->presence_sensor_always_there = (sdr->data[2] & 0x20) == 0x20; info->is_ranges = (sdr->data[2] & 0x80) == 0x80; for (i=0,pos=3; pos<11; pos+=2,i++) { info->contained_entities[i].entity_id = sdr->data[pos]; info->contained_entities[i].entity_instance = sdr->data[pos+1]; } return 0; } static int decode_drear(ipmi_sdr_t *sdr, dlr_info_t *info, ipmi_mc_t *mc) { int i; int pos; info->type = IPMI_ENTITY_DREAR; info->output_handler = NULL; info->entity_id = sdr->data[0]; info->entity_instance = sdr->data[1]; if (sdr->data[1] >= 0x60) { info->device_num.channel = sdr->data[3] >> 4; info->device_num.address = sdr->data[2]; } info->linked_ear_exists = (sdr->data[4] & 0x40) == 0x40; info->presence_sensor_always_there = (sdr->data[4] & 0x20) == 0x20; info->is_ranges = (sdr->data[4] & 0x80) == 0x80; for (i=0,pos=5; pos<21; pos+=4,i++) { if (sdr->data[pos+3] >= 0x60) { info->contained_entities[i].device_num.address = sdr->data[pos]; info->contained_entities[i].device_num.channel = sdr->data[pos+1]; } info->contained_entities[i].entity_id = sdr->data[pos+2]; info->contained_entities[i].entity_instance = sdr->data[pos+3]; } return 0; } static int gdlr_output(ipmi_entity_t *ent, ipmi_sdr_info_t *sdrs, void *cb_data) { ipmi_sdr_t sdr; unsigned int len; dlr_info_t *info = &ent->info; memset(&sdr, 0, sizeof(sdr)); sdr.major_version = IPMI_MAJOR_NUM_SDR; sdr.minor_version = IPMI_MINOR_NUM_SDR; sdr.type = IPMI_SDR_GENERIC_DEVICE_LOCATOR_RECORD; sdr.length = 10; /* We'll fix it later. */ sdr.data[0] = info->access_address; sdr.data[1] = (info->slave_address | (info->channel >> 3)); sdr.data[2] = ((info->channel << 5) | (info->lun << 3) | info->private_bus_id); sdr.data[3] = info->address_span & 0x7; sdr.data[4] = 0; sdr.data[5] = info->device_type; sdr.data[6] = info->device_type_modifier; sdr.data[7] = info->entity_id; sdr.data[8] = info->entity_instance; sdr.data[9] = info->oem; len = 16; ipmi_set_device_string(info->id, info->id_type, info->id_len, sdr.data+10, 0, &len); sdr.length += len; return ipmi_sdr_add(sdrs, &sdr); } static int decode_gdlr(ipmi_sdr_t *sdr, dlr_info_t *info, ipmi_mc_t *mc) { unsigned char *str; int rv; info->type = IPMI_ENTITY_GENERIC; info->output_handler = gdlr_output; if (sdr->data[8] >= 0x60) { info->device_num.channel = (sdr->data[2] >> 5) | ((sdr->data[1] << 3) & 0x08); info->device_num.address = sdr->data[0]; } else { info->device_num.channel = 0; info->device_num.address = 0; } info->access_address = sdr->data[0]; info->slave_address = sdr->data[1]; info->channel = ((sdr->data[2] >> 5) | ((sdr->data[1] << 3) & 0x08)); info->lun = (sdr->data[2] >> 3) & 0x3; info->private_bus_id = sdr->data[2] & 0x7; info->address_span = sdr->data[3] & 0x7; info->device_type = sdr->data[5]; info->device_type_modifier = sdr->data[6]; info->entity_id = sdr->data[7]; info->entity_instance = sdr->data[8]; info->oem = sdr->data[9]; str = sdr->data + 10; rv = ipmi_get_device_string(&str, sdr->length-10, info->id, IPMI_STR_SDR_SEMANTICS, 0, &info->id_type, ENTITY_ID_LEN, &info->id_len); if (rv) { ipmi_log(IPMI_LOG_WARNING, "%sentity.c(decode_gdlr):" " Error getting device ID string from SDR record %d: %d," " this entity will be named **INVALID**", MC_NAME(mc), sdr->record_id, rv); strncpy(info->id, "**INVALID**", sizeof(info->id)); info->id_len = strlen(info->id); info->id_type = IPMI_ASCII_STR; rv = 0; } return rv; } static int frudlr_output(ipmi_entity_t *ent, ipmi_sdr_info_t *sdrs, void *cb_data) { ipmi_sdr_t sdr; unsigned int len; dlr_info_t *info = &ent->info; memset(&sdr, 0, sizeof(sdr)); sdr.major_version = IPMI_MAJOR_NUM_SDR; sdr.minor_version = IPMI_MINOR_NUM_SDR; sdr.type = IPMI_SDR_FRU_DEVICE_LOCATOR_RECORD; sdr.length = 10; /* We'll fix it later. */ sdr.data[0] = info->access_address; sdr.data[1] = info->fru_device_id; sdr.data[2] = ((info->is_logical_fru << 7) | (info->lun << 3) | info->private_bus_id); sdr.data[3] = info->channel << 4; sdr.data[4] = 0; sdr.data[5] = info->device_type; sdr.data[6] = info->device_type_modifier; sdr.data[7] = info->entity_id; sdr.data[8] = info->entity_instance; sdr.data[9] = info->oem; len = 16; ipmi_set_device_string(info->id, info->id_type, info->id_len, sdr.data+10, 0, &len); sdr.length += len; return ipmi_sdr_add(sdrs, &sdr); } static int decode_frudlr(ipmi_sdr_t *sdr, dlr_info_t *info, ipmi_mc_t *mc) { unsigned char *str; int rv; info->type = IPMI_ENTITY_FRU; info->output_handler = frudlr_output; if (sdr->data[8] >= 0x60) { info->device_num.channel = sdr->data[3] >> 4; info->device_num.address = sdr->data[0]; } else { info->device_num.channel = 0; info->device_num.address = 0; } info->access_address = sdr->data[0]; info->fru_device_id = sdr->data[1]; info->channel = sdr->data[3] >> 4; info->is_logical_fru = ((sdr->data[2] & 0x80) == 0x80); info->lun = (sdr->data[2] >> 3) & 0x3; info->private_bus_id = sdr->data[2] & 0x7; info->device_type = sdr->data[5]; info->device_type_modifier = sdr->data[6]; info->oem = sdr->data[9]; info->entity_id = sdr->data[7]; info->entity_instance = sdr->data[8]; str = sdr->data + 10; rv = ipmi_get_device_string(&str, sdr->length-10, info->id, IPMI_STR_SDR_SEMANTICS, 0, &info->id_type, ENTITY_ID_LEN, &info->id_len); if (rv) { ipmi_log(IPMI_LOG_WARNING, "%sentity.c(decode_frudlr):" " Error getting device ID string from SDR record %d: %d," " this entity will be named **INVALID**", MC_NAME(mc), sdr->record_id, rv); strncpy(info->id, "**INVALID**", sizeof(info->id)); info->id_len = strlen(info->id); info->id_type = IPMI_ASCII_STR; rv = 0; } return rv; } static int mcdlr_output(ipmi_entity_t *ent, ipmi_sdr_info_t *sdrs, void *cb_data) { ipmi_sdr_t sdr; unsigned int len; dlr_info_t *info = &ent->info; memset(&sdr, 0, sizeof(sdr)); sdr.major_version = IPMI_MAJOR_NUM_SDR; sdr.minor_version = IPMI_MINOR_NUM_SDR; sdr.type = IPMI_SDR_MC_DEVICE_LOCATOR_RECORD; sdr.length = 10; /* We'll fix it later. */ sdr.data[0] = info->slave_address; sdr.data[1] = info->channel & 0xf; sdr.data[2] = ((info->ACPI_system_power_notify_required << 7) | (info->ACPI_device_power_notify_required << 6) | (info->controller_logs_init_agent_errors << 3) | (info->log_init_agent_errors_accessing << 2) | (info->global_init)); sdr.data[3] = ((info->chassis_device << 7) | (info->bridge << 6) | (info->IPMB_event_generator << 5) | (info->IPMB_event_receiver << 4) | (info->FRU_inventory_device << 3) | (info->SEL_device << 2) | (info->SDR_repository_device << 1) | info->sensor_device); sdr.data[4] = 0; sdr.data[5] = 0; sdr.data[6] = 0; sdr.data[7] = ent->key.entity_id; sdr.data[8] = ent->key.entity_instance; sdr.data[9] = info->oem; len = 16; ipmi_set_device_string(info->id, info->id_type, info->id_len, sdr.data+10, 0, &len); sdr.length += len; return ipmi_sdr_add(sdrs, &sdr); } static int decode_mcdlr(ipmi_sdr_t *sdr, dlr_info_t *info, ipmi_mc_t *mc) { unsigned char *data; unsigned char *str; int rv; info->type = IPMI_ENTITY_MC; info->output_handler = mcdlr_output; if (sdr->data[8] >= 0x60) { info->device_num.channel = sdr->data[1] & 0xf; info->device_num.address = sdr->data[0]; } else { info->device_num.channel = 0; info->device_num.address = 0; } data = sdr->data; info->slave_address = *data; data++; if (sdr->major_version == 1 && sdr->minor_version == 0) { /* IPMI 1.0 SDR type 12 record, doesn't have the channel field, so we have to have special handling. */ info->channel = 0; } else { info->channel = *data & 0xf; data++; } info->ACPI_system_power_notify_required = (data[0] >> 7) & 1; info->ACPI_device_power_notify_required = (data[0] >> 6) & 1; info->controller_logs_init_agent_errors = (data[0] >> 3) & 1; info->log_init_agent_errors_accessing = (data[0] >> 2) & 1; info->global_init = (data[0] >> 0) & 3; info->chassis_device = (data[1] >> 7) & 1; info->bridge = (data[1] >> 6) & 1; info->IPMB_event_generator = (data[1] >> 5) & 1; info->IPMB_event_receiver = (data[1] >> 4) & 1; info->FRU_inventory_device = (data[1] >> 3) & 1; info->SEL_device = (data[1] >> 2) & 1; info->SDR_repository_device = (data[1] >> 1) & 1; info->sensor_device = (data[1] >> 0) & 1; /* We switch back to referring to sdr->data here, because the rest of the offsets are the same in 1.0 and 1.5. Only the power state and device capabilities change between the two version. */ info->entity_id = sdr->data[7]; info->entity_instance = sdr->data[8]; info->oem = sdr->data[9]; str = sdr->data + 10; rv = ipmi_get_device_string(&str, sdr->length-10, info->id, IPMI_STR_SDR_SEMANTICS, 0, &info->id_type, ENTITY_ID_LEN, &info->id_len); if (rv) { ipmi_log(IPMI_LOG_WARNING, "%sentity.c(decode_mcdlr):" " Error getting device ID string from SDR record %d: %d," " this entity will be named **INVALID**", MC_NAME(mc), sdr->record_id, rv); strncpy(info->id, "**INVALID**", sizeof(info->id)); info->id_len = strlen(info->id); info->id_type = IPMI_ASCII_STR; rv = 0; } /* Make sure the FRU fetch stuff works. */ info->access_address = info->slave_address; info->fru_device_id = 0; info->is_logical_fru = 1; info->private_bus_id = 0; return rv; } typedef struct entity_found_s { int found; ipmi_entity_t *ent; ipmi_entity_t **cent; unsigned int cent_next; unsigned int cent_len; } entity_found_t; typedef struct entity_sdr_info_s { ipmi_entity_info_t *ents; unsigned int len; /* array size */ unsigned int next; /* next member to use. */ entity_found_t *found; /* bools and info used for comparing. */ dlr_info_t **dlrs; } entity_sdr_info_t; static int add_sdr_info(entity_sdr_info_t *infos, dlr_info_t *dlr) { dlr_info_t *new_dlr; if (infos->len == infos->next) { /* Need to expand the array. */ unsigned int new_length = infos->len + 5; dlr_info_t **new_dlrs; entity_found_t *new_found; new_dlrs = ipmi_mem_alloc(sizeof(dlr_info_t *) * new_length); if (!new_dlrs) return ENOMEM; new_found = ipmi_mem_alloc(sizeof(entity_found_t) * new_length); if (!new_found) { ipmi_mem_free(new_dlrs); return ENOMEM; } if (infos->dlrs) { memcpy(new_dlrs, infos->dlrs, sizeof(dlr_info_t *) * infos->len); memcpy(new_found, infos->found, sizeof(entity_found_t) * infos->len); ipmi_mem_free(infos->dlrs); ipmi_mem_free(infos->found); } memset(new_found + infos->len, 0, sizeof(entity_found_t) * (new_length - infos->len)); infos->dlrs = new_dlrs; infos->found = new_found; infos->len = new_length; } new_dlr = ipmi_mem_alloc(sizeof(*new_dlr)); if (!new_dlr) return ENOMEM; memcpy(new_dlr, dlr, sizeof(*new_dlr)); infos->dlrs[infos->next] = new_dlr; infos->next++; return 0; } static void destroy_sdr_info(entity_sdr_info_t *infos) { unsigned int i; if (infos->dlrs) { for (i=0; inext; i++) { if (infos->found[i].cent) ipmi_mem_free(infos->found[i].cent); } for (i=0; inext; i++) ipmi_mem_free(infos->dlrs[i]); ipmi_mem_free(infos->dlrs); ipmi_mem_free(infos->found); } } static void cleanup_sdr_info(entity_sdr_info_t *infos) { unsigned int i; if (infos->dlrs) { for (i=0; inext; i++) { if (infos->found[i].cent) ipmi_mem_free(infos->found[i].cent); infos->found[i].cent = NULL; infos->found[i].cent_len = 0; infos->found[i].cent_next = 0; } } } static int add_child_ent_to_found(entity_found_t *found, ipmi_entity_t *ent) { if (found->cent_next == found->cent_len) { int new_len = found->cent_len + 4; ipmi_entity_t **new_cent; new_cent = ipmi_mem_alloc(sizeof(ipmi_entity_t *) * new_len); if (!new_cent) return ENOMEM; if (found->cent) { memcpy(new_cent, found->cent, sizeof(ipmi_entity_t *) * found->cent_len); ipmi_mem_free(found->cent); } found->cent = new_cent; found->cent_len = new_len; } found->cent[found->cent_next] = ent; found->cent_next++; return 0; } /* Find all the entities for unfound dlrs and make sure there is room in the proper child and parent lists for the new parents/children. */ static int fill_in_entities(ipmi_entity_info_t *ents, entity_sdr_info_t *infos) { entity_found_t *found; unsigned int i, j; int rv; ipmi_entity_t *child; ipmi_entity_t *ent; for (i=0; inext; i++) { found = infos->found+i; if (found->found) continue; if (infos->dlrs[i]->entity_id) { i_ipmi_domain_entity_lock(ents->domain); rv = entity_add(ents, infos->dlrs[i]->device_num, infos->dlrs[i]->entity_id, infos->dlrs[i]->entity_instance, infos->dlrs[i]->output_handler, NULL, &found->ent); if (rv) goto out_err; } else { /* If entity id is null, it should be ignored. */ found->ent = NULL; continue; } if ((infos->dlrs[i]->type != IPMI_ENTITY_EAR) && (infos->dlrs[i]->type != IPMI_ENTITY_DREAR)) continue; /* Find the first previous unfound entry that has the same entity as me to add the contained entities to. This means that every unfound entity will only have one set of contained entities in the cent array even if it has multiple DLRs. It will always be in the first entry. */ if (i > 0) { j = i - 1; ent = found->ent; for (; ent == (infos->found+j)->ent; j--) { if ((infos->found+j)->found) goto next_ent; if ((infos->dlrs[j]->type != IPMI_ENTITY_EAR) && (infos->dlrs[j]->type != IPMI_ENTITY_DREAR)) goto next_ent; found = infos->found+j; /* Since this is an EAR and we are putting it's entries in another place, ignore this one. */ (infos->found+i)->found = 1; next_ent: if (j == 0) break; } } if (infos->dlrs[i]->is_ranges) { for (j=0; j<4; j+=2) { dlr_ref_t *cent1 = infos->dlrs[i]->contained_entities+j; dlr_ref_t *cent2 = infos->dlrs[i]->contained_entities+j+1; int k; if (cent1->entity_id == 0) continue; for (k=cent1->entity_instance; k<=cent2->entity_instance; k++){ i_ipmi_domain_entity_lock(ents->domain); rv = entity_add(ents, cent1->device_num, cent1->entity_id, k, NULL, NULL, &child); if (rv) goto out_err; rv = add_child_ent_to_found(found, child); if (rv) { i_ipmi_entity_put(child); goto out_err; } } } } else { for (j=0; j<4; j++) { dlr_ref_t *cent = infos->dlrs[i]->contained_entities+j; if (cent->entity_id == 0) continue; i_ipmi_domain_entity_lock(ents->domain); rv = entity_add(ents, cent->device_num, cent->entity_id, cent->entity_instance, NULL, NULL, &child); if (rv) return rv; rv = add_child_ent_to_found(found, child); if (rv) { i_ipmi_entity_put(child); goto out_err; } } } } return 0; out_err: return rv; } static void put_entities(entity_sdr_info_t *infos) { entity_found_t *found; unsigned int i, j; for (i=0; inext; i++) { found = infos->found+i; if (found->ent) i_ipmi_entity_put(found->ent); /* Still put the entity even if found, as it was refcounted by looking it up. */ if (found->found) continue; for (j=0; jcent_next; j++) i_ipmi_entity_put(found->cent[j]); } } static int cmp_dlr(const dlr_info_t *d1, const dlr_info_t *d2) { if (d1->entity_id < d2->entity_id) return -1; if (d1->entity_id > d2->entity_id) return 1; if (d1->entity_instance < d2->entity_instance) return -1; if (d1->entity_instance > d2->entity_instance) return 1; return memcmp(d1, d2, sizeof(dlr_info_t)); } static int cmp_dlr_qsort(const void *a, const void *b) { const dlr_info_t *d1 = *((dlr_info_t **) a); const dlr_info_t *d2 = *((dlr_info_t **) b); return cmp_dlr(d1, d2); } struct locked_list_entry_s { locked_list_entry_t *next; }; int ipmi_entity_scan_sdrs(ipmi_domain_t *domain, ipmi_mc_t *mc, ipmi_entity_info_t *ents, ipmi_sdr_info_t *sdrs) { unsigned int count; unsigned int i, j; int rv; entity_sdr_info_t infos; entity_sdr_info_t *old_infos; entity_found_t *found; locked_list_entry_t *entries = NULL, *entry; memset(&infos, 0, sizeof(infos)); rv = ipmi_get_sdr_count(sdrs, &count); if (rv) return rv; for (i=0; ients = ents; i_ipmi_set_sdr_entities(domain, mc, old_infos); } /* Clear out all the temporary found information we use for scanning. */ if (old_infos->next > 0) memset(old_infos->found, 0, sizeof(entity_found_t) * old_infos->next); if (infos.next > 0) memset(infos.found, 0, sizeof(entity_found_t) * infos.next); /* Sort the DLRs by parent entity id/entity instance/rest of data. This makes the rest of the operations here O(n) instead of O(n^2). */ qsort(infos.dlrs, infos.next, sizeof(dlr_info_t *), cmp_dlr_qsort); /* For every item in the new array, try to find it in the old array. Both arrays are sorted by entity id/entity instance/rest of data, so this is O(n). */ i=0; j=0; while ((i < infos.next) && (j < old_infos->next)) { int c = cmp_dlr(infos.dlrs[i], old_infos->dlrs[j]); if (c == 0) { infos.found[i].found = 1; old_infos->found[j].found = 1; i++; j++; } else if (c < 0) i++; else j++; } /* For every item in the array that is not found, make sure the entities exists and we have them. */ rv = fill_in_entities(ents, &infos); if (rv) goto out_err_unlock; rv = fill_in_entities(ents, old_infos); if (rv) goto out_err_unlock; /* Now ensure space is in each parent for all the children and each child's parent entry. */ for (i=0; inext = entries; entries = entry; } } /* After this, the operation cannot fail, since we have gotten all the objects we need and we have allocated enough entries for the parent and child lists. */ i_ipmi_domain_entity_lock(domain); rv = 0; /* Destroy all the old information that was not in the new version of the SDRs. */ for (i=0; inext; i++) { found = old_infos->found + i; if (found->found) continue; if (!found->ent) continue; if ((old_infos->dlrs[i]->type != IPMI_ENTITY_EAR) && (old_infos->dlrs[i]->type != IPMI_ENTITY_DREAR)) { /* A real DLR, decrement the refcount, and destroy the info. */ found->ent->ref_count--; memset(&found->ent->info, 0, sizeof(dlr_info_t)); } else { /* It's an EAR, so handling removing the children. */ for (j=0; jcent_next; j++) ipmi_entity_remove_child_internal(found->ent, found->cent[j]); } } /* Add all the new information that was in the new SDRs. */ for (i=0; ifound) continue; if (!found->ent) continue; if ((infos.dlrs[i]->type != IPMI_ENTITY_EAR) && (infos.dlrs[i]->type != IPMI_ENTITY_DREAR)) { uint8_t ipmb = 0xff; int channel = -1; found->ent->changed = 1; /* A real DLR, increment the refcount, and copy the info. */ found->ent->ref_count++; /* Don't fetch FRU information until present. */ /* Set up the MC information for the device. */ if (infos.dlrs[i]->type == IPMI_ENTITY_FRU) { channel = infos.dlrs[i]->channel; ipmb = infos.dlrs[i]->access_address; memcpy(&found->ent->pending_info, infos.dlrs[i], sizeof(dlr_info_t)); found->ent->pending_info_ready = 1; } else if (infos.dlrs[i]->type == IPMI_ENTITY_MC) { if (infos.dlrs[i]->FRU_inventory_device) { channel = infos.dlrs[i]->channel; ipmb = infos.dlrs[i]->access_address; memcpy(&found->ent->pending_info, infos.dlrs[i], sizeof(dlr_info_t)); found->ent->pending_info_ready = 1; } else { if ((!found->ent->info.FRU_inventory_device) && (!found->ent->pending_info.FRU_inventory_device)) { /* We prefer to only keep the information from the FRU inventory device MCDLR. */ memcpy(&found->ent->pending_info, infos.dlrs[i], sizeof(dlr_info_t)); found->ent->pending_info_ready = 1; } /* Go ahead and scan the MC if we don't do anything else with this data. */ ipmi_start_ipmb_mc_scan(domain, infos.dlrs[i]->channel, infos.dlrs[i]->access_address, infos.dlrs[i]->access_address, NULL, NULL); } } else { memcpy(&found->ent->pending_info, infos.dlrs[i], sizeof(dlr_info_t)); found->ent->pending_info_ready = 1; } /* If we can use the FRU device presence to detect whether the entity is present, we register the monitor with the appropriate management controller to see if it is active and base presence off of that, if no other presence detection capability is there. */ if (ipmb == 0) { /* Not a valid IPMB, just ignore it. */ } else if ((channel != -1) && (infos.dlrs[i]->entity_id)) { ipmi_mc_t *mc; /* Attempt to create the MC. */ rv = i_ipmi_find_or_create_mc_by_slave_addr (domain, channel, ipmb, &mc); if (rv) { ipmi_log(IPMI_LOG_SEVERE, "%sentity.c(ipmi_entity_scan_sdrs):" " Could not add MC for MCDLR or FRUDLR," " error %x", ENTITY_NAME(found->ent), rv); } else if (found->ent->frudev_present) { if (found->ent->frudev_mc != mc) { ipmi_log(IPMI_LOG_WARNING, "%sentity.c(ipmi_entity_scan_sdrs):" " Entity has two different MCs in" " different SDRs, only using the first" " for presence. MCs are %s and %s", ENTITY_NAME(found->ent), MC_NAME(found->ent->frudev_mc), MC_NAME(mc)); } i_ipmi_mc_put(mc); } else { rv = ipmi_mc_add_active_handler(mc, entity_mc_active, found->ent); if (rv) { ipmi_log(IPMI_LOG_SEVERE, "%sentity.c(ipmi_entity_scan_sdrs):" " Could not add an MC active handler for" " MCDLR or FRUDLR," " error %x", ENTITY_NAME(found->ent), rv); } else { i_ipmi_mc_use(mc); found->ent->frudev_present = 1; found->ent->frudev_active = ipmi_mc_is_active(mc); found->ent->frudev_mc = mc; found->ent->presence_possibly_changed = 1; } i_ipmi_mc_put(mc); } } } else { /* It's an EAR, so handling adding the children. */ for (j=0; jcent_next; j++) { entry = entries; entries = entry->next->next; add_child(found->ent, found->cent[j], entry, entry->next); found->ent->changed = 1; found->cent[j]->changed = 1; } } } infos.ents = ents; i_ipmi_domain_entity_unlock(domain); put_entities(&infos); put_entities(old_infos); destroy_sdr_info(old_infos); cleanup_sdr_info(&infos); memcpy(old_infos, &infos, sizeof(infos)); out: while (entries) { entry = entries; entries = entry->next; locked_list_free_entry(entry); } return rv; out_err_unlock: put_entities(&infos); put_entities(old_infos); out_err_unlock_nocleaninfos: i_ipmi_domain_entity_unlock(domain); out_err: destroy_sdr_info(&infos); goto out; } int ipmi_sdr_entity_destroy(void *info) { entity_sdr_info_t *infos = info; unsigned int i, j; int rv; ipmi_entity_t *ent, *child; for (i=0; inext; i++) { i_ipmi_domain_entity_lock(infos->ents->domain); rv = entity_find(infos->ents, infos->dlrs[i]->device_num, infos->dlrs[i]->entity_id, infos->dlrs[i]->entity_instance, &ent); i_ipmi_domain_entity_unlock(infos->ents->domain); if (rv) continue; if ((infos->dlrs[i]->type != IPMI_ENTITY_EAR) && (infos->dlrs[i]->type != IPMI_ENTITY_DREAR)) { if (ent->frudev_present) { ipmi_mc_t *mc = ent->frudev_mc; i_ipmi_domain_mc_lock(infos->ents->domain); i_ipmi_mc_get(mc); i_ipmi_domain_mc_unlock(infos->ents->domain); ipmi_mc_remove_active_handler(ent->frudev_mc, entity_mc_active, ent); i_ipmi_mc_release(ent->frudev_mc); i_ipmi_mc_put(mc); ent->frudev_mc = NULL; ent->frudev_present = 0; } ent->ref_count--; } else { if (infos->dlrs[i]->is_ranges) { for (j=0; j<4; j+=2) { dlr_ref_t *cent1 = infos->dlrs[i]->contained_entities+j; dlr_ref_t *cent2 = infos->dlrs[i]->contained_entities+j+1; int k; if (cent1->entity_id == 0) continue; for (k=cent1->entity_instance; k<=cent2->entity_instance; k++) { i_ipmi_domain_entity_lock(infos->ents->domain); rv = entity_find(infos->ents, cent1->device_num, cent1->entity_id, k, &child); i_ipmi_domain_entity_unlock(infos->ents->domain); if (rv) continue; ipmi_entity_remove_child(ent, child); i_ipmi_entity_put(child); } } } else { for (j=0; j<4; j++) { dlr_ref_t *cent = infos->dlrs[i]->contained_entities+j; if (cent->entity_id == 0) continue; i_ipmi_domain_entity_lock(infos->ents->domain); rv = entity_find(infos->ents, cent->device_num, cent->entity_id, cent->entity_instance, &child); i_ipmi_domain_entity_unlock(infos->ents->domain); if (rv) continue; ipmi_entity_remove_child(ent, child); i_ipmi_entity_put(child); } } ipmi_detect_entity_presence_change(ent, 0); } i_ipmi_entity_put(ent); } destroy_sdr_info(info); ipmi_mem_free(info); return 0; } /*********************************************************************** * * SDR output code. * **********************************************************************/ #if SAVE_SDR_CODE_ENABLE typedef struct sdr_append_info_s { int err; ipmi_entity_info_t *ents; ipmi_sdr_info_t *sdrs; } sdr_append_info_t; /* For sorting by entity ID/entity instance. */ static int cmp_entities(void *item1, void *item2) { ipmi_entity_t *ent1 = item1; ipmi_entity_t *ent2 = item2; if (ent1->info.entity_id < ent2->info.entity_id) return -1; if (ent1->info.entity_id > ent2->info.entity_id) return 1; if (ent1->info.entity_instance < ent2->info.entity_instance) return -1; if (ent1->info.entity_instance > ent2->info.entity_instance) return 1; return 0; } static int do_ear_output(ipmi_sdr_info_t *sdrs, ipmi_sdr_t *sdr, ipmi_entity_t *(ents[]), int is_range, int other_entries, int len) { int pos; int rv; int old_flags; int old_flags_pos; int i; if (sdr->type == IPMI_SDR_ENTITY_ASSOCIATION_RECORD) { /* not device-relative */ memset(sdr->data+3, 0, 8); old_flags = sdr->data[2]; old_flags_pos = 2; if (is_range) sdr->data[2] |= 1 << 7; if (other_entries) sdr->data[2] |= 1 << 6; pos = 3; for (i=0; idata[pos] = ents[i]->info.entity_id; pos++; sdr->data[pos] = ents[i]->info.entity_instance; pos++; } } else { /* device-relative */ memset(sdr->data+5, 0, 16); old_flags = sdr->data[4]; old_flags_pos = 4; if (is_range) sdr->data[4] |= 1 << 7; if (other_entries) sdr->data[4] |= 1 << 6; pos = 5; for (i=0; idata[pos] = ents[i]->info.device_num.address; pos++; sdr->data[pos] = ents[i]->info.device_num.channel; pos++; sdr->data[pos] = ents[i]->info.entity_id; pos++; sdr->data[pos] = ents[i]->info.entity_instance; pos++; } } rv = ipmi_sdr_add(sdrs, sdr); /* Restore the original value of the flags field. */ sdr->data[old_flags_pos] = old_flags; return rv; } static int output_child_ears(ipmi_entity_t *ent, ipmi_sdr_info_t *sdrs) { ipmi_sdr_t sdr; int prev_inst; ipmi_entity_t *curr, *next, *last; int curr_dlr_entry = 0; int is_range = 0; ipmi_entity_t *(ents[4]); ilist_iter_t iter; int rv; if (ilist_empty(ent->child_entities)) return 0; memset(&sdr, 0, sizeof(sdr)); sdr.major_version = IPMI_MAJOR_NUM_SDR; sdr.minor_version = IPMI_MINOR_NUM_SDR; sdr.data[0] = ent->info.entity_id; sdr.data[1] = ent->info.entity_instance; if ((sdr.major_version == 1) && (sdr.minor_version < 5)) { /* IPMI 1.0, we can olny use normal entity association records */ sdr.type = IPMI_SDR_ENTITY_ASSOCIATION_RECORD; sdr.length = 11; sdr.data[2] = (ent->info.presence_sensor_always_there << 5); } else { /* IPMI 1.5, we only use the device-relative EARs. */ sdr.type = IPMI_SDR_DR_ENTITY_ASSOCIATION_RECORD; sdr.length = 27; sdr.data[2] = ent->info.slave_address; sdr.data[3] = ent->info.channel; sdr.data[4] = (ent->info.presence_sensor_always_there << 5); } ilist_sort(ent->child_entities, cmp_entities); ilist_init_iter(&iter, ent->child_entities); last = NULL; if (ilist_first(&iter)) next = ilist_get(&iter); else next = NULL; while (next) { curr = next; prev_inst = curr->info.entity_instance; if (ilist_next(&iter)) next = ilist_get(&iter); else next = NULL; while (next && (next->info.entity_id == curr->info.entity_id) && (next->info.entity_instance == prev_inst+1)) { last = next; if (ilist_next(&iter)) next = ilist_get(&iter); else next = NULL; prev_inst++; } if (prev_inst > curr->info.entity_instance) { /* We have a range. */ if ((curr_dlr_entry > 0) && (!is_range)) { rv = do_ear_output(sdrs, &sdr, ents, is_range, 1, curr_dlr_entry); if (rv) return rv; } is_range = 1; ents[curr_dlr_entry] = curr; ents[curr_dlr_entry+1] = last; curr_dlr_entry += 2; } else { /* Not a range. */ if ((curr_dlr_entry > 0) && (is_range)) { rv = do_ear_output(sdrs, &sdr, ents, is_range, 1, curr_dlr_entry); if (rv) return rv; } is_range = 0; ents[curr_dlr_entry] = curr; curr_dlr_entry++; } if (curr_dlr_entry >= 4) { rv = do_ear_output(sdrs, &sdr, ents, is_range, next != NULL, curr_dlr_entry); if (rv) return rv; curr_dlr_entry = 0; } } return 0; } static void ent_sdr_append_handler(ipmi_entity_t *ent, void *cb_data) { sdr_append_info_t *info = cb_data; if (info->err) return; if (ent->sdr_gen_output) info->err = ent->sdr_gen_output(ent, info->sdrs, ent->sdr_gen_cb_data); if (!info->err) info->err = output_child_ears(ent, info->sdrs); } int ipmi_entity_append_to_sdrs(ipmi_entity_info_t *ents, ipmi_sdr_info_t *sdrs) { sdr_append_info_t info = { 0, ents, sdrs }; ipmi_entities_iterate_entities(ents, ent_sdr_append_handler, &info); return info.err; } #endif /*********************************************************************** * * Get/set all the various entity values. * **********************************************************************/ ipmi_domain_t * ipmi_entity_get_domain(ipmi_entity_t *ent) { CHECK_ENTITY_LOCK(ent); return ent->domain; } int ipmi_entity_get_access_address(ipmi_entity_t *ent) { CHECK_ENTITY_LOCK(ent); return ent->info.access_address; } void ipmi_entity_set_access_address(ipmi_entity_t *ent, int access_address) { CHECK_ENTITY_LOCK(ent); ent->info.access_address = access_address; } void ipmi_entity_set_physical_slot_num(ipmi_entity_t *ent, int present, unsigned int val) { CHECK_ENTITY_LOCK(ent); ent->slot_num = val; ent->slot_num_present = present; } int ipmi_entity_get_physical_slot_num(ipmi_entity_t *ent, unsigned int *slot_num) { CHECK_ENTITY_LOCK(ent); if (ent->slot_num_present) { *slot_num = ent->slot_num; return 0; } else return ENOSYS; } int ipmi_entity_get_slave_address(ipmi_entity_t *ent) { CHECK_ENTITY_LOCK(ent); return ent->info.slave_address; } void ipmi_entity_set_slave_address(ipmi_entity_t *ent, int slave_address) { CHECK_ENTITY_LOCK(ent); ent->info.slave_address = slave_address; } int ipmi_entity_get_channel(ipmi_entity_t *ent) { CHECK_ENTITY_LOCK(ent); return ent->info.channel; } void ipmi_entity_set_channel(ipmi_entity_t *ent, int channel) { CHECK_ENTITY_LOCK(ent); ent->info.channel = channel; } int ipmi_entity_get_mc_id(ipmi_entity_t *ent, ipmi_mcid_t *mc_id) { ipmi_ipmb_addr_t sa; ipmi_mc_t *mc; if ((ent->info.type != IPMI_ENTITY_MC) && (ent->info.type != IPMI_ENTITY_GENERIC)) { return ENOSYS; } sa.addr_type = IPMI_IPMB_ADDR_TYPE; sa.channel = ent->info.channel; sa.slave_addr = ent->info.slave_address; sa.lun = ent->info.lun; mc = i_ipmi_find_mc_by_addr(ent->domain, (ipmi_addr_t *) &sa, sizeof(sa)); if (!mc) return ENODEV; *mc_id = ipmi_mc_convert_to_id(mc); i_ipmi_mc_put(mc); return 0; } int ipmi_entity_get_lun(ipmi_entity_t *ent) { CHECK_ENTITY_LOCK(ent); return ent->info.lun; } void ipmi_entity_set_lun(ipmi_entity_t *ent, int lun) { CHECK_ENTITY_LOCK(ent); ent->info.lun = lun; } int ipmi_entity_get_private_bus_id(ipmi_entity_t *ent) { CHECK_ENTITY_LOCK(ent); return ent->info.private_bus_id; } void ipmi_entity_set_private_bus_id(ipmi_entity_t *ent, int private_bus_id) { CHECK_ENTITY_LOCK(ent); ent->info.private_bus_id = private_bus_id; } int ipmi_entity_get_is_logical_fru(ipmi_entity_t *ent) { CHECK_ENTITY_LOCK(ent); return ent->info.is_logical_fru; } void ipmi_entity_set_is_logical_fru(ipmi_entity_t *ent, int is_logical_fru) { CHECK_ENTITY_LOCK(ent); ent->info.is_logical_fru = is_logical_fru; } int ipmi_entity_get_fru_device_id(ipmi_entity_t *ent) { CHECK_ENTITY_LOCK(ent); return ent->info.fru_device_id; } void ipmi_entity_set_fru_device_id(ipmi_entity_t *ent, int fru_device_id) { CHECK_ENTITY_LOCK(ent); ent->info.fru_device_id = fru_device_id; } int ipmi_entity_get_is_fru(ipmi_entity_t *ent) { int rv = 0; CHECK_ENTITY_LOCK(ent); ent_lock(ent); if (ent->info.type == IPMI_ENTITY_FRU) rv = 1; if ((ent->info.type == IPMI_ENTITY_MC) && (ent->info.FRU_inventory_device)) rv = 1; ent_unlock(ent); return rv; } int ipmi_entity_get_is_mc(ipmi_entity_t *ent) { CHECK_ENTITY_LOCK(ent); return ent->info.type == IPMI_ENTITY_MC; } enum ipmi_dlr_type_e ipmi_entity_get_type(ipmi_entity_t *ent) { CHECK_ENTITY_LOCK(ent); return ent->info.type; } void ipmi_entity_set_type(ipmi_entity_t *ent, enum ipmi_dlr_type_e type) { CHECK_ENTITY_LOCK(ent); ent->info.type = type; } int ipmi_entity_get_entity_id(ipmi_entity_t *ent) { CHECK_ENTITY_LOCK(ent); return ent->key.entity_id; } int ipmi_entity_get_entity_instance(ipmi_entity_t *ent) { CHECK_ENTITY_LOCK(ent); return ent->key.entity_instance; } int ipmi_entity_get_device_channel(ipmi_entity_t *ent) { CHECK_ENTITY_LOCK(ent); return ent->key.device_num.channel; } int ipmi_entity_get_device_address(ipmi_entity_t *ent) { CHECK_ENTITY_LOCK(ent); return ent->key.device_num.address; } int ipmi_entity_get_device_type(ipmi_entity_t *ent) { CHECK_ENTITY_LOCK(ent); return ent->info.device_type; } void ipmi_entity_set_device_type(ipmi_entity_t *ent, int device_type) { CHECK_ENTITY_LOCK(ent); ent->info.device_type = device_type; } int ipmi_entity_get_device_modifier(ipmi_entity_t *ent) { CHECK_ENTITY_LOCK(ent); return ent->info.device_type_modifier; } void ipmi_entity_set_device_modifier(ipmi_entity_t *ent, int device_modifier) { CHECK_ENTITY_LOCK(ent); ent->info.device_type_modifier = device_modifier; } int ipmi_entity_get_oem(ipmi_entity_t *ent) { CHECK_ENTITY_LOCK(ent); return ent->info.oem; } void ipmi_entity_set_oem(ipmi_entity_t *ent, int oem) { CHECK_ENTITY_LOCK(ent); ent->info.oem = oem; } int ipmi_entity_get_address_span(ipmi_entity_t *ent) { CHECK_ENTITY_LOCK(ent); return ent->info.address_span; } void ipmi_entity_set_address_span(ipmi_entity_t *ent, int address_span) { CHECK_ENTITY_LOCK(ent); ent->info.address_span = address_span; } int ipmi_entity_get_id_length(ipmi_entity_t *ent) { CHECK_ENTITY_LOCK(ent); if (ent->info.id_type == IPMI_ASCII_STR) return ent->info.id_len+1; else return ent->info.id_len; } enum ipmi_str_type_e ipmi_entity_get_id_type(ipmi_entity_t *ent) { CHECK_ENTITY_LOCK(ent); return ent->info.id_type; } int ipmi_entity_get_id(ipmi_entity_t *ent, char *id, int length) { int clen; CHECK_ENTITY_LOCK(ent); ent_lock(ent); if ((int)ent->info.id_len > length) clen = length; else clen = ent->info.id_len; memcpy(id, ent->info.id, clen); if (ent->info.id_type == IPMI_ASCII_STR) { /* NIL terminate the ASCII string. */ if (clen == length) clen--; id[clen] = '\0'; } ent_unlock(ent); return clen; } void ipmi_entity_set_id(ipmi_entity_t *ent, char *id, enum ipmi_str_type_e type, int length) { CHECK_ENTITY_LOCK(ent); if (length > ENTITY_ID_LEN) length = ENTITY_ID_LEN; ent_lock(ent); memcpy(ent->info.id, id, length); ent->info.id_type = type; ent->info.id_len = length; ent_unlock(ent); entity_set_name(ent); } int ipmi_entity_get_presence_sensor_always_there(ipmi_entity_t *ent) { CHECK_ENTITY_LOCK(ent); return ent->info.presence_sensor_always_there; } void ipmi_entity_set_presence_sensor_always_there(ipmi_entity_t *ent, int val) { CHECK_ENTITY_LOCK(ent); ent->info.presence_sensor_always_there = val; } int ipmi_entity_get_ACPI_system_power_notify_required(ipmi_entity_t *ent) { CHECK_ENTITY_LOCK(ent); return ent->info.ACPI_system_power_notify_required; } void ipmi_entity_set_ACPI_system_power_notify_required(ipmi_entity_t *ent, int val) { CHECK_ENTITY_LOCK(ent); ent->info.ACPI_system_power_notify_required = val; } int ipmi_entity_get_ACPI_device_power_notify_required(ipmi_entity_t *ent) { CHECK_ENTITY_LOCK(ent); return ent->info.ACPI_device_power_notify_required; } void ipmi_entity_set_ACPI_device_power_notify_required(ipmi_entity_t *ent, int val) { CHECK_ENTITY_LOCK(ent); ent->info.ACPI_device_power_notify_required = val; } int ipmi_entity_get_controller_logs_init_agent_errors(ipmi_entity_t *ent) { CHECK_ENTITY_LOCK(ent); return ent->info.controller_logs_init_agent_errors; } void ipmi_entity_set_controller_logs_init_agent_errors(ipmi_entity_t *ent, int val) { CHECK_ENTITY_LOCK(ent); ent->info.controller_logs_init_agent_errors = val; } int ipmi_entity_get_log_init_agent_errors_accessing(ipmi_entity_t *ent) { CHECK_ENTITY_LOCK(ent); return ent->info.log_init_agent_errors_accessing; } void ipmi_entity_set_log_init_agent_errors_accessing(ipmi_entity_t *ent, int val) { CHECK_ENTITY_LOCK(ent); ent->info.log_init_agent_errors_accessing = val; } int ipmi_entity_get_global_init(ipmi_entity_t *ent) { CHECK_ENTITY_LOCK(ent); return ent->info.global_init; } void ipmi_entity_set_global_init(ipmi_entity_t *ent, int val) { CHECK_ENTITY_LOCK(ent); ent->info.global_init = val; } int ipmi_entity_get_chassis_device(ipmi_entity_t *ent) { CHECK_ENTITY_LOCK(ent); return ent->info.chassis_device; } void ipmi_entity_set_chassis_device(ipmi_entity_t *ent, int val) { CHECK_ENTITY_LOCK(ent); ent->info.chassis_device = val; } int ipmi_entity_get_bridge(ipmi_entity_t *ent) { CHECK_ENTITY_LOCK(ent); return ent->info.bridge; } void ipmi_entity_set_bridge(ipmi_entity_t *ent, int val) { CHECK_ENTITY_LOCK(ent); ent->info.bridge = val; } int ipmi_entity_get_IPMB_event_generator(ipmi_entity_t *ent) { CHECK_ENTITY_LOCK(ent); return ent->info.IPMB_event_generator; } void ipmi_entity_set_IPMB_event_generator(ipmi_entity_t *ent, int val) { CHECK_ENTITY_LOCK(ent); ent->info.IPMB_event_generator = val; } int ipmi_entity_get_IPMB_event_receiver(ipmi_entity_t *ent) { CHECK_ENTITY_LOCK(ent); return ent->info.IPMB_event_receiver; } void ipmi_entity_set_IPMB_event_receiver(ipmi_entity_t *ent, int val) { CHECK_ENTITY_LOCK(ent); ent->info.IPMB_event_receiver = val; } int ipmi_entity_get_FRU_inventory_device(ipmi_entity_t *ent) { CHECK_ENTITY_LOCK(ent); return ent->info.FRU_inventory_device; } void ipmi_entity_set_FRU_inventory_device(ipmi_entity_t *ent, int val) { CHECK_ENTITY_LOCK(ent); ent->info.FRU_inventory_device = val; } int ipmi_entity_get_SEL_device(ipmi_entity_t *ent) { CHECK_ENTITY_LOCK(ent); return ent->info.SEL_device; } void ipmi_entity_set_SEL_device(ipmi_entity_t *ent, int val) { CHECK_ENTITY_LOCK(ent); ent->info.SEL_device = val; } int ipmi_entity_get_SDR_repository_device(ipmi_entity_t *ent) { CHECK_ENTITY_LOCK(ent); return ent->info.SDR_repository_device; } void ipmi_entity_set_SDR_repository_device(ipmi_entity_t *ent, int val) { CHECK_ENTITY_LOCK(ent); ent->info.SDR_repository_device = val; } int ipmi_entity_get_sensor_device(ipmi_entity_t *ent) { CHECK_ENTITY_LOCK(ent); return ent->info.sensor_device; } void ipmi_entity_set_sensor_device(ipmi_entity_t *ent, int val) { CHECK_ENTITY_LOCK(ent); ent->info.sensor_device = val; } int ipmi_entity_get_is_child(ipmi_entity_t *ent) { CHECK_ENTITY_LOCK(ent); return locked_list_num_entries(ent->parent_entities) != 0; } int ipmi_entity_get_is_parent(ipmi_entity_t *ent) { CHECK_ENTITY_LOCK(ent); return locked_list_num_entries(ent->child_entities) != 0; } int ipmi_entity_is_present(ipmi_entity_t *ent) { CHECK_ENTITY_LOCK(ent); return ent->present; } static void entity_id_is_present_cb(ipmi_entity_t *ent, void *cb_data) { *((int *) cb_data) = ipmi_entity_is_present(ent); } int ipmi_entity_id_is_present(ipmi_entity_id_t id, int *present) { return ipmi_entity_pointer_cb(id, entity_id_is_present_cb, present); } const char * ipmi_entity_get_entity_id_string(ipmi_entity_t *ent) { CHECK_ENTITY_LOCK(ent); return ent->entity_id_string; } void ipmi_entity_set_entity_id_string(ipmi_entity_t *ent, char *str) { CHECK_ENTITY_LOCK(ent); ent->entity_id_string = str; } /*********************************************************************** * * Handle conversions between entity_ids and pointers. * **********************************************************************/ typedef struct iterate_entity_info_s { ipmi_entity_info_t *ents; ipmi_entities_iterate_entity_cb handler; void *cb_data; } iterate_entity_info_t; static int iterate_entity_handler(void *cb_data, void *item1, void *item2) { iterate_entity_info_t *info = cb_data; ipmi_entity_t *ent = item1; info->handler(ent, info->cb_data); i_ipmi_entity_put(ent); return LOCKED_LIST_ITER_CONTINUE; } void ipmi_entities_iterate_entities(ipmi_entity_info_t *ents, ipmi_entities_iterate_entity_cb handler, void *cb_data) { iterate_entity_info_t info = { ents, handler, cb_data }; locked_list_iterate_prefunc(ents->entities, iterate_entity_prefunc, iterate_entity_handler, &info); } ipmi_entity_id_t ipmi_entity_convert_to_id(ipmi_entity_t *ent) { ipmi_entity_id_t val; CHECK_ENTITY_LOCK(ent); val.domain_id = ent->domain_id; val.entity_id = ent->key.entity_id; val.entity_instance = ent->key.entity_instance; val.channel = ent->key.device_num.channel; val.address = ent->key.device_num.address; val.seq = ent->seq; return val; } typedef struct mc_cb_info_s { ipmi_entity_ptr_cb handler; void *cb_data; ipmi_entity_id_t id; int err; int ignore_seq; } mc_cb_info_t; static void domain_cb(ipmi_domain_t *domain, void *cb_data) { ipmi_device_num_t device_num; ipmi_entity_t *ent; mc_cb_info_t *info = cb_data; device_num.channel = info->id.channel; device_num.address = info->id.address; i_ipmi_domain_entity_lock(domain); info->err = entity_find(ipmi_domain_get_entities(domain), device_num, info->id.entity_id, info->id.entity_instance, &ent); i_ipmi_domain_entity_unlock(domain); if (!info->ignore_seq && !info->err) { if (ent->seq != info->id.seq) { info->err = EINVAL; i_ipmi_entity_put(ent); } } if (!info->err) { info->handler(ent, info->cb_data); i_ipmi_entity_put(ent); } } int ipmi_entity_pointer_cb(ipmi_entity_id_t id, ipmi_entity_ptr_cb handler, void *cb_data) { int rv; mc_cb_info_t info; info.handler = handler; info.cb_data = cb_data; info.id = id; info.err = 0; info.ignore_seq = 0; rv = ipmi_domain_pointer_cb(id.domain_id, domain_cb, &info); if (!rv) rv = info.err; return rv; } static int ipmi_entity_pointer_cb_noseq(ipmi_entity_id_t id, ipmi_entity_ptr_cb handler, void *cb_data) { int rv; mc_cb_info_t info; info.handler = handler; info.cb_data = cb_data; info.id = id; info.err = 0; info.ignore_seq = 1; rv = ipmi_domain_pointer_cb(id.domain_id, domain_cb, &info); if (!rv) rv = info.err; return rv; } static void get_seq(ipmi_entity_t *entity, void *cb_data) { ipmi_entity_id_t *id = cb_data; *id = ipmi_entity_convert_to_id(entity); } int ipmi_entity_find_id(ipmi_domain_id_t domain_id, int entity_id, int entity_instance, int channel, int slave_address, ipmi_entity_id_t *id) { int rv; id->domain_id = domain_id; id->entity_id = entity_id; id->entity_instance = entity_instance; id->channel = channel; id->address = slave_address; rv = ipmi_entity_pointer_cb_noseq(*id, get_seq, id); return rv; } int ipmi_cmp_entity_id(ipmi_entity_id_t id1, ipmi_entity_id_t id2) { int cmp; cmp = ipmi_cmp_domain_id(id1.domain_id, id2.domain_id); if (cmp) return cmp; if (id1.entity_id < id2.entity_id) return -1; if (id1.entity_id > id2.entity_id) return 1; if (id1.entity_instance < id2.entity_instance) return -1; if (id1.entity_instance > id2.entity_instance) return 1; if (id1.channel < id2.channel) return -1; if (id1.channel > id2.channel) return 1; if (id1.address < id2.address) return -1; if (id1.address > id2.address) return 1; if (id1.seq < id2.seq) return -1; if (id1.seq > id2.seq) return 1; return 0; } void ipmi_entity_id_set_invalid(ipmi_entity_id_t *id) { ipmi_domain_id_set_invalid(&id->domain_id); } int ipmi_entity_id_is_invalid(const ipmi_entity_id_t *id) { return (id->domain_id.domain == NULL); } #ifdef IPMI_CHECK_LOCKS void i__ipmi_check_entity_lock(const ipmi_entity_t *entity) { if (!entity) return; if (!DEBUG_LOCKS) return; if (entity->usecount == 0) ipmi_report_lock_error(entity->os_hnd, "entity not locked when it should have been"); } #endif /*********************************************************************** * * Entity FRU data handling. * **********************************************************************/ int ipmi_entity_add_fru_update_handler(ipmi_entity_t *ent, ipmi_entity_fru_cb handler, void *cb_data) { CHECK_ENTITY_LOCK(ent); if (locked_list_add(ent->fru_handlers, handler, cb_data)) return 0; else return ENOMEM; } int ipmi_entity_remove_fru_update_handler(ipmi_entity_t *ent, ipmi_entity_fru_cb handler, void *cb_data) { CHECK_ENTITY_LOCK(ent); if (locked_list_remove(ent->fru_handlers, handler, cb_data)) return 0; else return EINVAL; } int ipmi_entity_add_fru_update_handler_cl(ipmi_entity_t *ent, ipmi_entity_fru_cl_cb handler, void *cb_data) { CHECK_ENTITY_LOCK(ent); if (locked_list_add(ent->fru_handlers_cl, handler, cb_data)) return 0; else return ENOMEM; } int ipmi_entity_remove_fru_update_handler_cl(ipmi_entity_t *ent, ipmi_entity_fru_cl_cb handler, void *cb_data) { CHECK_ENTITY_LOCK(ent); if (locked_list_remove(ent->fru_handlers_cl, handler, cb_data)) return 0; else return EINVAL; } int ipmi_entity_add_fru_update_werr_handler(ipmi_entity_t *ent, ipmi_entity_fru_werr_cb handler, void *cb_data) { CHECK_ENTITY_LOCK(ent); if (locked_list_add(ent->fru_handlers_werr, handler, cb_data)) return 0; else return ENOMEM; } int ipmi_entity_remove_fru_update_werr_handler(ipmi_entity_t *ent, ipmi_entity_fru_werr_cb handler, void *cb_data) { CHECK_ENTITY_LOCK(ent); if (locked_list_remove(ent->fru_handlers_werr, handler, cb_data)) return 0; else return EINVAL; } int ipmi_entity_add_fru_update_werr_handler_cl(ipmi_entity_t *ent, ipmi_entity_fru_werr_cl_cb handler, void *cb_data) { CHECK_ENTITY_LOCK(ent); if (locked_list_add(ent->fru_handlers_werr_cl, handler, cb_data)) return 0; else return ENOMEM; } int ipmi_entity_remove_fru_update_werr_handler_cl(ipmi_entity_t *ent, ipmi_entity_fru_werr_cl_cb handler, void *cb_data) { CHECK_ENTITY_LOCK(ent); if (locked_list_remove(ent->fru_handlers_werr_cl, handler, cb_data)) return 0; else return EINVAL; } typedef struct fru_handler_s { enum ipmi_update_werr_e op; int err; ipmi_entity_t *entity; } fru_handler_t; static int call_fru_handler(void *cb_data, void *item1, void *item2) { fru_handler_t *info = cb_data; ipmi_entity_fru_cb handler = item1; handler(info->op, info->entity, item2); return LOCKED_LIST_ITER_CONTINUE; } static int call_fru_handler_werr(void *cb_data, void *item1, void *item2) { fru_handler_t *info = cb_data; ipmi_entity_fru_werr_cb handler = item1; handler(info->op, info->err, info->entity, item2); return LOCKED_LIST_ITER_CONTINUE; } void i_ipmi_entity_call_fru_handlers(ipmi_entity_t *ent, enum ipmi_update_werr_e op, int err) { fru_handler_t info; info.op = op; info.err = err; info.entity = ent; locked_list_iterate(ent->fru_handlers_werr, call_fru_handler_werr, &info); if (op == IPMIE_ERROR) /* Old handler doesn't handle error value. */ info.op = IPMI_CHANGED; locked_list_iterate(ent->fru_handlers, call_fru_handler, &info); } typedef struct fru_ent_info_s { ipmi_entity_id_t ent_id; ipmi_entity_ptr_cb done; void *cb_data; ipmi_fru_t *fru; int err; } fru_ent_info_t; static void fru_fetched_ent_cb(ipmi_entity_t *ent, void *cb_data) { fru_ent_info_t *info = cb_data; if (!info->err) { enum ipmi_update_werr_e op; ipmi_fru_t *ofru = ent->fru; ent->fru = info->fru; if (ofru) { op = IPMIE_CHANGED; ipmi_fru_destroy_internal(ofru, NULL, NULL); } else { op = IPMIE_ADDED; } i_ipmi_entity_call_fru_handlers(ent, op, 0); } else { ipmi_log(IPMI_LOG_WARNING, "%sentity.c(fru_fetched_ent_cb):" "Error fetching entity %d.%d FRU: %x", ENTITY_NAME(ent), ent->key.entity_id, ent->key.entity_instance, info->err); if ((ent->fru) && (info->fru)) /* Keep the old FRU on errors. */ ipmi_fru_destroy_internal(info->fru, NULL, NULL); else /* Keep it if we got it, it might have some useful information. */ ent->fru = info->fru; i_ipmi_entity_call_fru_handlers(ent, IPMIE_ERROR, info->err); } if (info->done) info->done(ent, info->cb_data); } static void fru_fetched_handler(ipmi_domain_t *domain, ipmi_fru_t *fru, int err, void *cb_data) { fru_ent_info_t *info = cb_data; int rv; info->fru = fru; info->err = err; rv = ipmi_entity_pointer_cb(info->ent_id, fru_fetched_ent_cb, info); if (rv) { /* If we can't put the fru someplace, just destroy it. */ ipmi_fru_destroy_internal(fru, NULL, NULL); if (info->done) info->done(NULL, info->cb_data); } ipmi_mem_free(info); if (domain) i_ipmi_put_domain_fully_up(domain, "fru_fetched_handler"); } int ipmi_entity_fetch_frus_cb(ipmi_entity_t *ent, ipmi_entity_ptr_cb done, void *cb_data) { fru_ent_info_t *info; int rv; if (! ipmi_option_FRUs(ent->domain)) return ENOSYS; info = ipmi_mem_alloc(sizeof(*info)); if (!info) return ENOMEM; info->ent_id = ipmi_entity_convert_to_id(ent); info->done = done; info->cb_data = cb_data; /* fetch the FRU information. */ i_ipmi_get_domain_fully_up(ent->domain, "ipmi_entity_fetch_frus_cb"); rv = ipmi_fru_alloc_notrack(ent->domain, ent->info.is_logical_fru, ent->info.access_address, ent->info.fru_device_id, ent->info.lun, ent->info.private_bus_id, ent->info.channel, IPMI_FRU_ALL_AREA_MASK, fru_fetched_handler, info, NULL); if (rv) { ipmi_mem_free(info); ipmi_log(IPMI_LOG_WARNING, "%sentity.c(ipmi_entity_fetch_frus_cb):" " Unable to allocate the FRU: %x", ENTITY_NAME(ent), rv); i_ipmi_put_domain_fully_up(ent->domain, "ipmi_entity_fetch_frus_cb"); } return rv; } int ipmi_entity_fetch_frus(ipmi_entity_t *ent) { return ipmi_entity_fetch_frus_cb(ent, NULL, NULL); } ipmi_fru_t * ipmi_entity_get_fru(ipmi_entity_t *ent) { CHECK_ENTITY_LOCK(ent); return ent->fru; } void i_ipmi_entity_set_fru(ipmi_entity_t *ent, ipmi_fru_t *fru) { CHECK_ENTITY_LOCK(ent); if (ent->fru) ipmi_fru_destroy_internal(ent->fru, NULL, NULL); ent->fru = fru; } /*************************************************************************** * * Hot swap * ***************************************************************************/ int ipmi_entity_set_hot_swappable(ipmi_entity_t *ent, int val) { ent->hot_swappable = val; /* Make sure the user knows of the change. */ ent->changed = 1; return 0; } int ipmi_entity_hot_swappable(ipmi_entity_t *ent) { return ent->hot_swappable; } int ipmi_entity_set_supports_managed_hot_swap(ipmi_entity_t *ent, int val) { ent->supports_managed_hot_swap = val; /* Make sure the user knows of the change. */ ent->changed = 1; return 0; } int ipmi_entity_supports_managed_hot_swap(ipmi_entity_t *ent) { return ent->supports_managed_hot_swap; } int ipmi_entity_add_hot_swap_handler(ipmi_entity_t *ent, ipmi_entity_hot_swap_cb handler, void *cb_data) { CHECK_ENTITY_LOCK(ent); if (locked_list_add(ent->hot_swap_handlers, handler, cb_data)) return 0; else return ENOMEM; } int ipmi_entity_remove_hot_swap_handler(ipmi_entity_t *ent, ipmi_entity_hot_swap_cb handler, void *cb_data) { CHECK_ENTITY_LOCK(ent); if (locked_list_remove(ent->hot_swap_handlers, handler, cb_data)) return 0; else return EINVAL; } int ipmi_entity_add_hot_swap_handler_cl(ipmi_entity_t *ent, ipmi_entity_hot_swap_cl_cb handler, void *cb_data) { CHECK_ENTITY_LOCK(ent); if (locked_list_add(ent->hot_swap_handlers_cl, handler, cb_data)) return 0; else return ENOMEM; } int ipmi_entity_remove_hot_swap_handler_cl(ipmi_entity_t *ent, ipmi_entity_hot_swap_cl_cb handler, void *cb_data) { CHECK_ENTITY_LOCK(ent); if (locked_list_remove(ent->hot_swap_handlers_cl, handler, cb_data)) return 0; else return EINVAL; } void ipmi_entity_set_hot_swap_control(ipmi_entity_t *ent, ipmi_entity_hot_swap_t *cbs) { CHECK_ENTITY_LOCK(ent); ent->hs_cb = *cbs; } typedef struct hot_swap_handler_info_s { ipmi_entity_t *ent; enum ipmi_hot_swap_states last_state; enum ipmi_hot_swap_states curr_state; ipmi_event_t **event; int handled; } hot_swap_handler_info_t; static int call_hot_swap_handler(void *cb_data, void *item1, void *item2) { hot_swap_handler_info_t *info = cb_data; ipmi_entity_hot_swap_cb handler = item1; int handled; handled = handler(info->ent, info->last_state, info->curr_state, item2, *(info->event)); if (handled != IPMI_EVENT_NOT_HANDLED) { if (info->handled != IPMI_EVENT_HANDLED) /* Allow handled to override handled_pass, but not the other way. */ info->handled = handled; if (handled == IPMI_EVENT_HANDLED) *(info->event) = NULL; } return LOCKED_LIST_ITER_CONTINUE; } void ipmi_entity_call_hot_swap_handlers(ipmi_entity_t *ent, enum ipmi_hot_swap_states last_state, enum ipmi_hot_swap_states curr_state, ipmi_event_t **event, int *handled) { hot_swap_handler_info_t info; info.ent = ent; info.last_state = last_state; info.curr_state = curr_state; info.event = event; if (handled) info.handled = *handled; else info.handled = IPMI_EVENT_NOT_HANDLED; locked_list_iterate(ent->hot_swap_handlers, call_hot_swap_handler, &info); if (handled) *handled = info.handled; } int ipmi_entity_get_hot_swap_state(ipmi_entity_t *ent, ipmi_entity_hot_swap_state_cb handler, void *cb_data) { if (!ent->hot_swappable) return ENOSYS; if (!ent->hs_cb.get_hot_swap_state) return ENOSYS; return ent->hs_cb.get_hot_swap_state(ent, handler, cb_data); } int ipmi_entity_supports_auto_activate_time(ipmi_entity_t *ent) { return (ent->hot_swappable && ent->hs_cb.get_auto_activate); } int ipmi_entity_set_auto_activate_time(ipmi_entity_t *ent, ipmi_timeout_t auto_act, ipmi_entity_cb done, void *cb_data) { if (!ent->hot_swappable) return ENOSYS; if (!ent->hs_cb.set_auto_activate) return ENOSYS; return ent->hs_cb.set_auto_activate(ent, auto_act, done, cb_data); } int ipmi_entity_get_auto_activate_time(ipmi_entity_t *ent, ipmi_entity_time_cb handler, void *cb_data) { if (!ent->hot_swappable) return ENOSYS; if (!ent->hs_cb.get_auto_activate) return ENOSYS; return ent->hs_cb.get_auto_activate(ent, handler, cb_data); } int ipmi_entity_supports_auto_deactivate_time(ipmi_entity_t *ent) { return (ent->hot_swappable && ent->hs_cb.get_auto_activate); } int ipmi_entity_set_auto_deactivate_time(ipmi_entity_t *ent, ipmi_timeout_t auto_deact, ipmi_entity_cb done, void *cb_data) { if (!ent->hot_swappable) return ENOSYS; if (!ent->hs_cb.set_auto_deactivate) return ENOSYS; return ent->hs_cb.set_auto_deactivate(ent, auto_deact, done, cb_data); } int ipmi_entity_get_auto_deactivate_time(ipmi_entity_t *ent, ipmi_entity_time_cb handler, void *cb_data) { if (!ent->hot_swappable) return ENOSYS; if (!ent->hs_cb.get_auto_deactivate) return ENOSYS; return ent->hs_cb.get_auto_deactivate(ent, handler, cb_data); } int ipmi_entity_set_activation_requested(ipmi_entity_t *ent, ipmi_entity_cb done, void *cb_data) { if (!ent->hot_swappable) return ENOSYS; if (!ent->hs_cb.activate) return ENOSYS; return ent->hs_cb.set_activation_requested(ent, done, cb_data); } int ipmi_entity_activate(ipmi_entity_t *ent, ipmi_entity_cb done, void *cb_data) { if (!ent->hot_swappable) return ENOSYS; if (!ent->hs_cb.activate) return ENOSYS; return ent->hs_cb.activate(ent, done, cb_data); } int ipmi_entity_deactivate(ipmi_entity_t *ent, ipmi_entity_cb done, void *cb_data) { if (!ent->hot_swappable) return ENOSYS; if (!ent->hs_cb.deactivate) return ENOSYS; return ent->hs_cb.deactivate(ent, done, cb_data); } int ipmi_entity_check_hot_swap_state(ipmi_entity_t *ent) { if (!ent->hot_swappable) return ENOSYS; if (!ent->hs_cb.check_hot_swap_state) return ENOSYS; return ent->hs_cb.check_hot_swap_state(ent); } /*********************************************************************** * * Entity ID versions of the hot-swap calls. * **********************************************************************/ typedef struct entity_hot_swap_cb_info_s { int rv; ipmi_entity_hot_swap_state_cb handler; void *cb_data; } entity_hot_swap_cb_info_t; typedef struct entity_cb_info_s { int rv; ipmi_timeout_t time; int val; ipmi_entity_cb handler; void *cb_data; } entity_cb_info_t; typedef struct entity_val_cb_info_s { int rv; ipmi_entity_val_cb handler; void *cb_data; } entity_val_cb_info_t; typedef struct entity_time_cb_info_s { int rv; ipmi_entity_time_cb handler; void *cb_data; } entity_time_cb_info_t; static void entity_id_get_hot_swap_state_cb(ipmi_entity_t *entity, void *cb_data) { entity_hot_swap_cb_info_t *info = cb_data; info->rv = ipmi_entity_get_hot_swap_state(entity, info->handler, info->cb_data); } int ipmi_entity_id_get_hot_swap_state(ipmi_entity_id_t id, ipmi_entity_hot_swap_state_cb handler, void *cb_data) { int rv; entity_hot_swap_cb_info_t info; info.rv = 0; info.handler = handler; info.cb_data = cb_data; rv = ipmi_entity_pointer_cb(id, entity_id_get_hot_swap_state_cb, &info); if (!rv) rv = info.rv; return rv; } static void entity_get_auto_activate_time_cb(ipmi_entity_t *ent, void *cb_data) { entity_time_cb_info_t *info = cb_data; info->rv = ipmi_entity_get_auto_activate_time(ent, info->handler, info->cb_data); } int ipmi_entity_id_get_auto_activate_time(ipmi_entity_id_t id, ipmi_entity_time_cb handler, void *cb_data) { entity_time_cb_info_t info; int rv; info.rv = 0; info.handler = handler; info.cb_data = cb_data; rv = ipmi_entity_pointer_cb(id, entity_get_auto_activate_time_cb, &info); if (!rv) rv = info.rv; return rv; } static void entity_set_auto_activate_time_cb(ipmi_entity_t *ent, void *cb_data) { entity_cb_info_t *info = cb_data; info->rv = ipmi_entity_set_auto_activate_time(ent, info->time, info->handler, info->cb_data); } int ipmi_entity_id_set_auto_activate_time(ipmi_entity_id_t id, ipmi_timeout_t auto_act, ipmi_entity_cb done, void *cb_data) { entity_cb_info_t info; int rv; info.rv = 0; info.time = auto_act; info.handler = done; info.cb_data = cb_data; rv = ipmi_entity_pointer_cb(id, entity_set_auto_activate_time_cb, &info); if (!rv) rv = info.rv; return rv; } static void entity_get_auto_deactivate_time_cb(ipmi_entity_t *ent, void *cb_data) { entity_time_cb_info_t *info = cb_data; info->rv = ipmi_entity_get_auto_deactivate_time(ent, info->handler, info->cb_data); } int ipmi_entity_id_get_auto_deactivate_time(ipmi_entity_id_t id, ipmi_entity_time_cb handler, void *cb_data) { entity_time_cb_info_t info; int rv; info.rv = 0; info.handler = handler; info.cb_data = cb_data; rv = ipmi_entity_pointer_cb(id, entity_get_auto_deactivate_time_cb, &info); if (!rv) rv = info.rv; return rv; } static void entity_set_auto_deactivate_time_cb(ipmi_entity_t *ent, void *cb_data) { entity_cb_info_t *info = cb_data; info->rv = ipmi_entity_set_auto_deactivate_time(ent, info->time, info->handler, info->cb_data); } int ipmi_entity_id_set_auto_deactivate_time(ipmi_entity_id_t id, ipmi_timeout_t auto_deact, ipmi_entity_cb done, void *cb_data) { entity_cb_info_t info; int rv; info.rv = 0; info.time = auto_deact; info.handler = done; info.cb_data = cb_data; rv = ipmi_entity_pointer_cb(id, entity_set_auto_deactivate_time_cb, &info); if (!rv) rv = info.rv; return rv; } static void entity_activate_cb(ipmi_entity_t *ent, void *cb_data) { entity_cb_info_t *info = cb_data; info->rv = ipmi_entity_activate(ent, info->handler, info->cb_data); } int ipmi_entity_id_activate(ipmi_entity_id_t id, ipmi_entity_cb done, void *cb_data) { entity_cb_info_t info; int rv; info.rv = 0; info.handler = done; info.cb_data = cb_data; rv = ipmi_entity_pointer_cb(id, entity_activate_cb, &info); if (!rv) rv = info.rv; return rv; } static void entity_deactivate_cb(ipmi_entity_t *ent, void *cb_data) { entity_cb_info_t *info = cb_data; info->rv = ipmi_entity_deactivate(ent, info->handler, info->cb_data); } int ipmi_entity_id_deactivate(ipmi_entity_id_t id, ipmi_entity_cb done, void *cb_data) { entity_cb_info_t info; int rv; info.rv = 0; info.handler = done; info.cb_data = cb_data; rv = ipmi_entity_pointer_cb(id, entity_deactivate_cb, &info); if (!rv) rv = info.rv; return rv; } static void entity_check_hot_swap_state_cb(ipmi_entity_t *ent, void *cb_data) { entity_val_cb_info_t *info = cb_data; info->rv = ipmi_entity_check_hot_swap_state(ent); } int ipmi_entity_id_check_hot_swap_state(ipmi_entity_id_t id) { entity_val_cb_info_t info; int rv; info.rv = 0; rv = ipmi_entity_pointer_cb(id, entity_check_hot_swap_state_cb, &info); if (!rv) rv = info.rv; return rv; } /*********************************************************************** * * The internal hot-swap state machine. * **********************************************************************/ static int set_hot_swap_state(ipmi_entity_t *ent, enum ipmi_hot_swap_states state, ipmi_event_t *event); static void hot_swap_power_on(ipmi_control_t *control, int err, void *cb_data) { ipmi_entity_t *ent = cb_data; if (err) { ipmi_log(IPMI_LOG_WARNING, "%sentity.c(hot_swap_power_on):" " Unable to set the hot swap power: %x", CONTROL_NAME(control), err); } else { ent_lock(ent); set_hot_swap_state(ent, IPMI_HOT_SWAP_ACTIVE, NULL); ent_unlock(ent); } } static void hot_swap_power_off(ipmi_control_t *control, int err, void *cb_data) { ipmi_entity_t *ent = cb_data; if (err) { ipmi_log(IPMI_LOG_WARNING, "%sentity.c(hot_swap_power_off):" " Unable to set the hot swap power: %x", CONTROL_NAME(control), err); } else { ent_lock(ent); set_hot_swap_state(ent, IPMI_HOT_SWAP_INACTIVE, NULL); ent_unlock(ent); } } typedef struct power_cb_info_s { ipmi_entity_t *ent; ipmi_entity_cb handler; void *cb_data; } power_cb_info_t; static void hot_swap_power_on_cb(ipmi_control_t *control, int err, void *cb_data) { power_cb_info_t *info = cb_data; ipmi_entity_t *ent = info->ent; if (err) { ipmi_log(IPMI_LOG_WARNING, "%sentity.c(hot_swap_power_on_cb):" " Unable to set the hot swap power: %x", CONTROL_NAME(control), err); } else { ent_lock(ent); set_hot_swap_state(ent, IPMI_HOT_SWAP_ACTIVE, NULL); ent_unlock(ent); } if (info->handler) info->handler(info->ent, err, info->cb_data); ipmi_mem_free(info); } static void hot_swap_power_off_cb(ipmi_control_t *control, int err, void *cb_data) { power_cb_info_t *info = cb_data; ipmi_entity_t *ent = info->ent; if (err) { ipmi_log(IPMI_LOG_WARNING, "%sentity.c(hot_swap_power_off_cb):" " Unable to set the hot swap power: %x", CONTROL_NAME(control), err); } else { ent_lock(ent); set_hot_swap_state(ent, IPMI_HOT_SWAP_INACTIVE, NULL); ent_unlock(ent); } if (info->handler) info->handler(info->ent, err, info->cb_data); ipmi_mem_free(info); } static void indicator_change(ipmi_control_t *control, int err, void *cb_data) { if (err) ipmi_log(IPMI_LOG_WARNING, "%sentity.c(indicator_change):" " Unable to set the hot swap indicator: %x", CONTROL_NAME(control), err); } static int hot_swap_act(ipmi_entity_t *ent, ipmi_entity_cb handler, void *cb_data) { int val; int rv = ENOSYS; ipmi_control_op_cb cb; power_cb_info_t *info = NULL; ent_lock(ent); if (ent->hot_swap_state == IPMI_HOT_SWAP_ACTIVATION_REQUESTED) { if (ent->hot_swap_power) { if (handler == NULL) { cb = hot_swap_power_on; cb_data = ent; } else { info = ipmi_mem_alloc(sizeof(*info)); if (!info) { rv = ENOMEM; goto out; } cb = hot_swap_power_on_cb; info->ent = ent; info->handler = handler; info->cb_data = cb_data; cb_data = info; } val = 1; ent_unlock(ent); rv = ipmi_control_id_set_val(ent->hot_swap_power_id, &val, cb, cb_data); ent_lock(ent); if (!rv) set_hot_swap_state(ent, IPMI_HOT_SWAP_ACTIVATION_IN_PROGRESS, NULL); else if (info) ipmi_mem_free(info); } } else { rv = EAGAIN; } out: ent_unlock(ent); return rv; } static void hot_swap_act_cb(ipmi_entity_t *ent, void *cb_data) { int rv; rv = hot_swap_act(ent, NULL, NULL); if (rv && (rv != EAGAIN)) ipmi_log(IPMI_LOG_WARNING, "%sentity.c(hot_swap_act_cb):" " Unable to set the hot swap power: %x", ENTITY_NAME(ent), rv); } static void hot_swap_act_timeout(void *cb_data, os_hnd_timer_id_t *timer) { ent_timer_info_t *info = cb_data; ipmi_entity_id_t entity_id; ipmi_lock(info->lock); if (info->destroyed) { ipmi_unlock(info->lock); info->os_hnd->free_timer(info->os_hnd, info->timer); ipmi_mem_free(info); return; } info->running = 0; entity_id = ipmi_entity_convert_to_id(info->entity); ipmi_unlock(info->lock); ipmi_entity_pointer_cb(entity_id, hot_swap_act_cb, NULL); } static int hot_swap_deact(ipmi_entity_t *ent, ipmi_entity_cb handler, void *cb_data) { int val; int rv = ENOSYS; ipmi_control_op_cb cb; power_cb_info_t *info; ent_lock(ent); if (ent->hot_swap_state == IPMI_HOT_SWAP_DEACTIVATION_REQUESTED) { if (ent->hot_swap_power) { if (handler == NULL) { cb = hot_swap_power_off; cb_data = ent; } else { info = ipmi_mem_alloc(sizeof(*info)); if (!info) { rv = ENOMEM; goto out; } cb = hot_swap_power_off_cb; info->ent = ent; info->handler = handler; info->cb_data = cb_data; cb_data = info; } val = 0; ent_unlock(ent); rv = ipmi_control_id_set_val(ent->hot_swap_power_id, &val, cb, cb_data); ent_lock(ent); if (!rv) set_hot_swap_state(ent, IPMI_HOT_SWAP_DEACTIVATION_IN_PROGRESS, NULL); } } else { rv = EAGAIN; } out: ent_unlock(ent); return rv; } static void hot_swap_deact_cb(ipmi_entity_t *ent, void *cb_data) { int rv; rv = hot_swap_deact(ent, NULL, NULL); if (rv && (rv != EAGAIN)) ipmi_log(IPMI_LOG_WARNING, "%sentity.c(hot_swap_deact_cb):" " Unable to set the hot swap power: %x", ENTITY_NAME(ent), rv); } static void hot_swap_deact_timeout(void *cb_data, os_hnd_timer_id_t *timer) { ent_timer_info_t *info = cb_data; ipmi_entity_id_t entity_id; ipmi_lock(info->lock); if (info->destroyed) { ipmi_unlock(info->lock); info->os_hnd->free_timer(info->os_hnd, info->timer); ipmi_mem_free(info); return; } info->running = 0; entity_id = ipmi_entity_convert_to_id(info->entity); ipmi_unlock(info->lock); ipmi_entity_pointer_cb(entity_id, hot_swap_deact_cb, NULL); } /* Must be called with the entity locked. Note that it may release and reclaim the lock as part of its operation. */ static int set_hot_swap_state(ipmi_entity_t *ent, enum ipmi_hot_swap_states state, ipmi_event_t *event) { int val; int set = 1; enum ipmi_hot_swap_states old_state; int handled = IPMI_EVENT_NOT_HANDLED; old_state = ent->hot_swap_state; switch (state) { case IPMI_HOT_SWAP_INACTIVE: val = ent->hot_swap_ind_inact; break; case IPMI_HOT_SWAP_ACTIVATION_REQUESTED: val = ent->hot_swap_ind_req_act; entity_start_timer(ent->hot_swap_act_info, ent->hot_swap_act_timeout, hot_swap_act_timeout); break; case IPMI_HOT_SWAP_ACTIVE: val = ent->hot_swap_ind_act; break; case IPMI_HOT_SWAP_DEACTIVATION_REQUESTED: val = ent->hot_swap_ind_req_deact; entity_start_timer(ent->hot_swap_deact_info, ent->hot_swap_deact_timeout, hot_swap_deact_timeout); break; case IPMI_HOT_SWAP_DEACTIVATION_IN_PROGRESS: case IPMI_HOT_SWAP_NOT_PRESENT: case IPMI_HOT_SWAP_OUT_OF_CON: default: set = 0; break; } if (set && ent->hot_swap_indicator) { int rv; rv = ipmi_control_id_set_val(ent->hot_swap_indicator_id, &val, indicator_change, NULL); if (rv) ipmi_log(IPMI_LOG_SEVERE, "%sentity.c(set_hot_swap_state): Unable to" " set control value to %d, error %x", CONTROL_NAME(ent->hot_swap_indicator), val, rv); } if (old_state != state) { ent->hot_swap_state = state; ent_unlock(ent); ipmi_entity_call_hot_swap_handlers(ent, old_state, state, &event, &handled); ent_lock(ent); } return handled; } static int hot_swap_requester_changed(ipmi_sensor_t *sensor, enum ipmi_event_dir_e dir, int offset, int severity, int prev_severity, void *cb_data, ipmi_event_t *event) { ipmi_entity_t *ent = cb_data; int handled = IPMI_EVENT_NOT_HANDLED; ent_lock(ent); if (offset != ent->hot_swap_offset) goto out; if (ent->hot_swap_requesting_val && (dir == IPMI_ASSERTION)) { /* A hot-swap is being requested */ switch (ent->hot_swap_state) { case IPMI_HOT_SWAP_ACTIVE: handled = set_hot_swap_state (ent, IPMI_HOT_SWAP_DEACTIVATION_REQUESTED, event); break; case IPMI_HOT_SWAP_ACTIVATION_REQUESTED: handled = set_hot_swap_state (ent, IPMI_HOT_SWAP_INACTIVE, event); break; case IPMI_HOT_SWAP_ACTIVATION_IN_PROGRESS: handled = set_hot_swap_state (ent, IPMI_HOT_SWAP_DEACTIVATION_IN_PROGRESS, event); break; case IPMI_HOT_SWAP_DEACTIVATION_REQUESTED: case IPMI_HOT_SWAP_DEACTIVATION_IN_PROGRESS: case IPMI_HOT_SWAP_OUT_OF_CON: case IPMI_HOT_SWAP_INACTIVE: case IPMI_HOT_SWAP_NOT_PRESENT: default: break; } } else { /* A hot-swap is being derequested */ switch (ent->hot_swap_state) { case IPMI_HOT_SWAP_DEACTIVATION_REQUESTED: handled = set_hot_swap_state (ent, IPMI_HOT_SWAP_ACTIVE, event); break; case IPMI_HOT_SWAP_INACTIVE: handled = set_hot_swap_state (ent, IPMI_HOT_SWAP_ACTIVATION_REQUESTED, event); break; case IPMI_HOT_SWAP_ACTIVATION_REQUESTED: case IPMI_HOT_SWAP_ACTIVATION_IN_PROGRESS: case IPMI_HOT_SWAP_ACTIVE: case IPMI_HOT_SWAP_DEACTIVATION_IN_PROGRESS: case IPMI_HOT_SWAP_OUT_OF_CON: case IPMI_HOT_SWAP_NOT_PRESENT: default: break; } } out: ent_unlock(ent); return handled; } static void power_checked(ipmi_control_t *control, int err, int *val, void *cb_data); static int hot_swap_power_changed(ipmi_control_t *control, int *valid_vals, int *vals, void *cb_data, ipmi_event_t *event) { ipmi_entity_t *ent = cb_data; if (!valid_vals[0]) return IPMI_EVENT_NOT_HANDLED; if (ent->present) power_checked(control, 0, vals, ent); return IPMI_EVENT_NOT_HANDLED; } static void handle_new_hot_swap_indicator(ipmi_entity_t *ent, ipmi_control_t *control) { int val = 0; int rv; ipmi_control_is_hot_swap_indicator(control, &ent->hot_swap_ind_req_act, &ent->hot_swap_ind_act, &ent->hot_swap_ind_req_deact, &ent->hot_swap_ind_inact); ent->hot_swap_indicator_id = ipmi_control_convert_to_id(control); ent->hot_swap_indicator = control; switch (ent->hot_swap_state) { case IPMI_HOT_SWAP_INACTIVE: val = ent->hot_swap_ind_inact; break; case IPMI_HOT_SWAP_ACTIVATION_REQUESTED: val = ent->hot_swap_ind_req_act; break; case IPMI_HOT_SWAP_ACTIVATION_IN_PROGRESS: case IPMI_HOT_SWAP_ACTIVE: val = ent->hot_swap_ind_act; break; case IPMI_HOT_SWAP_DEACTIVATION_REQUESTED: case IPMI_HOT_SWAP_DEACTIVATION_IN_PROGRESS: val = ent->hot_swap_ind_req_deact; break; default: val = ent->hot_swap_ind_inact; break; } ent_unlock(ent); rv = ipmi_control_set_val(control, &val, NULL, NULL); ent_lock(ent); if (rv) ipmi_log(IPMI_LOG_SEVERE, "%sentity.c(handle_new_hot_swap_indicator): Unable to" " set control value, error %x", CONTROL_NAME(control), rv); } static void requester_checked(ipmi_sensor_t *sensor, int err, ipmi_states_t *states, void *cb_data) { ipmi_entity_t *ent = cb_data; if (err) { ipmi_log(IPMI_LOG_SEVERE, "%sentity.c(requester_chedked): Unable to" " get requester value, error %x", SENSOR_NAME(sensor), err); return; } ent_lock(ent); if (ipmi_is_state_set(states, ent->hot_swap_offset) == ent->hot_swap_requesting_val) { /* requester is requesting, change the state. */ if (ent->hot_swap_state == IPMI_HOT_SWAP_ACTIVE) set_hot_swap_state(ent, IPMI_HOT_SWAP_DEACTIVATION_REQUESTED, NULL); } else { if (ent->hot_swap_state == IPMI_HOT_SWAP_INACTIVE) set_hot_swap_state(ent, IPMI_HOT_SWAP_ACTIVATION_REQUESTED, NULL); } ent_unlock(ent); } static void power_checked(ipmi_control_t *control, int err, int *val, void *cb_data) { int rv; ipmi_entity_t *ent = cb_data; if (err) { ipmi_log(IPMI_LOG_SEVERE, "%sentity.c(power_checked): Unable to" " get power value, error %x", CONTROL_NAME(control), err); return; } ent_lock(ent); if (val[0]) set_hot_swap_state(ent, IPMI_HOT_SWAP_ACTIVE, NULL); else set_hot_swap_state(ent, IPMI_HOT_SWAP_INACTIVE, NULL); if (ent->hot_swap_requester) { ipmi_sensor_id_t hsr = ent->hot_swap_requester_id; ent_unlock(ent); rv = ipmi_sensor_id_get_states(hsr, requester_checked, ent); if (rv) { ipmi_log(IPMI_LOG_SEVERE, "%sentity.c(power_checked): Unable to" " request requester status, error %x", SENSOR_NAME(ent->hot_swap_requester), rv); } } else ent_unlock(ent); } static void handle_new_hot_swap_power(ipmi_entity_t *ent, ipmi_control_t *control) { int rv; /* Add our own event handler. */ rv = ipmi_control_add_val_event_handler(control, hot_swap_power_changed, ent); if (rv) { ipmi_log(IPMI_LOG_SEVERE, "%sentity.c(handle_new_hot_swap_power): Unable to" " add an event handler, error %x", CONTROL_NAME(control), rv); goto out; } ent->hot_swap_power_id = ipmi_control_convert_to_id(control); ent->hot_swap_power = control; /* If we have power control, we can manage hot-swap. */ ipmi_entity_set_supports_managed_hot_swap(ent, 1); if (ent->hot_swappable) { ent_unlock(ent); rv = ipmi_control_get_val(control, power_checked, ent); ent_lock(ent); if (rv) { ipmi_log(IPMI_LOG_SEVERE, "%sentity.c(handle_new_hot_swap_power): Unable to" " request power status, error %x", CONTROL_NAME(ent->hot_swap_power), rv); } } out: return; } /* Must be called with the entity lock held. May release and reclaim it. */ static void handle_new_hot_swap_requester(ipmi_entity_t *ent, ipmi_sensor_t *sensor) { ipmi_event_state_t events; int event_support; int rv; int val; ent->hot_swap_requester_id = ipmi_sensor_convert_to_id(sensor); ipmi_sensor_is_hot_swap_requester(sensor, &ent->hot_swap_offset, &ent->hot_swap_requesting_val); event_support = ipmi_sensor_get_event_support(sensor); /* Add our own event handler. */ rv = ipmi_sensor_add_discrete_event_handler(sensor, hot_swap_requester_changed, ent); if (rv) { ipmi_log(IPMI_LOG_SEVERE, "%sentity.c(handle_new_hot_swap_requester): Unable to" " add an event handler, error %x", SENSOR_NAME(sensor), rv); goto out; } ent->hot_swap_requester = sensor; /* Nothing to do, events will just be on. */ if (event_support == IPMI_EVENT_SUPPORT_GLOBAL_ENABLE) goto out; /* Turn events and scanning on. */ ipmi_event_state_init(&events); ipmi_event_state_set_events_enabled(&events, 1); ipmi_event_state_set_scanning_enabled(&events, 1); if (event_support == IPMI_EVENT_SUPPORT_PER_STATE) { /* Turn on all the event enables that we can. */ rv = ipmi_sensor_discrete_event_supported (sensor, ent->hot_swap_offset, IPMI_ASSERTION, &val); if ((!rv) && (val)) ipmi_discrete_event_set(&events, ent->hot_swap_offset, IPMI_ASSERTION); rv = ipmi_sensor_discrete_event_supported (sensor, ent->hot_swap_offset, IPMI_DEASSERTION, &val); if ((!rv) && (val)) ipmi_discrete_event_set(&events, ent->hot_swap_offset, IPMI_DEASSERTION); } ent_unlock(ent); ipmi_sensor_set_event_enables(sensor, &events, NULL, NULL); ent_lock(ent); if (ent->hot_swappable) { ipmi_sensor_id_t hsr = ent->hot_swap_requester_id; ent_unlock(ent); rv = ipmi_sensor_id_get_states(hsr, requester_checked, ent); ent_lock(ent); if (rv) { ipmi_log(IPMI_LOG_SEVERE, "%sentity.c(handle_new_hot_swap_requester): Unable to" " request requester status, error %x", SENSOR_NAME(ent->hot_swap_requester), rv); } } out: return; } static int handle_hot_swap_presence(ipmi_entity_t *ent, int present, ipmi_event_t *event) { int handled = IPMI_EVENT_NOT_HANDLED; int rv; ent_lock(ent); if (present) { if ((!ent->hot_swap_power) || (ent->hot_swap_act_timeout == IPMI_TIMEOUT_NOW)) { /* No power control or immediate timeout, it goes straight to active. */ handled = set_hot_swap_state(ent, IPMI_HOT_SWAP_ACTIVE, event); ent_unlock(ent); } else { ipmi_control_id_t hsp = ent->hot_swap_power_id; ent_unlock(ent); rv = ipmi_control_id_get_val(hsp, power_checked, ent); if (rv) { ipmi_log(IPMI_LOG_SEVERE, "%sentity.c(handle_hot_swap_presence): Unable to" " request power status, error %x", CONTROL_NAME(ent->hot_swap_power), rv); } } } else { handled = set_hot_swap_state(ent, IPMI_HOT_SWAP_NOT_PRESENT, event); ent_unlock(ent); } return handled; } static int e_get_hot_swap_state(ipmi_entity_t *ent, ipmi_entity_hot_swap_state_cb handler, void *cb_data) { if (handler) handler(ent, 0, ent->hot_swap_state, cb_data); return 0; } static int e_set_auto_activate(ipmi_entity_t *ent, ipmi_timeout_t auto_act, ipmi_entity_cb done, void *cb_data) { int rv = 0; ent_lock(ent); if (!ent->hot_swap_power) rv = ENOSYS; else ent->hot_swap_act_timeout = auto_act; ent_unlock(ent); if ((!rv) && (done)) done(ent, 0, cb_data); return rv; } static int e_get_auto_activate(ipmi_entity_t *ent, ipmi_entity_time_cb handler, void *cb_data) { int rv = 0; ipmi_timeout_t time = 0; ent_lock(ent); if (!ent->hot_swap_power) rv = ENOSYS; else time = ent->hot_swap_act_timeout; ent_unlock(ent); if ((!rv) && (handler)) handler(ent, 0, time, cb_data); return rv; } static int e_set_auto_deactivate(ipmi_entity_t *ent, ipmi_timeout_t auto_act, ipmi_entity_cb done, void *cb_data) { int rv = 0; ent_lock(ent); if (!ent->hot_swap_power) rv = ENOSYS; else ent->hot_swap_deact_timeout = auto_act; ent_unlock(ent); if ((!rv) && (done)) done(ent, 0, cb_data); return rv; } static int e_get_auto_deactivate(ipmi_entity_t *ent, ipmi_entity_time_cb handler, void *cb_data) { int rv = 0; ipmi_timeout_t time = 0; ent_lock(ent); if (!ent->hot_swap_power) rv = ENOSYS; else time = ent->hot_swap_deact_timeout; ent_unlock(ent); if ((!rv) && (handler)) handler(ent, 0, time, cb_data); return 0; } static int e_activate(ipmi_entity_t *ent, ipmi_entity_cb done, void *cb_data) { return hot_swap_act(ent, done, cb_data); } static int e_deactivate(ipmi_entity_t *ent, ipmi_entity_cb done, void *cb_data) { return hot_swap_deact(ent, done, cb_data); } typedef struct get_hot_swap_info_s { ipmi_entity_t *ent; ipmi_entity_val_cb handler; void *cb_data; } get_hot_swap_info_t; static void got_hot_swap_ind(ipmi_control_t *control, int err, int *cbval, void *cb_data) { get_hot_swap_info_t *info = cb_data; int val = 0; if (!err) val = *cbval; info->handler(info->ent, err, val, info->cb_data); ipmi_mem_free(info); } static int e_get_hot_swap_indicator(ipmi_entity_t *ent, ipmi_entity_val_cb handler, void *cb_data) { get_hot_swap_info_t *info; int rv; ipmi_control_id_t id; ent_lock(ent); if (! ent->hot_swap_indicator) { ent_unlock(ent); return ENOSYS; } id = ent->hot_swap_indicator_id; ent_unlock(ent); info = ipmi_mem_alloc(sizeof(*info)); if (!info) return ENOMEM; info->ent = ent; info->handler = handler; info->cb_data = cb_data; rv = ipmi_control_id_get_val(id, got_hot_swap_ind, &info); if (rv) ipmi_mem_free(info); return rv; } typedef struct set_hot_swap_ind_info_s { ipmi_entity_t *ent; ipmi_entity_cb handler; void *cb_data; } set_hot_swap_ind_info_t; static void set_hot_swap_ind(ipmi_control_t *control, int err, void *cb_data) { set_hot_swap_ind_info_t *info = cb_data; info->handler(info->ent, err, info->cb_data); ipmi_mem_free(info); } static int e_set_hot_swap_indicator(ipmi_entity_t *ent, int val, ipmi_entity_cb done, void *cb_data) { set_hot_swap_ind_info_t *info; int rv; ipmi_control_id_t id; ent_lock(ent); if (! ent->hot_swap_indicator) { ent_unlock(ent); return ENOSYS; } id = ent->hot_swap_indicator_id; ent_unlock(ent); info = ipmi_mem_alloc(sizeof(*info)); if (!info) return ENOMEM; info->ent = ent; info->handler = done; info->cb_data = cb_data; rv = ipmi_control_id_set_val(id, &val, set_hot_swap_ind, &info); if (rv) ipmi_mem_free(info); return rv; } static void got_hot_swap_req(ipmi_sensor_t *sensor, int err, ipmi_states_t *states, void *cb_data) { get_hot_swap_info_t *info = cb_data; int val = 0; if (!err) { ent_lock(info->ent); if (ipmi_is_state_set(states, info->ent->hot_swap_offset) == info->ent->hot_swap_requesting_val) { val = 1; } ent_unlock(info->ent); } info->handler(info->ent, err, val, info->cb_data); ipmi_mem_free(info); } static int e_get_hot_swap_requester(ipmi_entity_t *ent, ipmi_entity_val_cb handler, void *cb_data) { get_hot_swap_info_t *info; int rv; ipmi_sensor_id_t id; ent_lock(ent); if (! ent->hot_swap_requester) { ent_unlock(ent); return ENOSYS; } id = ent->hot_swap_requester_id; ent_unlock(ent); info = ipmi_mem_alloc(sizeof(*info)); if (!info) return ENOMEM; info->ent = ent; info->handler = handler; info->cb_data = cb_data; rv = ipmi_sensor_id_get_states(id, got_hot_swap_req, &info); if (rv) ipmi_mem_free(info); return rv; } typedef struct hs_check_s { int power; ipmi_entity_t *entity; } hs_check_t; static void check_requester(ipmi_sensor_t *sensor, int err, ipmi_states_t *states, void *cb_data) { hs_check_t *info = cb_data; ipmi_entity_t *ent = info->entity; if (err) { ipmi_log(IPMI_LOG_SEVERE, "%sentity.c(requester_checked): Unable to" " get requester value, error %x", SENSOR_NAME(sensor), err); goto out; } ent_lock(ent); if (ipmi_is_state_set(states, ent->hot_swap_offset) == ent->hot_swap_requesting_val) { /* requester is requesting, change the state. */ if (info->power) set_hot_swap_state(ent, IPMI_HOT_SWAP_DEACTIVATION_REQUESTED, NULL); else set_hot_swap_state(ent, IPMI_HOT_SWAP_INACTIVE, NULL); } else { if (info->power) set_hot_swap_state(ent, IPMI_HOT_SWAP_ACTIVE, NULL); else set_hot_swap_state(ent, IPMI_HOT_SWAP_ACTIVATION_REQUESTED, NULL); } ent_unlock(ent); out: ipmi_mem_free(info); } static void check_power(ipmi_control_t *control, int err, int *val, void *cb_data) { int rv; hs_check_t *info = cb_data; ipmi_entity_t *ent = info->entity; if (err) { ipmi_log(IPMI_LOG_SEVERE, "%sentity.c(check_power): Unable to" " get power value, error %x", CONTROL_NAME(control), err); ipmi_mem_free(info); return; } info->power = val[0]; ent_lock(ent); if (ent->hot_swap_requester) { ipmi_sensor_id_t hsr = ent->hot_swap_requester_id; ent_unlock(ent); rv = ipmi_sensor_id_get_states(hsr, check_requester, info); if (rv) { ipmi_log(IPMI_LOG_SEVERE, "%sentity.c(check_power): Unable to" " request requester status, error %x", SENSOR_NAME(ent->hot_swap_requester), rv); ipmi_mem_free(info); } } else { if (info->power) set_hot_swap_state(ent, IPMI_HOT_SWAP_ACTIVE, NULL); else set_hot_swap_state(ent, IPMI_HOT_SWAP_INACTIVE, NULL); ent_unlock(ent); ipmi_mem_free(info); } } static int e_check_hot_swap_state(ipmi_entity_t *ent) { hs_check_t *info; int rv = 0; info = ipmi_mem_alloc(sizeof(*info)); if (!info) return ENOMEM; info->entity = ent; info->power = 1; /* Assume power is on if no power control. */ ent_lock(ent); if (ent->hot_swap_power) { ipmi_control_id_t hsp = ent->hot_swap_power_id; ent_unlock(ent); rv = ipmi_control_id_get_val(hsp, check_power, info); } else if (ent->hot_swap_requester) { ipmi_sensor_id_t hsr = ent->hot_swap_requester_id; ent_unlock(ent); rv = ipmi_sensor_id_get_states(hsr, check_requester, info); } else { ent_unlock(ent); ipmi_mem_free(info); } if (info && rv) ipmi_mem_free(info); return rv; } /*********************************************************************** * * More hot-swap stuff. Don't use it. This is only here because HPI * requires it. * **********************************************************************/ int ipmi_entity_get_hot_swap_indicator(ipmi_entity_t *ent, ipmi_entity_val_cb handler, void *cb_data) { if (!ent->hot_swappable) return ENOSYS; if (!ent->hs_cb.get_hot_swap_indicator) return ENOSYS; return ent->hs_cb.get_hot_swap_indicator(ent, handler, cb_data); } int ipmi_entity_set_hot_swap_indicator(ipmi_entity_t *ent, int val, ipmi_entity_cb done, void *cb_data) { if (!ent->hot_swappable) return ENOSYS; if (!ent->hs_cb.set_hot_swap_indicator) return ENOSYS; return ent->hs_cb.set_hot_swap_indicator(ent, val, done, cb_data); } int ipmi_entity_get_hot_swap_requester(ipmi_entity_t *ent, ipmi_entity_val_cb handler, void *cb_data) { if (!ent->hot_swappable) return ENOSYS; if (!ent->hs_cb.get_hot_swap_requester) return ENOSYS; return ent->hs_cb.get_hot_swap_requester(ent, handler, cb_data); } static void entity_get_hot_swap_indicator_cb(ipmi_entity_t *ent, void *cb_data) { entity_val_cb_info_t *info = cb_data; if (!ent->hot_swappable) info->rv = ENOSYS; else if (!ent->hs_cb.get_hot_swap_indicator) info->rv = ENOSYS; else info->rv = ent->hs_cb.get_hot_swap_indicator(ent, info->handler, info->cb_data); } int ipmi_entity_id_get_hot_swap_indicator(ipmi_entity_id_t id, ipmi_entity_val_cb handler, void *cb_data) { entity_val_cb_info_t info; int rv; info.rv = 0; info.handler = handler; info.cb_data = cb_data; rv = ipmi_entity_pointer_cb(id, entity_get_hot_swap_indicator_cb, &info); if (!rv) rv = info.rv; return rv; } static void entity_set_hot_swap_indicator_cb(ipmi_entity_t *ent, void *cb_data) { entity_cb_info_t *info = cb_data; if (!ent->hot_swappable) info->rv = ENOSYS; else if (!ent->hs_cb.set_hot_swap_indicator) info->rv = ENOSYS; else info->rv = ent->hs_cb.set_hot_swap_indicator(ent, info->val, info->handler, info->cb_data); } int ipmi_entity_id_set_hot_swap_indicator(ipmi_entity_id_t id, int val, ipmi_entity_cb done, void *cb_data) { entity_cb_info_t info; int rv; info.rv = 0; info.val = val; info.handler = done; info.cb_data = cb_data; rv = ipmi_entity_pointer_cb(id, entity_set_hot_swap_indicator_cb, &info); if (!rv) rv = info.rv; return rv; } static void entity_get_hot_swap_requester_cb(ipmi_entity_t *ent, void *cb_data) { entity_val_cb_info_t *info = cb_data; if (!ent->hot_swappable) info->rv = ENOSYS; else if (!ent->hs_cb.get_hot_swap_requester) info->rv = ENOSYS; else info->rv = ent->hs_cb.get_hot_swap_requester(ent, info->handler, info->cb_data); } int ipmi_entity_id_get_hot_swap_requester(ipmi_entity_id_t id, ipmi_entity_val_cb handler, void *cb_data) { entity_val_cb_info_t info; int rv; info.rv = 0; info.handler = handler; info.cb_data = cb_data; rv = ipmi_entity_pointer_cb(id, entity_get_hot_swap_requester_cb, &info); if (!rv) rv = info.rv; return rv; } /*********************************************************************** * * Entity message handling. * **********************************************************************/ static void entity_opq_ready2(ipmi_entity_t *entity, void *cb_data) { ipmi_entity_op_info_t *info = cb_data; if (info->__handler) info->__handler(entity, 0, info->__cb_data); } static int entity_opq_ready(void *cb_data, int shutdown) { ipmi_entity_op_info_t *info = cb_data; int rv; if (shutdown) { /* Technically this can't happen, but just in case... */ ipmi_log(IPMI_LOG_SEVERE, "%sentity.c(entity_opq_ready): " "Entity was destroyed while an operation was in progress", ENTITY_NAME(info->__entity)); if (info->__handler) info->__handler(info->__entity, ECANCELED, info->__cb_data); return OPQ_HANDLER_STARTED; } rv = ipmi_entity_pointer_cb(info->__entity_id, entity_opq_ready2, info); if (rv) { /* Technically this can't happen, but just in case... */ ipmi_log(IPMI_LOG_SEVERE, "%sentity.c(entity_opq_ready): " "Entity pointer callback failed", ENTITY_NAME(info->__entity)); if (info->__handler) info->__handler(info->__entity, rv, info->__cb_data); } return OPQ_HANDLER_STARTED; } int ipmi_entity_add_opq(ipmi_entity_t *entity, ipmi_entity_cb handler, ipmi_entity_op_info_t *info, void *cb_data) { if (entity->destroyed) return EINVAL; info->__entity = entity; info->__entity_id = ipmi_entity_convert_to_id(entity); info->__cb_data = cb_data; info->__handler = handler; if (!opq_new_op(entity->waitq, entity_opq_ready, info, 0)) return ENOMEM; return 0; } void ipmi_entity_opq_done(ipmi_entity_t *entity) { /* Protect myself from NULL entitys. This way, it doesn't have to be done in each call. */ if (!entity) return; /* No check for the lock. It will sometimes fail at destruction time. */ opq_op_done(entity->waitq); } static void entity_rsp_handler2(ipmi_entity_t *entity, void *cb_data) { ipmi_entity_op_info_t *info = cb_data; if (info->__rsp_handler) info->__rsp_handler(entity, 0, info->__rsp, info->__cb_data); } static void entity_rsp_handler(ipmi_mc_t *mc, ipmi_msg_t *rsp, void *rsp_data) { ipmi_entity_op_info_t *info = rsp_data; int rv; /* Call the next stage with the lock held. */ info->__rsp = rsp; rv = ipmi_entity_pointer_cb(info->__entity_id, entity_rsp_handler2, info); if (rv) { /* This really can't happen, there will be something in the opq, so the entity cannot go away. */ ipmi_log(IPMI_LOG_ERR_INFO, "%sentity.c(entity_rsp_handler): " "Could not convert entity id to a pointer, entity was" " probably destroyed while operation was in progress", MC_NAME(mc)); if (info->__rsp_handler) { i_ipmi_domain_entity_lock(info->__entity->domain); info->__entity->usecount++; i_ipmi_domain_entity_unlock(info->__entity->domain); info->__rsp_handler(info->__entity, rv, NULL, info->__cb_data); i_ipmi_entity_put(info->__entity); } } } static void send_command_mc_cb(ipmi_mc_t *mc, void *cb_data) { ipmi_entity_op_info_t *info = cb_data; info->__err = ipmi_mc_send_command(mc, info->__lun, info->__msg, entity_rsp_handler, info); } int ipmi_entity_send_command(ipmi_entity_t *entity, ipmi_mcid_t mcid, unsigned int lun, ipmi_msg_t *msg, ipmi_entity_rsp_cb handler, ipmi_entity_op_info_t *info, void *cb_data) { int rv; CHECK_ENTITY_LOCK(entity); if (entity->destroyed) return EINVAL; info->__entity = entity; info->__entity_id = ipmi_entity_convert_to_id(entity); info->__cb_data = cb_data; info->__rsp_handler = handler; info->__err = 0; info->__msg = msg; info->__lun = lun; rv = ipmi_mc_pointer_cb(mcid, send_command_mc_cb, info); if (!rv) rv = info->__err; return rv; } /*********************************************************************** * * Cruft * **********************************************************************/ int ipmi_entity_set_presence_handler(ipmi_entity_t *ent, ipmi_entity_presence_nd_cb handler, void *cb_data) { CHECK_ENTITY_LOCK(ent); ent_lock(ent); ent->cruft_presence_handler = handler; ent->cruft_presence_cb_data = cb_data; ent_unlock(ent); return 0; } int ipmi_entity_set_sensor_update_handler(ipmi_entity_t *ent, ipmi_entity_sensor_cb handler, void *cb_data) { int rv = 0; CHECK_ENTITY_LOCK(ent); ent_lock(ent); if (ent->cruft_sensor_handler) ipmi_entity_remove_sensor_update_handler (ent, ent->cruft_sensor_handler, ent->cruft_sensor_cb_data); ent->cruft_sensor_handler = handler; ent->cruft_sensor_cb_data = cb_data; if (handler) rv = ipmi_entity_add_sensor_update_handler(ent, handler, cb_data); ent_unlock(ent); return rv; } int ipmi_entity_set_control_update_handler(ipmi_entity_t *ent, ipmi_entity_control_cb handler, void *cb_data) { int rv = 0; CHECK_ENTITY_LOCK(ent); ent_lock(ent); if (ent->cruft_control_handler) ipmi_entity_remove_control_update_handler (ent, ent->cruft_control_handler, ent->cruft_control_cb_data); ent->cruft_control_handler = handler; ent->cruft_control_cb_data = cb_data; if (handler) rv = ipmi_entity_add_control_update_handler(ent, handler, cb_data); ent_unlock(ent); return rv; } int ipmi_entity_set_fru_update_handler(ipmi_entity_t *ent, ipmi_entity_fru_cb handler, void *cb_data) { int rv = 0; CHECK_ENTITY_LOCK(ent); ent_lock(ent); if (ent->cruft_fru_handler) ipmi_entity_remove_fru_update_handler (ent, ent->cruft_fru_handler, ent->cruft_fru_cb_data); ent->cruft_fru_handler = handler; ent->cruft_fru_cb_data = cb_data; if (handler) rv = ipmi_entity_add_fru_update_handler(ent, handler, cb_data); ent_unlock(ent); return rv; } /* * Getting the FRU values for an entity. */ #define FRU_VAL_GET(type, name) \ int \ ipmi_entity_get_ ## name(ipmi_entity_t *entity, \ type *val) \ { \ CHECK_ENTITY_LOCK(entity); \ if (!entity->fru) \ return ENOSYS; \ return ipmi_fru_get_ ## name(entity->fru, val); \ } #define FRU_STR_GET(name) \ int \ ipmi_entity_get_ ## name(ipmi_entity_t *entity, \ char *str, \ unsigned int *strlen) \ { \ CHECK_ENTITY_LOCK(entity); \ if (!entity->fru) \ return ENOSYS; \ return ipmi_fru_get_ ## name(entity->fru, str, strlen); \ } #define FRU_CUSTOM_GET(name) \ int \ ipmi_entity_get_ ## name ## _custom_len(ipmi_entity_t *entity, \ unsigned int num, \ unsigned int *length) \ { \ CHECK_ENTITY_LOCK(entity); \ if (!entity->fru) \ return ENOSYS; \ return ipmi_fru_get_ ## name ## _custom_len(entity->fru, num, length);\ } \ int \ ipmi_entity_get_## name ## _custom_type(ipmi_entity_t *entity, \ unsigned int num, \ enum ipmi_str_type_e *type) \ { \ CHECK_ENTITY_LOCK(entity); \ if (!entity->fru) \ return ENOSYS; \ return ipmi_fru_get_ ## name ## _custom_type(entity->fru, num, type);\ } \ int \ ipmi_entity_get_## name ## _custom(ipmi_entity_t *entity, \ unsigned int num, \ char *str, \ unsigned int *str_len) \ { \ CHECK_ENTITY_LOCK(entity); \ if (!entity->fru) \ return ENOSYS; \ return ipmi_fru_get_ ## name ## _custom(entity->fru, num, str, str_len);\ } FRU_VAL_GET(unsigned char, internal_use_version) FRU_VAL_GET(unsigned int, internal_use_length) int ipmi_entity_get_internal_use_data(ipmi_entity_t *entity, unsigned char *data, unsigned int *max_len) { CHECK_ENTITY_LOCK(entity); if (!entity->fru) return ENOSYS; return ipmi_fru_get_internal_use_data(entity->fru, data, max_len); } FRU_VAL_GET(unsigned char, chassis_info_version) FRU_VAL_GET(unsigned char, chassis_info_type) FRU_VAL_GET(unsigned int, chassis_info_part_number_len) FRU_VAL_GET(enum ipmi_str_type_e, chassis_info_part_number_type) FRU_STR_GET(chassis_info_part_number) FRU_VAL_GET(unsigned int, chassis_info_serial_number_len) FRU_VAL_GET(enum ipmi_str_type_e, chassis_info_serial_number_type) FRU_STR_GET(chassis_info_serial_number) FRU_CUSTOM_GET(chassis_info) FRU_VAL_GET(unsigned char, board_info_version) FRU_VAL_GET(unsigned char, board_info_lang_code) int ipmi_entity_get_board_info_mfg_time(ipmi_entity_t *entity, time_t *time) { CHECK_ENTITY_LOCK(entity); if (!entity->fru) return ENOSYS; return ipmi_fru_get_board_info_mfg_time(entity->fru, time); } FRU_VAL_GET(unsigned int, board_info_board_manufacturer_len) FRU_VAL_GET(enum ipmi_str_type_e, board_info_board_manufacturer_type) FRU_STR_GET(board_info_board_manufacturer) FRU_VAL_GET(unsigned int, board_info_board_product_name_len) FRU_VAL_GET(enum ipmi_str_type_e, board_info_board_product_name_type) FRU_STR_GET(board_info_board_product_name) FRU_VAL_GET(unsigned int, board_info_board_serial_number_len) FRU_VAL_GET(enum ipmi_str_type_e, board_info_board_serial_number_type) FRU_STR_GET(board_info_board_serial_number) FRU_VAL_GET(unsigned int, board_info_board_part_number_len) FRU_VAL_GET(enum ipmi_str_type_e, board_info_board_part_number_type) FRU_STR_GET(board_info_board_part_number) FRU_VAL_GET(unsigned int, board_info_fru_file_id_len) FRU_VAL_GET(enum ipmi_str_type_e, board_info_fru_file_id_type) FRU_STR_GET(board_info_fru_file_id) FRU_CUSTOM_GET(board_info) FRU_VAL_GET(unsigned char, product_info_version) FRU_VAL_GET(unsigned char, product_info_lang_code) FRU_VAL_GET(unsigned int, product_info_manufacturer_name_len) FRU_VAL_GET(enum ipmi_str_type_e, product_info_manufacturer_name_type) FRU_STR_GET(product_info_manufacturer_name) FRU_VAL_GET(unsigned int, product_info_product_name_len) FRU_VAL_GET(enum ipmi_str_type_e, product_info_product_name_type) FRU_STR_GET(product_info_product_name) FRU_VAL_GET(unsigned int, product_info_product_part_model_number_len) FRU_VAL_GET(enum ipmi_str_type_e, product_info_product_part_model_number_type) FRU_STR_GET(product_info_product_part_model_number) FRU_VAL_GET(unsigned int, product_info_product_version_len) FRU_VAL_GET(enum ipmi_str_type_e, product_info_product_version_type) FRU_STR_GET(product_info_product_version) FRU_VAL_GET(unsigned int, product_info_product_serial_number_len) FRU_VAL_GET(enum ipmi_str_type_e, product_info_product_serial_number_type) FRU_STR_GET(product_info_product_serial_number) FRU_VAL_GET(unsigned int, product_info_asset_tag_len) FRU_VAL_GET(enum ipmi_str_type_e, product_info_asset_tag_type) FRU_STR_GET(product_info_asset_tag) FRU_VAL_GET(unsigned int, product_info_fru_file_id_len) FRU_VAL_GET(enum ipmi_str_type_e, product_info_fru_file_id_type) FRU_STR_GET(product_info_fru_file_id) FRU_CUSTOM_GET(product_info) unsigned int ipmi_entity_get_num_multi_records(ipmi_entity_t *entity) { CHECK_ENTITY_LOCK(entity); if (!entity->fru) return 0; return ipmi_fru_get_num_multi_records(entity->fru); } int ipmi_entity_get_multi_record_type(ipmi_entity_t *entity, unsigned int num, unsigned char *type) { CHECK_ENTITY_LOCK(entity); if (!entity->fru) return ENOSYS; return ipmi_fru_get_multi_record_type(entity->fru, num, type); } int ipmi_entity_get_multi_record_format_version(ipmi_entity_t *entity, unsigned int num, unsigned char *ver) { CHECK_ENTITY_LOCK(entity); if (!entity->fru) return ENOSYS; return ipmi_fru_get_multi_record_format_version(entity->fru, num, ver); } int ipmi_entity_get_multi_record_data_len(ipmi_entity_t *entity, unsigned int num, unsigned int *len) { CHECK_ENTITY_LOCK(entity); if (!entity->fru) return ENOSYS; return ipmi_fru_get_multi_record_data_len(entity->fru, num, len); } int ipmi_entity_get_multi_record_data(ipmi_entity_t *entity, unsigned int num, unsigned char *data, unsigned int *length) { CHECK_ENTITY_LOCK(entity); if (!entity->fru) return ENOSYS; return ipmi_fru_get_multi_record_data(entity->fru, num, data, length); }