/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 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. */ #ifndef _SOC_QCOM_GLINK_PRIVATE_H_ #define _SOC_QCOM_GLINK_PRIVATE_H_ #include #include #include #include #include #include #include #include #include #include #include #include struct glink_core_xprt_ctx; struct channel_ctx; enum transport_state_e; enum local_channel_state_e; /* Logging Macros */ enum { QCOM_GLINK_INFO = 1U << 0, QCOM_GLINK_DEBUG = 1U << 1, QCOM_GLINK_GPIO = 1U << 2, QCOM_GLINK_PERF = 1U << 3, }; enum glink_dbgfs_ss { GLINK_DBGFS_MPSS, GLINK_DBGFS_APSS, GLINK_DBGFS_LPASS, GLINK_DBGFS_DSPS, GLINK_DBGFS_RPM, GLINK_DBGFS_WCNSS, GLINK_DBGFS_LLOOP, GLINK_DBGFS_MOCK, GLINK_DBGFS_MAX_NUM_SUBS }; enum glink_dbgfs_xprt { GLINK_DBGFS_SMEM, GLINK_DBGFS_SMD, GLINK_DBGFS_XLLOOP, GLINK_DBGFS_XMOCK, GLINK_DBGFS_XMOCK_LOW, GLINK_DBGFS_XMOCK_HIGH, GLINK_DBGFS_MAX_NUM_XPRTS }; struct glink_dbgfs { const char *curr_name; const char *par_name; bool b_dir_create; }; struct glink_dbgfs_data { struct list_head flist; struct dentry *dent; void (*o_func)(struct seq_file *s); void *priv_data; bool b_priv_free_req; }; struct xprt_ctx_iterator { struct list_head *xprt_list; struct glink_core_xprt_ctx *i_curr; unsigned long xprt_list_flags; }; struct ch_ctx_iterator { struct list_head *ch_list; struct channel_ctx *i_curr; unsigned long ch_list_flags; }; struct glink_ch_intent_info { spinlock_t *li_lst_lock; struct list_head *li_avail_list; struct list_head *li_used_list; spinlock_t *ri_lst_lock; struct list_head *ri_list; }; /* Tracer Packet Event IDs for G-Link */ enum glink_tracer_pkt_events { GLINK_CORE_TX = 1, GLINK_QUEUE_TO_SCHEDULER = 2, GLINK_SCHEDULER_TX = 3, GLINK_XPRT_TX = 4, GLINK_XPRT_RX = 5, GLINK_CORE_RX = 6, }; /** * glink_get_ss_enum_string() - get the name of the subsystem based on enum value * @enum_id: enum id of a specific subsystem. * * Return: name of the subsystem, NULL in case of invalid input */ const char *glink_get_ss_enum_string(unsigned int enum_id); /** * glink_get_xprt_enum_string() - get the name of the transport based on enum value * @enum_id: enum id of a specific transport. * * Return: name of the transport, NULL in case of invalid input */ const char *glink_get_xprt_enum_string(unsigned int enum_id); /** * glink_get_xprt_state_string() - get the name of the transport based on enum value * @enum_id: enum id of the state of the transport. * * Return: name of the transport state, NULL in case of invalid input */ const char *glink_get_xprt_state_string(enum transport_state_e enum_id); /** * glink_get_ch_state_string() - get the name of the transport based on enum value * @enum_id: enum id of a specific state of the channel. * * Return: name of the channel state, NULL in case of invalid input */ const char *glink_get_ch_state_string(enum local_channel_state_e enum_id); #define GLINK_IPC_LOG_STR(x...) do { \ if (glink_get_log_ctx()) \ ipc_log_string(glink_get_log_ctx(), x); \ } while (0) #define GLINK_DBG(x...) do { \ if (glink_get_debug_mask() & QCOM_GLINK_DEBUG) \ GLINK_IPC_LOG_STR(x); \ } while (0) #define GLINK_INFO(x...) do { \ if (glink_get_debug_mask() & QCOM_GLINK_INFO) \ GLINK_IPC_LOG_STR(x); \ } while (0) #define GLINK_INFO_PERF(x...) do { \ if (glink_get_debug_mask() & (QCOM_GLINK_INFO | QCOM_GLINK_PERF)) \ GLINK_IPC_LOG_STR(x); \ } while (0) #define GLINK_PERF(x...) do { \ if (glink_get_debug_mask() & QCOM_GLINK_PERF) \ GLINK_IPC_LOG_STR(" " x); \ } while (0) #define GLINK_UT_ERR(x...) do { \ if (!(glink_get_debug_mask() & QCOM_GLINK_PERF)) \ pr_err(" " x); \ GLINK_IPC_LOG_STR(" " x); \ } while (0) #define GLINK_UT_DBG(x...) do { \ if (glink_get_debug_mask() & QCOM_GLINK_DEBUG) \ GLINK_IPC_LOG_STR(" " x); \ } while (0) #define GLINK_UT_INFO(x...) do { \ if (glink_get_debug_mask() & QCOM_GLINK_INFO) \ GLINK_IPC_LOG_STR(" " x); \ } while (0) #define GLINK_UT_INFO_PERF(x...) do { \ if (glink_get_debug_mask() & (QCOM_GLINK_INFO | QCOM_GLINK_PERF)) \ GLINK_IPC_LOG_STR(" " x); \ } while (0) #define GLINK_UT_PERF(x...) do { \ if (glink_get_debug_mask() & QCOM_GLINK_PERF) \ GLINK_IPC_LOG_STR(" " x); \ } while (0) #define GLINK_XPRT_IPC_LOG_STR(xprt, x...) do { \ if (glink_get_xprt_log_ctx(xprt)) \ ipc_log_string(glink_get_xprt_log_ctx(xprt), x); \ } while (0) #define GLINK_XPRT_IF_INFO(xprt_if, x...) do { \ if (glink_get_debug_mask() & QCOM_GLINK_INFO) \ GLINK_XPRT_IPC_LOG_STR(xprt_if.glink_core_priv, " " x); \ } while (0) #define GLINK_XPRT_IF_DBG(xprt_if, x...) do { \ if (glink_get_debug_mask() & QCOM_GLINK_DEBUG) \ GLINK_XPRT_IPC_LOG_STR(xprt_if.glink_core_priv, " " x); \ } while (0) #define GLINK_XPRT_IF_ERR(xprt_if, x...) do { \ pr_err(" " x); \ GLINK_XPRT_IPC_LOG_STR(xprt_if.glink_core_priv, " " x); \ } while (0) #define GLINK_PERF_XPRT(xprt, fmt, args...) do { \ if (glink_get_debug_mask() & QCOM_GLINK_PERF) \ GLINK_XPRT_IPC_LOG_STR(xprt, " %s:%s " fmt, \ xprt->name, xprt->edge, args); \ } while (0) #define GLINK_PERF_CH(ctx, fmt, args...) do { \ if (glink_get_debug_mask() & QCOM_GLINK_PERF) \ GLINK_XPRT_IPC_LOG_STR(ctx->transport_ptr, \ " %s:%s:%s[%u:%u] " fmt, \ ctx->transport_ptr->name, \ ctx->transport_ptr->edge, \ ctx->name, \ ctx->lcid, \ ctx->rcid, args); \ } while (0) #define GLINK_PERF_CH_XPRT(ctx, xprt, fmt, args...) do { \ if (glink_get_debug_mask() & QCOM_GLINK_PERF) \ GLINK_XPRT_IPC_LOG_STR(xprt, \ " %s:%s:%s[%u:%u] " fmt, \ xprt->name, \ xprt->edge, \ ctx->name, \ ctx->lcid, \ ctx->rcid, args); \ } while (0) #define GLINK_INFO_PERF_XPRT(xprt, fmt, args...) do { \ if (glink_get_debug_mask() & (QCOM_GLINK_INFO | QCOM_GLINK_PERF)) \ GLINK_XPRT_IPC_LOG_STR(xprt, " %s:%s " fmt, \ xprt->name, xprt->edge, args); \ } while (0) #define GLINK_INFO_PERF_CH(ctx, fmt, args...) do { \ if (glink_get_debug_mask() & (QCOM_GLINK_INFO | QCOM_GLINK_PERF)) \ GLINK_XPRT_IPC_LOG_STR(ctx->transport_ptr, \ " %s:%s:%s[%u:%u] " fmt, \ ctx->transport_ptr->name, \ ctx->transport_ptr->edge, \ ctx->name, \ ctx->lcid, \ ctx->rcid, args); \ } while (0) #define GLINK_INFO_PERF_CH_XPRT(ctx, xprt, fmt, args...) do { \ if (glink_get_debug_mask() & (QCOM_GLINK_INFO | QCOM_GLINK_PERF)) \ GLINK_XPRT_IPC_LOG_STR(xprt,\ " %s:%s:%s[%u:%u] " fmt, \ xprt->name, \ xprt->edge, \ ctx->name, \ ctx->lcid, \ ctx->rcid, args); \ } while (0) #define GLINK_INFO_XPRT(xprt, fmt, args...) do { \ if (glink_get_debug_mask() & QCOM_GLINK_INFO) \ GLINK_XPRT_IPC_LOG_STR(xprt, " %s:%s " fmt, \ xprt->name, xprt->edge, args); \ } while (0) #define GLINK_INFO_CH(ctx, fmt, args...) do { \ if (glink_get_debug_mask() & QCOM_GLINK_INFO) \ GLINK_XPRT_IPC_LOG_STR(ctx->transport_ptr, \ " %s:%s:%s[%u:%u] " fmt, \ ctx->transport_ptr->name, \ ctx->transport_ptr->edge, \ ctx->name, \ ctx->lcid, \ ctx->rcid, args); \ } while (0) #define GLINK_INFO_CH_XPRT(ctx, xprt, fmt, args...) do { \ if (glink_get_debug_mask() & QCOM_GLINK_INFO) \ GLINK_XPRT_IPC_LOG_STR(xprt, \ " %s:%s:%s[%u:%u] " fmt, \ xprt->name, \ xprt->edge, \ ctx->name, \ ctx->lcid, \ ctx->rcid, args); \ } while (0) #define GLINK_DBG_XPRT(xprt, fmt, args...) do { \ if (glink_get_debug_mask() & QCOM_GLINK_DEBUG) \ GLINK_XPRT_IPC_LOG_STR(xprt, " %s:%s " fmt, \ xprt->name, xprt->edge, args); \ } while (0) #define GLINK_DBG_CH(ctx, fmt, args...) do { \ if (glink_get_debug_mask() & QCOM_GLINK_DEBUG) \ GLINK_XPRT_IPC_LOG_STR(ctx->transport_ptr, \ " %s:%s:%s[%u:%u] " fmt, \ ctx->transport_ptr->name, \ ctx->transport_ptr->edge, \ ctx->name, \ ctx->lcid, \ ctx->rcid, args); \ } while (0) #define GLINK_DBG_CH_XPRT(ctx, xprt, fmt, args...) do { \ if (glink_get_debug_mask() & QCOM_GLINK_DEBUG) \ GLINK_XPRT_IPC_LOG_STR(xprt, \ " %s:%s:%s[%u:%u] " fmt, \ xprt->name, \ xprt->edge, \ ctx->name, \ ctx->lcid, \ ctx->rcid, args); \ } while (0) #define GLINK_ERR(x...) do { \ pr_err_ratelimited(" " x); \ GLINK_IPC_LOG_STR(" " x); \ } while (0) #define GLINK_ERR_XPRT(xprt, fmt, args...) do { \ pr_err_ratelimited(" %s:%s " fmt, \ xprt->name, xprt->edge, args); \ GLINK_INFO_XPRT(xprt, fmt, args); \ } while (0) #define GLINK_ERR_CH(ctx, fmt, args...) do { \ pr_err_ratelimited(" %s:%s:%s[%u:%u] " fmt, \ ctx->transport_ptr->name, \ ctx->transport_ptr->edge, \ ctx->name, \ ctx->lcid, \ ctx->rcid, args); \ GLINK_INFO_CH(ctx, fmt, args); \ } while (0) #define GLINK_ERR_CH_XPRT(ctx, xprt, fmt, args...) do { \ pr_err_ratelimited(" %s:%s:%s[%u:%u] " fmt, \ xprt->name, \ xprt->edge, \ ctx->name, \ ctx->lcid, \ ctx->rcid, args); \ GLINK_INFO_CH_XPRT(ctx, xprt, fmt, args); \ } while (0) /** * OVERFLOW_ADD_UNSIGNED() - check for unsigned overflow * * type: type to check for overflow * a: left value to use * b: right value to use * returns: true if a + b will result in overflow; false otherwise */ #define OVERFLOW_ADD_UNSIGNED(type, a, b) \ (((type)~0 - (a)) < (b) ? true : false) /** * glink_get_debug_mask() - Return debug mask attribute * * Return: debug mask attribute */ unsigned glink_get_debug_mask(void); /** * glink_get_log_ctx() - Return log context for other GLINK modules. * * Return: Log context or NULL if none. */ void *glink_get_log_ctx(void); /** * glink_get_xprt_log_ctx() - Return log context for other GLINK modules. * * Return: Log context or NULL if none. */ void *glink_get_xprt_log_ctx(struct glink_core_xprt_ctx *xprt); /** * glink_get_channel_id_for_handle() - Get logical channel ID * * @handle: handle of channel * * Used internally by G-Link debugfs. * * Return: Logical Channel ID or standard Linux error code */ int glink_get_channel_id_for_handle(void *handle); /** * glink_get_channel_name_for_handle() - return channel name * * @handle: handle of channel * * Used internally by G-Link debugfs. * * Return: Channel name or NULL */ char *glink_get_channel_name_for_handle(void *handle); /** * glink_debugfs_init() - initialize glink debugfs directory * * Return: error code or success. */ int glink_debugfs_init(void); /** * glink_debugfs_exit() - removes glink debugfs directory */ void glink_debugfs_exit(void); /** * glink_debugfs_create() - create the debugfs file * @name: debugfs file name * @show: pointer to the actual function which will be invoked upon * opening this file. * @dir: pointer to a structure debugfs_dir * @dbgfs_data: pointer to any private data need to be associated with debugfs * @b_free_req: boolean value to decide to free the memory associated with * @dbgfs_data during deletion of the file * * Return: pointer to the file/directory created, NULL in case of error * * This function checks which directory will be used to create the debugfs file * and calls glink_dfs_create_file. Anybody who intend to allocate some memory * for the dbgfs_data and required to free it in deletion, need to set * b_free_req to true. Otherwise, there will be a memory leak. */ struct dentry *glink_debugfs_create(const char *name, void (*show)(struct seq_file *), struct glink_dbgfs *dir, void *dbgfs_data, bool b_free_req); /** * glink_debugfs_remove_recur() - remove the the directory & files recursively * @rm_dfs: pointer to the structure glink_dbgfs * * This function removes the files & directories. This also takes care of * freeing any memory associated with the debugfs file. */ void glink_debugfs_remove_recur(struct glink_dbgfs *dfs); /** * glink_debugfs_remove_channel() - remove all channel specifc files & folder in * debugfs when channel is fully closed * @ch_ctx: pointer to the channel_contenxt * @xprt_ctx: pointer to the transport_context * * This function is invoked when any channel is fully closed. It removes the * folders & other files in debugfs for that channel. */ void glink_debugfs_remove_channel(struct channel_ctx *ch_ctx, struct glink_core_xprt_ctx *xprt_ctx); /** * glink_debugfs_add_channel() - create channel specifc files & folder in * debugfs when channel is added * @ch_ctx: pointer to the channel_contenxt * @xprt_ctx: pointer to the transport_context * * This function is invoked when a new channel is created. It creates the * folders & other files in debugfs for that channel */ void glink_debugfs_add_channel(struct channel_ctx *ch_ctx, struct glink_core_xprt_ctx *xprt_ctx); /** * glink_debugfs_add_xprt() - create transport specifc files & folder in * debugfs when new transport is registerd * @xprt_ctx: pointer to the transport_context * * This function is invoked when a new transport is registered. It creates the * folders & other files in debugfs for that transport */ void glink_debugfs_add_xprt(struct glink_core_xprt_ctx *xprt_ctx); /** * glink_xprt_ctx_iterator_init() - Initializes the transport context list iterator * @xprt_i: pointer to the transport context iterator. * * Return: None * * This function acquires the transport context lock which must then be * released by glink_xprt_ctx_iterator_end() */ void glink_xprt_ctx_iterator_init(struct xprt_ctx_iterator *xprt_i); /** * glink_xprt_ctx_iterator_end() - Ends the transport context list iteration * @xprt_i: pointer to the transport context iterator. * * Return: None */ void glink_xprt_ctx_iterator_end(struct xprt_ctx_iterator *xprt_i); /** * glink_xprt_ctx_iterator_next() - iterates element by element in transport context list * @xprt_i: pointer to the transport context iterator. * * Return: pointer to the transport context structure */ struct glink_core_xprt_ctx *glink_xprt_ctx_iterator_next( struct xprt_ctx_iterator *xprt_i); /** * glink_get_xprt_name() - get the transport name * @xprt_ctx: pointer to the transport context. * * Return: name of the transport */ char *glink_get_xprt_name(struct glink_core_xprt_ctx *xprt_ctx); /** * glink_get_xprt_edge_name() - get the name of the remote processor/edge * of the transport * @xprt_ctx: pointer to the transport context. * * Return: name of the remote processor/edge */ char *glink_get_xprt_edge_name(struct glink_core_xprt_ctx *xprt_ctx); /** * glink_get_xprt_state() - get the state of the transport * @xprt_ctx: pointer to the transport context. * * Return: name of the transport state, NULL in case of invalid input */ const char *glink_get_xprt_state(struct glink_core_xprt_ctx *xprt_ctx); /** * glink_get_xprt_version_features() - get the version and feature set * of local transport in glink * @xprt_ctx: pointer to the transport context. * * Return: pointer to the glink_core_version */ const struct glink_core_version *glink_get_xprt_version_features( struct glink_core_xprt_ctx *xprt_ctx); /** * glink_ch_ctx_iterator_init() - Initializes the channel context list iterator * @ch_iter: pointer to the channel context iterator. * @xprt: pointer to the transport context that holds the channel list * * This function acquires the channel context lock which must then be * released by glink_ch_ctx_iterator_end() */ void glink_ch_ctx_iterator_init(struct ch_ctx_iterator *ch_iter, struct glink_core_xprt_ctx *xprt); /** * glink_ch_ctx_iterator_end() - Ends the channel context list iteration * @ch_iter: pointer to the channel context iterator. * */ void glink_ch_ctx_iterator_end(struct ch_ctx_iterator *ch_iter, struct glink_core_xprt_ctx *xprt); /** * glink_ch_ctx_iterator_next() - iterates element by element in channel context list * @c_i: pointer to the channel context iterator. * * Return: pointer to the channel context structure */ struct channel_ctx *glink_ch_ctx_iterator_next(struct ch_ctx_iterator *ch_iter); /** * glink_get_ch_name() - get the channel name * @ch_ctx: pointer to the channel context. * * Return: name of the channel, NULL in case of invalid input */ char *glink_get_ch_name(struct channel_ctx *ch_ctx); /** * glink_get_ch_edge_name() - get the name of the remote processor/edge * of the channel * @xprt_ctx: pointer to the channel context. * * Return: name of the remote processor/edge */ char *glink_get_ch_edge_name(struct channel_ctx *ch_ctx); /** * glink_get_ch_rcid() - get the remote channel ID * @ch_ctx: pointer to the channel context. * * Return: remote channel id, -EINVAL in case of invalid input */ int glink_get_ch_lcid(struct channel_ctx *ch_ctx); /** * glink_get_ch_rcid() - get the remote channel ID * @ch_ctx: pointer to the channel context. * * Return: remote channel id, -EINVAL in case of invalid input */ int glink_get_ch_rcid(struct channel_ctx *ch_ctx); /** * glink_get_ch_lstate() - get the local channel state * @ch_ctx: pointer to the channel context. * * Return: name of the local channel state, NULL in case of invalid input */ const char *glink_get_ch_lstate(struct channel_ctx *ch_ctx); /** * glink_get_ch_rstate() - get the remote channel state * @ch_ctx: pointer to the channel context. * * Return: true if remote side is opened false otherwise */ bool glink_get_ch_rstate(struct channel_ctx *ch_ctx); /** * glink_get_ch_xprt_name() - get the name of the transport to which * the channel belongs * @ch_ctx: pointer to the channel context. * * Return: name of the export, NULL in case of invalid input */ char *glink_get_ch_xprt_name(struct channel_ctx *ch_ctx); /** * glink_get_tx_pkt_count() - get the total number of packets sent * through this channel * @ch_ctx: pointer to the channel context. * * Return: number of packets transmitted, -EINVAL in case of invalid input */ int glink_get_ch_tx_pkt_count(struct channel_ctx *ch_ctx); /** * glink_get_ch_rx_pkt_count() - get the total number of packets * recieved at this channel * @ch_ctx: pointer to the channel context. * * Return: number of packets recieved, -EINVAL in case of invalid input */ int glink_get_ch_rx_pkt_count(struct channel_ctx *ch_ctx); /** * glink_get_ch_lintents_queued() - get the total number of intents queued * at local side * @ch_ctx: pointer to the channel context. * * Return: number of intents queued, -EINVAL in case of invalid input */ int glink_get_ch_lintents_queued(struct channel_ctx *ch_ctx); /** * glink_get_ch_rintents_queued() - get the total number of intents queued * from remote side * @ch_ctx: pointer to the channel context. * * Return: number of intents queued */ int glink_get_ch_rintents_queued(struct channel_ctx *ch_ctx); /** * glink_get_ch_intent_info() - get the intent details of a channel * @ch_ctx: pointer to the channel context. * @ch_ctx_i: pointer to a structure that will contain intent details * * This funcion is used to get all the channel intent details including locks. */ void glink_get_ch_intent_info(struct channel_ctx *ch_ctx, struct glink_ch_intent_info *ch_ctx_i); /** * enum ssr_command - G-Link SSR protocol commands */ enum ssr_command { GLINK_SSR_DO_CLEANUP, GLINK_SSR_CLEANUP_DONE, }; /** * struct ssr_notify_data - Contains private data used for client notifications * from G-Link. * tx_done: Indicates whether or not the tx_done notification has * been received. * event: The state notification event received. * responded: Indicates whether or not a cleanup_done response was * received. * edge: The G-Link edge name for the channel associated with * this callback data * do_cleanup_data: Structure containing the G-Link SSR do_cleanup message. */ struct ssr_notify_data { bool tx_done; unsigned event; bool responded; const char *edge; struct do_cleanup_msg *do_cleanup_data; }; /** * struct subsys_info - Subsystem info structure * ssr_name: name of the subsystem recognized by the SSR framework * edge: name of the G-Link edge * xprt: name of the G-Link transport * handle: glink_ssr channel used for this subsystem * link_state_handle: link state handle for this edge, used to unregister * from receiving link state callbacks * link_info: Transport info used in link state callback registration * cb_data: Private callback data structure for notification * functions * subsystem_list_node: used to chain this structure in a list of subsystem * info structures * notify_list: list of subsys_info_leaf structures, containing the * subsystems to notify if this subsystem undergoes SSR * notify_list_len: length of notify_list * link_up: Flag indicating whether transport is up or not * link_up_lock: Lock for protecting the link_up flag */ struct subsys_info { const char *ssr_name; const char *edge; const char *xprt; void *handle; void *link_state_handle; struct glink_link_info *link_info; struct ssr_notify_data *cb_data; struct list_head subsystem_list_node; struct list_head notify_list; int notify_list_len; bool link_up; spinlock_t link_up_lock; }; /** * struct subsys_info_leaf - Subsystem info leaf structure (a subsystem on the * notify list of a subsys_info structure) * ssr_name: Name of the subsystem recognized by the SSR framework * edge: Name of the G-Link edge * xprt: Name of the G-Link transport * restarted: Indicates whether a restart has been triggered for this edge * cb_data: Private callback data structure for notification functions * notify_list_node: used to chain this structure in the notify list */ struct subsys_info_leaf { const char *ssr_name; const char *edge; const char *xprt; bool restarted; struct ssr_notify_data *cb_data; struct list_head notify_list_node; }; /** * struct do_cleanup_msg - The data structure for an SSR do_cleanup message * version: The G-Link SSR protocol version * command: The G-Link SSR command - do_cleanup * seq_num: Sequence number * name_len: Length of the name of the subsystem being restarted * name: G-Link edge name of the subsystem being restarted */ struct do_cleanup_msg { uint32_t version; uint32_t command; uint32_t seq_num; uint32_t name_len; char name[32]; }; /** * struct cleanup_done_msg - The data structure for an SSR cleanup_done message * version: The G-Link SSR protocol version * response: The G-Link SSR response to a do_cleanup command, cleanup_done * seq_num: Sequence number */ struct cleanup_done_msg { uint32_t version; uint32_t response; uint32_t seq_num; }; /** * get_info_for_subsystem() - Retrieve information about a subsystem from the * global subsystem_info_list * @subsystem: The name of the subsystem recognized by the SSR * framework * * Return: subsys_info structure containing info for the requested subsystem; * NULL if no structure can be found for the requested subsystem */ struct subsys_info *get_info_for_subsystem(const char *subsystem); /** * get_info_for_edge() - Retrieve information about a subsystem from the * global subsystem_info_list * @edge: The name of the edge recognized by G-Link * * Return: subsys_info structure containing info for the requested subsystem; * NULL if no structure can be found for the requested subsystem */ struct subsys_info *get_info_for_edge(const char *edge); /** * glink_ssr_get_seq_num() - Get the current SSR sequence number * * Return: The current SSR sequence number */ uint32_t glink_ssr_get_seq_num(void); /* * glink_reinit_ssr() - reinit the glink state * * Return: Standard error code. */ int glink_reinit_ssr(const char *subsystem); /* * glink_ssr() - SSR cleanup function. * * Return: Standard error code. */ int glink_ssr(const char *subsystem); /** * notify for subsystem() - Notify other subsystems that a subsystem is being * restarted * @ss_info: Subsystem info structure for the subsystem being restarted * * This function sends notifications to affected subsystems that the subsystem * in ss_info is being restarted, and waits for the cleanup done response from * all of those subsystems. It also initiates any local cleanup that is * necessary. * * Return: 0 on success, standard error codes otherwise */ int notify_for_subsystem(struct subsys_info *ss_info); /** * glink_ssr_wait_cleanup_done() - Get the value of the * notifications_successful flag in glink_ssr. * @timeout_multiplier: timeout multiplier for waiting on all processors * * * Return: True if cleanup_done received from all processors, false otherwise */ bool glink_ssr_wait_cleanup_done(unsigned ssr_timeout_multiplier); struct channel_lcid { struct list_head list_node; uint32_t lcid; }; /** * struct rwref_lock - Read/Write Reference Lock * * kref: reference count * read_count: number of readers that own the lock * write_count: number of writers (max 1) that own the lock * count_zero: used for internal signaling for non-atomic locks * * A Read/Write Reference Lock is a combination of a read/write spinlock and a * refence count. The main difference is that no locks are held in the * critical section and the lifetime of the object is guaranteed. * * Read Locking * Multiple readers may access the lock at any given time and a read lock will * also ensure that the object exists for the life of the lock. * * rwref_read_get() * use resource in "critical section" (no locks are held) * rwref_read_put() * * Write Locking * A single writer may access the lock at any given time and a write lock will * also ensure that the object exists for the life of the lock. * * rwref_write_get() * use resource in "critical section" (no locks are held) * rwref_write_put() * * Reference Lock * To ensure the lifetime of the lock (and not affect the read or write lock), * a simple reference can be done. By default, rwref_lock_init() will set the * reference count to 1. * * rwref_lock_init() Reference count is 1 * rwref_get() Reference count is 2 * rwref_put() Reference count is 1 * rwref_put() Reference count goes to 0 and object is destroyed */ struct rwref_lock { struct kref kref; unsigned read_count; unsigned write_count; spinlock_t lock; wait_queue_head_t count_zero; void (*release)(struct rwref_lock *); }; /** * rwref_lock_release() - Initialize rwref_lock * lock_ptr: pointer to lock structure */ static inline void rwref_lock_release(struct kref *kref_ptr) { struct rwref_lock *lock_ptr; BUG_ON(kref_ptr == NULL); lock_ptr = container_of(kref_ptr, struct rwref_lock, kref); if (lock_ptr->release) lock_ptr->release(lock_ptr); } /** * rwref_lock_init() - Initialize rwref_lock * lock_ptr: pointer to lock structure * release: release function called when reference count goes to 0 */ static inline void rwref_lock_init(struct rwref_lock *lock_ptr, void (*release)(struct rwref_lock *)) { BUG_ON(lock_ptr == NULL); kref_init(&lock_ptr->kref); lock_ptr->read_count = 0; lock_ptr->write_count = 0; spin_lock_init(&lock_ptr->lock); init_waitqueue_head(&lock_ptr->count_zero); lock_ptr->release = release; } /** * rwref_get() - gains a reference count for the object * lock_ptr: pointer to lock structure */ static inline void rwref_get(struct rwref_lock *lock_ptr) { BUG_ON(lock_ptr == NULL); kref_get(&lock_ptr->kref); } /** * rwref_put() - puts a reference count for the object * lock_ptr: pointer to lock structure * * If the reference count goes to zero, the release function is called. */ static inline void rwref_put(struct rwref_lock *lock_ptr) { BUG_ON(lock_ptr == NULL); kref_put(&lock_ptr->kref, rwref_lock_release); } /** * rwref_read_get_atomic() - gains a reference count for a read operation * lock_ptr: pointer to lock structure * is_atomic: if True, do not wait when acquiring lock * * Multiple readers may acquire the lock as long as the write count is zero. */ static inline void rwref_read_get_atomic(struct rwref_lock *lock_ptr, bool is_atomic) { unsigned long flags; BUG_ON(lock_ptr == NULL); kref_get(&lock_ptr->kref); while (1) { spin_lock_irqsave(&lock_ptr->lock, flags); if (lock_ptr->write_count == 0) { lock_ptr->read_count++; spin_unlock_irqrestore(&lock_ptr->lock, flags); break; } spin_unlock_irqrestore(&lock_ptr->lock, flags); if (!is_atomic) { wait_event(lock_ptr->count_zero, lock_ptr->write_count == 0); } } } /** * rwref_read_get() - gains a reference count for a read operation * lock_ptr: pointer to lock structure * * Multiple readers may acquire the lock as long as the write count is zero. */ static inline void rwref_read_get(struct rwref_lock *lock_ptr) { rwref_read_get_atomic(lock_ptr, false); } /** * rwref_read_put() - returns a reference count for a read operation * lock_ptr: pointer to lock structure * * Must be preceded by a call to rwref_read_get(). */ static inline void rwref_read_put(struct rwref_lock *lock_ptr) { unsigned long flags; BUG_ON(lock_ptr == NULL); spin_lock_irqsave(&lock_ptr->lock, flags); BUG_ON(lock_ptr->read_count == 0); if (--lock_ptr->read_count == 0) wake_up(&lock_ptr->count_zero); spin_unlock_irqrestore(&lock_ptr->lock, flags); kref_put(&lock_ptr->kref, rwref_lock_release); } /** * rwref_write_get_atomic() - gains a reference count for a write operation * lock_ptr: pointer to lock structure * is_atomic: if True, do not wait when acquiring lock * * Only one writer may acquire the lock as long as the reader count is zero. */ static inline void rwref_write_get_atomic(struct rwref_lock *lock_ptr, bool is_atomic) { unsigned long flags; BUG_ON(lock_ptr == NULL); kref_get(&lock_ptr->kref); while (1) { spin_lock_irqsave(&lock_ptr->lock, flags); if (lock_ptr->read_count == 0 && lock_ptr->write_count == 0) { lock_ptr->write_count++; spin_unlock_irqrestore(&lock_ptr->lock, flags); break; } spin_unlock_irqrestore(&lock_ptr->lock, flags); if (!is_atomic) { wait_event(lock_ptr->count_zero, (lock_ptr->read_count == 0 && lock_ptr->write_count == 0)); } } } /** * rwref_write_get() - gains a reference count for a write operation * lock_ptr: pointer to lock structure * * Only one writer may acquire the lock as long as the reader count is zero. */ static inline void rwref_write_get(struct rwref_lock *lock_ptr) { rwref_write_get_atomic(lock_ptr, false); } /** * rwref_write_put() - returns a reference count for a write operation * lock_ptr: pointer to lock structure * * Must be preceded by a call to rwref_write_get(). */ static inline void rwref_write_put(struct rwref_lock *lock_ptr) { unsigned long flags; BUG_ON(lock_ptr == NULL); spin_lock_irqsave(&lock_ptr->lock, flags); BUG_ON(lock_ptr->write_count != 1); if (--lock_ptr->write_count == 0) wake_up(&lock_ptr->count_zero); spin_unlock_irqrestore(&lock_ptr->lock, flags); kref_put(&lock_ptr->kref, rwref_lock_release); } #endif /* _SOC_QCOM_GLINK_PRIVATE_H_ */