/****************************************************************************** * * Copyright(c) 2019 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * *****************************************************************************/ #define _PHL_LED_C_ #include "phl_headers.h" #define PHL_LED_INTERVALS_ARR_LEN_MAX 4 struct phl_led_event_args_t { enum rtw_led_state state_condition; struct rtw_led_action_args_t *action_args_arr; u8 action_args_arr_len; u32 toggle_delay_unit; struct phl_led_event_args_t *next; }; struct phl_led_timer_args_t { struct phl_info_t *phl_info; _os_timer timer; u32 delay_unit; bool timer_alive; bool is_avail; u32 led_manage_mask; }; struct phl_led_info_t { enum rtw_led_ctrl_mode ctrl_mode; enum rtw_led_ctrl_mode reg_ctrl_mode; enum rtw_led_opt curr_opt; const struct rtw_led_toggle_args_t *toggle_args; struct phl_led_timer_args_t *toggle_timer_args; u32 toggle_interval_counter; u32 toggle_start_delay_counter; bool toggle_start_delay_over; u32 toggle_loop_counter; u8 toggle_curr_interval_idx; }; struct phl_led_ctrl_t { struct phl_led_info_t led_info_arr[RTW_LED_ID_LENGTH]; struct phl_led_event_args_t *event_args_list_arr[RTW_LED_EVENT_LENGTH]; struct rtw_led_intervals_t intervals_arr[PHL_LED_INTERVALS_ARR_LEN_MAX]; enum rtw_led_state state; struct phl_led_timer_args_t *toggle_timer_args[RTW_LED_TIMER_LENGTH]; }; static void _phl_led_timer_release(struct phl_led_timer_args_t *timer_args) { void *drv_priv = phl_to_drvpriv(timer_args->phl_info); PHL_TRACE(COMP_PHL_LED, _PHL_INFO_, "%s: led_manage_mask == 0x%x\n", __func__, timer_args->led_manage_mask); _os_cancel_timer(drv_priv, &(timer_args->timer)); _os_release_timer(drv_priv, &(timer_args->timer)); _os_mem_free(drv_priv, timer_args, sizeof(struct phl_led_timer_args_t)); } static void _phl_led_remove_from_timer(struct phl_led_info_t *led_info, enum rtw_led_id led_id) { u32 *mask = NULL; if (led_info->toggle_timer_args != NULL) { mask = &(led_info->toggle_timer_args->led_manage_mask); *mask &= ~(BIT(led_id)); if (*mask == 0) led_info->toggle_timer_args->timer_alive = false; led_info->toggle_timer_args = NULL; led_info->toggle_args = NULL; } } static void _phl_led_timer_cb_done(void* priv, struct phl_msg* msg) { struct phl_led_timer_args_t *timer_args = (struct phl_led_timer_args_t *)(msg->inbuf); if (!timer_args->timer_alive) timer_args->is_avail = true; } static void _phl_led_timer_cb(void *args) { struct phl_led_timer_args_t *timer_args = (struct phl_led_timer_args_t *) args; enum rtw_phl_status phl_status = RTW_PHL_STATUS_SUCCESS; struct phl_info_t *phl_info = timer_args->phl_info; struct phl_msg msg = {0}; struct phl_msg_attribute attr = {0}; SET_MSG_MDL_ID_FIELD(msg.msg_id, PHL_MDL_LED); SET_MSG_EVT_ID_FIELD(msg.msg_id, MSG_EVT_LED_TICK); msg.band_idx = HW_BAND_0; msg.inbuf = (u8 *)(timer_args); msg.inlen = sizeof(struct phl_led_timer_args_t); attr.completion.completion = _phl_led_timer_cb_done; attr.completion.priv = phl_info; phl_status = phl_disp_eng_send_msg(timer_args->phl_info, &msg, &attr, NULL); if(phl_status != RTW_PHL_STATUS_SUCCESS){ PHL_ERR("%s: phl_disp_eng_send_msg failed!\n", __func__); timer_args->timer_alive = false; _phl_led_timer_cb_done(phl_info, &msg); } } static enum rtw_phl_status _phl_led_ctrl_write_opt(void *hal, enum rtw_led_id led_id, enum rtw_led_opt *curr_opt, enum rtw_led_opt opt) { if (opt >= RTW_LED_OPT_UNKNOWN) { PHL_TRACE(COMP_PHL_LED, _PHL_INFO_, "%s: unknown opt (%d)\n", __func__, opt); return RTW_PHL_STATUS_FAILURE; } if (RTW_HAL_STATUS_SUCCESS != rtw_hal_led_control(hal, led_id, opt)) return RTW_PHL_STATUS_FAILURE; *curr_opt = opt; return RTW_PHL_STATUS_SUCCESS; } static enum rtw_phl_status _phl_led_ctrl_start_delay_hdlr(void *hal, struct phl_led_info_t *led_info, enum rtw_led_id led_id) { if (led_info->toggle_start_delay_counter >= led_info->toggle_args->start_delay) { led_info->toggle_start_delay_over = true; PHL_TRACE(COMP_PHL_LED, _PHL_INFO_, "%s: start delay is over\n", __func__); if (RTW_PHL_STATUS_SUCCESS != _phl_led_ctrl_write_opt(hal, led_id, &(led_info->curr_opt), led_info->toggle_args->start_opt)) return RTW_PHL_STATUS_FAILURE; return RTW_PHL_STATUS_SUCCESS; } (led_info->toggle_start_delay_counter)++; return RTW_PHL_STATUS_SUCCESS; } static enum rtw_phl_status _phl_led_ctrl_interval_hdlr(void *hal, struct phl_led_info_t *led_info, enum rtw_led_id led_id, struct rtw_led_intervals_t *intervals) { u32 interval = 0; enum rtw_led_opt opt = RTW_LED_OPT_UNKNOWN; if (intervals == NULL) { PHL_TRACE(COMP_PHL_LED, _PHL_INFO_, "%s: intervals == NULL\n", __func__); return RTW_PHL_STATUS_FAILURE; } if (intervals->interval_arr == NULL) { PHL_TRACE(COMP_PHL_LED, _PHL_INFO_, "%s: interval_arr == NULL\n", __func__); return RTW_PHL_STATUS_FAILURE; } if (led_info->toggle_curr_interval_idx >= intervals->len) { PHL_TRACE( COMP_PHL_LED, _PHL_INFO_, "%s: curr_interval_idx ( %d ) >= intervals' len ( %d )\n", __func__, led_info->toggle_curr_interval_idx, intervals->len); return RTW_PHL_STATUS_FAILURE; } interval = intervals->interval_arr[led_info->toggle_curr_interval_idx]; PHL_TRACE(COMP_PHL_LED, _PHL_INFO_, "%s: curr_interval_idx == %d, interval == %d, " "interval_counter == %d\n", __func__, led_info->toggle_curr_interval_idx, interval, led_info->toggle_interval_counter); if (interval > ++(led_info->toggle_interval_counter)) /* it is not time to toggle */ return RTW_PHL_STATUS_SUCCESS; led_info->toggle_interval_counter = 0; /* set curr_interval_idx to next */ if (++(led_info->toggle_curr_interval_idx) >= intervals->len) { led_info->toggle_curr_interval_idx = 0; if (led_info->toggle_args->loop > 0) (led_info->toggle_loop_counter)++; } PHL_TRACE(COMP_PHL_LED, _PHL_INFO_, "%s: toggle led_id: %d\n", __func__, led_id); if (led_info->curr_opt == RTW_LED_OPT_LOW) opt = RTW_LED_OPT_HIGH; else if (led_info->curr_opt == RTW_LED_OPT_HIGH) opt = RTW_LED_OPT_LOW; else { PHL_TRACE(COMP_PHL_LED, _PHL_INFO_, "%s: incorrect curr_opt ( %d ). led_id: %d\n", __func__, led_info->curr_opt, led_id); return RTW_PHL_STATUS_FAILURE; } if (RTW_PHL_STATUS_SUCCESS != _phl_led_ctrl_write_opt(hal, led_id, &(led_info->curr_opt), opt)) return RTW_PHL_STATUS_FAILURE; return RTW_PHL_STATUS_SUCCESS; } static enum rtw_phl_status _phl_led_ctrl_toggle_hdlr(struct phl_led_timer_args_t *timer_args) { enum rtw_phl_status status = RTW_PHL_STATUS_SUCCESS; void *drv_priv = phl_to_drvpriv(timer_args->phl_info); enum rtw_led_id led_id = 0; struct phl_led_ctrl_t *led_ctrl = (struct phl_led_ctrl_t *)(timer_args->phl_info->led_ctrl); struct phl_led_info_t *led_info = NULL; u8 intervals_idx = 0; for (led_id = 0; led_id < RTW_LED_ID_LENGTH; led_id++) { if ((timer_args->led_manage_mask & BIT(led_id)) == 0) continue; led_info = &(led_ctrl->led_info_arr[led_id]); /* start_delay handling */ if (!led_info->toggle_start_delay_over) { if (RTW_PHL_STATUS_SUCCESS != _phl_led_ctrl_start_delay_hdlr( timer_args->phl_info->hal, led_info, led_id)) { status = RTW_PHL_STATUS_FAILURE; } continue; } /* start_delay is over, handle intervals */ intervals_idx = led_info->toggle_args->intervals_idx; if (RTW_PHL_STATUS_SUCCESS != _phl_led_ctrl_interval_hdlr( timer_args->phl_info->hal, led_info, led_id, &(led_ctrl->intervals_arr[intervals_idx]))) { PHL_TRACE(COMP_PHL_LED, _PHL_INFO_, "%s: intervals handling failed. led_id: %d\n", __func__, led_id); status = RTW_PHL_STATUS_FAILURE; } if (led_info->toggle_args->loop > 0 && led_info->toggle_args->loop == led_info->toggle_loop_counter) { _phl_led_remove_from_timer(led_info, led_id); } } if (timer_args->timer_alive) _os_set_timer(drv_priv, &(timer_args->timer), timer_args->delay_unit); return status; } static enum rtw_phl_status _phl_led_ctrl_action_hdlr(struct phl_info_t *phl_info, enum rtw_led_id led_id, enum rtw_led_action action, struct rtw_led_toggle_args_t *toggle_args, struct phl_led_timer_args_t **timer_args_ptr) { enum rtw_phl_status status = RTW_PHL_STATUS_SUCCESS; struct phl_led_ctrl_t *led_ctrl = (struct phl_led_ctrl_t *)(phl_info->led_ctrl); struct phl_led_info_t *led_info = &(led_ctrl->led_info_arr[led_id]); enum rtw_led_ctrl_mode target_ctrl_mode; u8 i = 0; PHL_TRACE(COMP_PHL_LED, _PHL_INFO_, "%s: led_id == %d, action == 0X%X\n", __func__, led_id, action); /* Set ctrl mode*/ switch (action) { case RTW_LED_ACTION_LOW: case RTW_LED_ACTION_HIGH: case RTW_LED_ACTION_TOGGLE: target_ctrl_mode = led_info->reg_ctrl_mode; break; case RTW_LED_ACTION_HW_TRX: target_ctrl_mode = RTW_LED_CTRL_HW_TRX_MODE; break; default: target_ctrl_mode = led_info->ctrl_mode; break; } if(led_info->ctrl_mode != target_ctrl_mode){ if (rtw_hal_led_set_ctrl_mode(phl_info->hal, led_id, target_ctrl_mode)){ status = RTW_PHL_STATUS_FAILURE; return status; } led_info->ctrl_mode = target_ctrl_mode; } /* Sw action */ switch (action) { case RTW_LED_ACTION_LOW: if(led_info->ctrl_mode != RTW_LED_CTRL_SW_PP_MODE && led_info->ctrl_mode != RTW_LED_CTRL_SW_OD_MODE) break; _phl_led_remove_from_timer(led_info, led_id); if (RTW_PHL_STATUS_SUCCESS != _phl_led_ctrl_write_opt(phl_info->hal, led_id, &(led_info->curr_opt), RTW_LED_OPT_LOW)) status = RTW_PHL_STATUS_FAILURE; break; case RTW_LED_ACTION_HIGH: if(led_info->ctrl_mode != RTW_LED_CTRL_SW_PP_MODE && led_info->ctrl_mode != RTW_LED_CTRL_SW_OD_MODE) break; _phl_led_remove_from_timer(led_info, led_id); if (RTW_PHL_STATUS_SUCCESS != _phl_led_ctrl_write_opt(phl_info->hal, led_id, &(led_info->curr_opt), RTW_LED_OPT_HIGH)) status = RTW_PHL_STATUS_FAILURE; break; case RTW_LED_ACTION_HW_TRX: _phl_led_remove_from_timer(led_info, led_id); break; case RTW_LED_ACTION_TOGGLE: if(led_info->ctrl_mode != RTW_LED_CTRL_SW_PP_MODE && led_info->ctrl_mode != RTW_LED_CTRL_SW_OD_MODE) break; _phl_led_remove_from_timer(led_info, led_id); led_info->toggle_args = toggle_args; led_info->toggle_interval_counter = 0; led_info->toggle_start_delay_counter = toggle_args->start_delay; led_info->toggle_start_delay_over = false; led_info->toggle_loop_counter = 0; led_info->toggle_curr_interval_idx = 0; if (*timer_args_ptr == NULL) { for (i = 0; i < RTW_LED_TIMER_LENGTH; i++) { if (led_ctrl->toggle_timer_args[i] == NULL) continue; if (led_ctrl->toggle_timer_args[i]->is_avail == true) { *timer_args_ptr = led_ctrl->toggle_timer_args[i]; (*timer_args_ptr)->is_avail = false; break; } } if (*timer_args_ptr == NULL) { PHL_ERR("%s: get available timer failed!\n", __func__); break; } (*timer_args_ptr)->phl_info = phl_info; (*timer_args_ptr)->led_manage_mask = 0; (*timer_args_ptr)->timer_alive = true; (*timer_args_ptr)->delay_unit = 0; } (*timer_args_ptr)->led_manage_mask |= BIT(led_id); led_info->toggle_timer_args = *timer_args_ptr; break; default: status = RTW_PHL_STATUS_FAILURE; break; } return status; } static enum rtw_phl_status _phl_led_ctrl_event_hdlr(struct phl_info_t *phl_info, enum rtw_led_event event) { enum rtw_phl_status status = RTW_PHL_STATUS_SUCCESS; struct phl_led_event_args_t *event_args = NULL; struct phl_led_ctrl_t *led_ctrl = (struct phl_led_ctrl_t *)(phl_info->led_ctrl); u8 args_idx; enum rtw_led_id led_id; struct phl_led_info_t *led_info = NULL; struct rtw_led_action_args_t *action_args = NULL; struct phl_led_timer_args_t *timer_args = NULL; if(event >= RTW_LED_EVENT_LENGTH){ PHL_TRACE(COMP_PHL_LED, _PHL_INFO_, "%s: invalid event(0X%X) >= RTW_LED_EVENT_LENGTH(0X%X).\n", __func__, event, RTW_LED_EVENT_LENGTH); return RTW_PHL_STATUS_FAILURE; } PHL_TRACE(COMP_PHL_LED, _PHL_INFO_, "%s: event == 0X%X\n", __func__, event); /* set state */ switch (event) { case RTW_LED_EVENT_SW_RF_ON: PHL_TRACE(COMP_PHL_LED, _PHL_INFO_, "%s: set state sw rf on\n", __func__); led_ctrl->state |= RTW_LED_STATE_SW_RF_ON; break; case RTW_LED_EVENT_SW_RF_OFF: PHL_TRACE(COMP_PHL_LED, _PHL_INFO_, "%s: set state sw rf off\n", __func__); led_ctrl->state &= ~RTW_LED_STATE_SW_RF_ON; break; default: break; } /* handle event */ event_args = led_ctrl->event_args_list_arr[event]; /* event_args = list head */ for (; event_args != NULL; event_args = event_args->next) { if (!(event_args->state_condition & (led_ctrl->state | RTW_LED_STATE_IGNORE))) continue; timer_args = NULL; for (args_idx = 0; args_idx < event_args->action_args_arr_len; args_idx++) { action_args = &(event_args->action_args_arr[args_idx]); led_id = action_args->led_id; led_info = &(led_ctrl->led_info_arr[led_id]); if (RTW_PHL_STATUS_SUCCESS != _phl_led_ctrl_action_hdlr( phl_info, led_id, action_args->led_action, &(action_args->toggle_args), &timer_args)) { status = RTW_PHL_STATUS_FAILURE; } } if (timer_args == NULL) continue; timer_args->delay_unit = event_args->toggle_delay_unit; if (RTW_PHL_STATUS_SUCCESS != _phl_led_ctrl_toggle_hdlr(timer_args)) status = RTW_PHL_STATUS_FAILURE; } return status; } static enum phl_mdl_ret_code _phl_led_module_init(void *phl, void *dispr, void **priv) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; void *drv_priv = phl_to_drvpriv(phl_info); struct phl_led_ctrl_t *led_ctrl = NULL; enum rtw_led_id led_id = 0; enum rtw_led_event event_id = 0; struct phl_led_info_t *led_info = NULL; struct rtw_led_intervals_t *intervals = NULL; u8 intervals_idx = 0, i = 0; struct phl_led_timer_args_t *timer_args = NULL; PHL_TRACE(COMP_PHL_LED, _PHL_INFO_, "===> _phl_led_module_init()\n"); if (NULL == (led_ctrl = _os_mem_alloc(drv_priv, sizeof(struct phl_led_ctrl_t)))) { PHL_ERR("%s: alloc buffer failed!\n", __func__); phl_info->led_ctrl = NULL; return MDL_RET_FAIL; } *priv = phl; phl_info->led_ctrl = led_ctrl; /* set default value in led_ctrl */ led_ctrl->state = 0; for (led_id = 0; led_id < RTW_LED_ID_LENGTH; led_id++) { led_info = &(led_ctrl->led_info_arr[led_id]); led_info->ctrl_mode = RTW_LED_CTRL_NOT_SUPPORT; led_info->reg_ctrl_mode = RTW_LED_CTRL_NOT_SUPPORT; led_info->curr_opt = RTW_LED_OPT_UNKNOWN; led_info->toggle_interval_counter = 0; led_info->toggle_start_delay_counter = 0; led_info->toggle_start_delay_over = false; led_info->toggle_loop_counter = 0; led_info->toggle_curr_interval_idx = 0; led_info->toggle_timer_args = NULL; led_info->toggle_args = NULL; } for (event_id = 0; event_id < RTW_LED_EVENT_LENGTH; event_id++) { led_ctrl->event_args_list_arr[event_id] = NULL; } for (intervals_idx = 0; intervals_idx < PHL_LED_INTERVALS_ARR_LEN_MAX; intervals_idx++) { intervals = &(led_ctrl->intervals_arr[intervals_idx]); intervals->interval_arr = NULL; intervals->len = 0; } for (i = 0; i < RTW_LED_TIMER_LENGTH; i++) { if (NULL == (timer_args = _os_mem_alloc( drv_priv, sizeof(struct phl_led_timer_args_t)))) { PHL_ERR("%s: alloc #%d timer buffer failed!\n", __func__, i); led_ctrl->toggle_timer_args[i] = NULL; continue; } timer_args->phl_info = phl_info; _os_init_timer(drv_priv, &(timer_args->timer), _phl_led_timer_cb, timer_args, "phl_led_timer"); timer_args->delay_unit = 0; timer_args->timer_alive = false; timer_args->is_avail = true; timer_args->led_manage_mask = 0; led_ctrl->toggle_timer_args[i] = timer_args; } return MDL_RET_SUCCESS; } static void _phl_led_module_deinit(void *dispr, void *priv) { struct phl_info_t *phl_info = (struct phl_info_t *)priv; void *drv_priv = phl_to_drvpriv(phl_info); struct phl_led_ctrl_t *led_ctrl = (struct phl_led_ctrl_t *)(phl_info->led_ctrl); enum rtw_led_event event = 0; struct phl_led_event_args_t *event_args = NULL; struct phl_led_event_args_t *event_args_next = NULL; struct rtw_led_intervals_t *intervals = NULL; u8 intervals_idx = 0, i = 0; enum rtw_led_id led_id = 0; struct phl_led_info_t *led_info = NULL; PHL_TRACE(COMP_PHL_LED, _PHL_INFO_, "===> _phl_led_module_deinit()\n"); if (led_ctrl == NULL) return; /* free event_args_list_arr */ for (event = 0; event < RTW_LED_EVENT_LENGTH; event++) { event_args = led_ctrl->event_args_list_arr[event]; if (event_args == NULL) continue; while (event_args != NULL) { event_args_next = event_args->next; if (event_args->action_args_arr != NULL) _os_mem_free( drv_priv, event_args->action_args_arr, event_args->action_args_arr_len * sizeof(struct rtw_led_action_args_t)); _os_mem_free(drv_priv, event_args, sizeof(struct phl_led_event_args_t)); event_args = event_args_next; } } /* free intervals_arr */ for (intervals_idx = 0; intervals_idx < PHL_LED_INTERVALS_ARR_LEN_MAX; intervals_idx++) { intervals = &(led_ctrl->intervals_arr[intervals_idx]); if (intervals->interval_arr == NULL) continue; _os_mem_free(drv_priv, intervals->interval_arr, intervals->len * sizeof(u32)); } /* free all timers */ for (led_id = 0; led_id < RTW_LED_ID_LENGTH; led_id++) { led_info = &(led_ctrl->led_info_arr[led_id]); if (led_info->toggle_timer_args == NULL) continue; _phl_led_remove_from_timer(led_info, led_id); } for (i = 0; i < RTW_LED_TIMER_LENGTH; i++) { if (led_ctrl->toggle_timer_args[i] == NULL) continue; _phl_led_timer_release(led_ctrl->toggle_timer_args[i]); } _os_mem_free(drv_priv, led_ctrl, sizeof(struct phl_led_ctrl_t)); phl_info->led_ctrl = NULL; } static enum phl_mdl_ret_code _phl_led_module_start(void *dispr, void *priv) { struct phl_info_t *phl_info = (struct phl_info_t *)priv; struct phl_led_ctrl_t *led_ctrl = (struct phl_led_ctrl_t *)(phl_info->led_ctrl); enum phl_mdl_ret_code ret = MDL_RET_SUCCESS; enum rtw_led_id led_id = 0; PHL_TRACE(COMP_PHL_LED, _PHL_INFO_, "===> _phl_led_module_start()\n"); if (led_ctrl == NULL) { PHL_TRACE(COMP_PHL_LED, _PHL_INFO_, "%s: led_ctrl == NULL\n", __func__); return MDL_RET_FAIL; } for (led_id = 0; led_id < RTW_LED_ID_LENGTH; led_id++) { if (RTW_HAL_STATUS_SUCCESS != rtw_hal_led_set_ctrl_mode( phl_info->hal, led_id, led_ctrl->led_info_arr[led_id].ctrl_mode)) ret = MDL_RET_FAIL; } if (RTW_PHL_STATUS_SUCCESS != _phl_led_ctrl_event_hdlr(phl_info, RTW_LED_EVENT_PHL_START)) ret = MDL_RET_FAIL; if (RTW_PHL_STATUS_SUCCESS != _phl_led_ctrl_event_hdlr(phl_info, RTW_LED_EVENT_SW_RF_ON)) ret = MDL_RET_FAIL; return ret; } static enum phl_mdl_ret_code _phl_led_module_stop(void *dispr, void *priv) { struct phl_info_t *phl_info = (struct phl_info_t *)priv; void *drv_priv = phl_to_drvpriv(phl_info); struct phl_led_ctrl_t *led_ctrl = (struct phl_led_ctrl_t *)(phl_info->led_ctrl); enum phl_mdl_ret_code ret = MDL_RET_SUCCESS; u8 i = 0; PHL_TRACE(COMP_PHL_LED, _PHL_INFO_, "===> _phl_led_module_stop()\n"); if (led_ctrl == NULL) { PHL_TRACE(COMP_PHL_LED, _PHL_INFO_, "%s: led_ctrl == NULL\n", __func__); return MDL_RET_FAIL; } if (RTW_PHL_STATUS_SUCCESS != _phl_led_ctrl_event_hdlr(phl_info, RTW_LED_EVENT_SW_RF_OFF)) ret = MDL_RET_FAIL; if (RTW_PHL_STATUS_SUCCESS != _phl_led_ctrl_event_hdlr(phl_info, RTW_LED_EVENT_PHL_STOP)) ret = MDL_RET_FAIL; for (i = 0; i < RTW_LED_TIMER_LENGTH; i++) { _os_cancel_timer(drv_priv, &(led_ctrl->toggle_timer_args[i]->timer)); led_ctrl->toggle_timer_args[i]->timer_alive = false; led_ctrl->toggle_timer_args[i]->is_avail = true; } return ret; } static enum phl_mdl_ret_code _phl_led_module_msg_hdlr(void *dispr, void *priv, struct phl_msg *msg) { struct phl_info_t *phl_info = (struct phl_info_t *)priv; struct phl_led_ctrl_t *led_ctrl = (struct phl_led_ctrl_t *)(phl_info->led_ctrl); enum phl_msg_evt_id msg_evt_id = MSG_EVT_ID_FIELD(msg->msg_id); struct phl_led_timer_args_t *timer_args = NULL; PHL_TRACE(COMP_PHL_LED, _PHL_INFO_, "===> _phl_led_module_msg_hdlr()\n"); if (IS_MSG_IN_PRE_PHASE(msg->msg_id)) return MDL_RET_SUCCESS; if (MSG_MDL_ID_FIELD(msg->msg_id) != PHL_MDL_LED) return MDL_RET_IGNORE; if(IS_MSG_CANNOT_IO(msg->msg_id)) return MDL_RET_FAIL; if (led_ctrl == NULL) { PHL_TRACE(COMP_PHL_LED, _PHL_INFO_, "%s: led_ctrl == NULL\n", __func__); return MDL_RET_FAIL; } if(msg_evt_id < MSG_EVT_LED_EVT_START || msg_evt_id > MSG_EVT_LED_EVT_END){ if (msg_evt_id == MSG_EVT_LED_TICK) { PHL_TRACE(COMP_PHL_LED, _PHL_INFO_, "%s: MSG_EVT_LED_TICK\n", __func__); timer_args = (struct phl_led_timer_args_t *)(msg->inbuf); if (!timer_args->timer_alive) return MDL_RET_SUCCESS; if (RTW_PHL_STATUS_SUCCESS != _phl_led_ctrl_toggle_hdlr(timer_args)) return MDL_RET_FAIL; return MDL_RET_SUCCESS; } } else { if (RTW_PHL_STATUS_SUCCESS != _phl_led_ctrl_event_hdlr(phl_info, msg_evt_id - MSG_EVT_LED_EVT_START)) return MDL_RET_FAIL; } return MDL_RET_SUCCESS; } static enum phl_mdl_ret_code _phl_led_module_set_info(void *dispr, void *priv, struct phl_module_op_info *info) { return MDL_RET_SUCCESS; } static enum phl_mdl_ret_code _phl_led_module_query_info(void *dispr, void *priv, struct phl_module_op_info *info) { return MDL_RET_SUCCESS; } enum rtw_phl_status phl_register_led_module(struct phl_info_t *phl_info) { #ifdef CONFIG_CMD_DISP enum rtw_phl_status phl_status = RTW_PHL_STATUS_FAILURE; struct phl_bk_module_ops bk_ops; bk_ops.init = _phl_led_module_init; bk_ops.deinit = _phl_led_module_deinit; bk_ops.start = _phl_led_module_start; bk_ops.stop = _phl_led_module_stop; bk_ops.msg_hdlr = _phl_led_module_msg_hdlr; bk_ops.set_info = _phl_led_module_set_info; bk_ops.query_info = _phl_led_module_query_info; phl_status = phl_disp_eng_register_module(phl_info, HW_BAND_0, PHL_MDL_LED, &bk_ops); if (phl_status != RTW_PHL_STATUS_SUCCESS) { PHL_ERR("%s register LED module in cmd disp failed\n", __func__); phl_status = RTW_PHL_STATUS_FAILURE; } return phl_status; #else return RTW_PHL_STATUS_FAILURE; #endif } void rtw_phl_led_set_ctrl_mode(void *phl, enum rtw_led_id led_id, enum rtw_led_ctrl_mode ctrl_mode) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct phl_led_ctrl_t *led_ctrl = (struct phl_led_ctrl_t *)phl_info->led_ctrl; PHL_TRACE(COMP_PHL_LED, _PHL_INFO_, "===> %s()\n", __func__); if (led_ctrl == NULL) { PHL_TRACE(COMP_PHL_LED, _PHL_INFO_, "%s: led_ctrl == NULL\n", __func__); return; } PHL_TRACE(COMP_PHL_LED, _PHL_INFO_, "%s: led_id == %d, ctrl_mode == %d\n", __func__, led_id, ctrl_mode); led_ctrl->led_info_arr[led_id].ctrl_mode = ctrl_mode; led_ctrl->led_info_arr[led_id].reg_ctrl_mode = ctrl_mode; } void rtw_phl_led_set_toggle_intervals(void *phl, u8 intervals_idx, u32 *interval_arr, u8 intervals_len) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct phl_led_ctrl_t *led_ctrl = (struct phl_led_ctrl_t *)phl_info->led_ctrl; void *drv_priv = phl_to_drvpriv(phl_info); struct rtw_led_intervals_t *intervals = NULL; PHL_TRACE(COMP_PHL_LED, _PHL_INFO_, "===> rtw_phl_led_set_toggle_intervals()\n"); if (led_ctrl == NULL) { PHL_TRACE(COMP_PHL_LED, _PHL_INFO_, "%s: led_ctrl == NULL\n", __func__); return; } if (intervals_idx >= PHL_LED_INTERVALS_ARR_LEN_MAX) { PHL_TRACE( COMP_PHL_LED, _PHL_INFO_, "%s: intervals_idx >= PHL_LED_INTERVALS_ARR_LEN_MAX\n", __func__); return; } intervals = &(led_ctrl->intervals_arr[intervals_idx]); /* check if the target intervals_arr has already been set */ if (intervals->interval_arr != NULL) { PHL_TRACE(COMP_PHL_LED, _PHL_INFO_, "%s: intervals_arr[%d] has already been set. " "The new one is going to replace the old one!\n", __func__, intervals_idx); _os_mem_free(drv_priv, intervals->interval_arr, intervals->len * sizeof(u32)); intervals->interval_arr = NULL; intervals->len = 0; } if (NULL == (intervals->interval_arr = _os_mem_alloc( drv_priv, intervals_len * sizeof(u32)))) { PHL_ERR("%s: alloc buffer failed!\n", __func__); return; } _os_mem_cpy(drv_priv, intervals->interval_arr, interval_arr, intervals_len * sizeof(u32)); intervals->len = intervals_len; return; } void rtw_phl_led_set_action(void *phl, enum rtw_led_event event, enum rtw_led_state state_condition, struct rtw_led_action_args_t *action_args_arr, u8 action_args_arr_len, u32 toggle_delay_unit) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct phl_led_ctrl_t *led_ctrl = (struct phl_led_ctrl_t *)phl_info->led_ctrl; void *drv_priv = phl_to_drvpriv(phl_info); struct phl_led_event_args_t *event_args = NULL; struct phl_led_event_args_t *event_args_prev = NULL; PHL_TRACE(COMP_PHL_LED, _PHL_INFO_, "===> %s()\n", __func__); if (led_ctrl == NULL) { PHL_TRACE(COMP_PHL_LED, _PHL_INFO_, "%s: led_ctrl == NULL\n", __func__); return; } if (action_args_arr == NULL) { PHL_TRACE(COMP_PHL_LED, _PHL_INFO_, "%s: input -- action_args_arr == NULL\n", __func__); return; } PHL_TRACE(COMP_PHL_LED, _PHL_INFO_, "%s: event == %d, state_condition == %d\n", __func__, event, state_condition); event_args = led_ctrl->event_args_list_arr[event]; /* event_args = list head */ while (event_args != NULL) { if (event_args->state_condition == state_condition) { PHL_TRACE( COMP_PHL_LED, _PHL_INFO_, "%s: the event_args_list of event 0x%x already has " "a node with state_condition == 0x%x\n", __func__, event, state_condition); return; } event_args_prev = event_args; event_args = event_args->next; } if (NULL == (event_args = _os_mem_alloc( drv_priv, sizeof(struct phl_led_event_args_t)))) { PHL_ERR("%s: alloc buffer failed!\n", __func__); return; } if (NULL == (event_args->action_args_arr = _os_mem_alloc( drv_priv, action_args_arr_len * sizeof(struct rtw_led_action_args_t)))) { PHL_ERR("%s: alloc buffer failed!\n", __func__); _os_mem_free(drv_priv, event_args, sizeof(struct phl_led_event_args_t)); return; } event_args->action_args_arr_len = action_args_arr_len; event_args->state_condition = state_condition; event_args->toggle_delay_unit = toggle_delay_unit; _os_mem_cpy(drv_priv, event_args->action_args_arr, action_args_arr, action_args_arr_len * sizeof(struct rtw_led_action_args_t)); event_args->next = NULL; if (event_args_prev == NULL) /* the event_args_list was empty */ led_ctrl->event_args_list_arr[event] = event_args; else event_args_prev->next = event_args; } void phl_led_control(struct phl_info_t *phl_info, enum rtw_led_event led_event) { #ifdef CONFIG_CMD_DISP struct phl_msg msg = {0}; struct phl_msg_attribute attr = {0}; PHL_TRACE(COMP_PHL_LED, _PHL_INFO_, "===> rtw_phl_led_control()\n"); SET_MSG_MDL_ID_FIELD(msg.msg_id, PHL_MDL_LED); /* * led_event here is passed via the msg_evt_id field instead of * msg_evt_id due to the following reason: * * (a) led_event is used for mapping LED events with LED actions, and * the mapping can be configured in core layer according to the * customized LED table. * * (b) LED module inside uses led_event as the index of led action * arrays, and hence it would be inappropriate to directly replace * led_event with msg_evt_id which is not continuous and does not * start from zero. * * (c) It is not worth it to use inbuf with the overhead of dynamic * allocation and completion callback only for a number. */ SET_MSG_EVT_ID_FIELD(msg.msg_id, led_event + MSG_EVT_LED_EVT_START); msg.band_idx = HW_BAND_0; phl_disp_eng_send_msg(phl_info, &msg, &attr, NULL); #else PHL_ERR("phl_fsm not support %s\n", __func__); #endif } void rtw_phl_led_control(void *phl, enum rtw_led_event led_event) { phl_led_control((struct phl_info_t *)phl, led_event); }