blob: dc45b20d2e461f85ec54682c45e71b4dde530b50 [file] [log] [blame]
Channagoud Kadabi4d480b02016-12-20 11:57:51 -08001/*
Satya Durga Srinivasu Prabhala34947112017-01-16 10:37:08 -08002 * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
Channagoud Kadabi4d480b02016-12-20 11:57:51 -08003 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 */
14
15#define pr_fmt(fmt) "service-notifier: %s: " fmt, __func__
16
17#include <linux/kernel.h>
18#include <linux/module.h>
19#include <linux/string.h>
20#include <linux/completion.h>
21#include <linux/slab.h>
22#include <linux/of.h>
23#include <linux/err.h>
Channagoud Kadabi4d480b02016-12-20 11:57:51 -080024#include <linux/uaccess.h>
25
26#include <soc/qcom/subsystem_restart.h>
27#include <soc/qcom/subsystem_notif.h>
28#include <soc/qcom/sysmon.h>
29#include <soc/qcom/service-locator.h>
30#include <soc/qcom/service-notifier.h>
31#include "service-notifier-private.h"
32
Channagoud Kadabi4d480b02016-12-20 11:57:51 -080033#define SERVREG_NOTIF_NAME_LENGTH QMI_SERVREG_NOTIF_NAME_LENGTH_V01
34#define SERVREG_NOTIF_SERVICE_ID SERVREG_NOTIF_SERVICE_ID_V01
35#define SERVREG_NOTIF_SERVICE_VERS SERVREG_NOTIF_SERVICE_VERS_V01
36
37#define SERVREG_NOTIF_SET_ACK_REQ \
38 QMI_SERVREG_NOTIF_STATE_UPDATED_IND_ACK_REQ_V01
39#define SERVREG_NOTIF_SET_ACK_REQ_MSG_LEN \
40 QMI_SERVREG_NOTIF_SET_ACK_REQ_MSG_V01_MAX_MSG_LEN
41#define SERVREG_NOTIF_SET_ACK_RESP \
42 QMI_SERVREG_NOTIF_STATE_UPDATED_IND_ACK_RESP_V01
43#define SERVREG_NOTIF_SET_ACK_RESP_MSG_LEN \
44 QMI_SERVREG_NOTIF_SET_ACK_RESP_MSG_V01_MAX_MSG_LEN
45#define SERVREG_NOTIF_STATE_UPDATED_IND_MSG \
46 QMI_SERVREG_NOTIF_STATE_UPDATED_IND_V01
47#define SERVREG_NOTIF_STATE_UPDATED_IND_MSG_LEN \
48 QMI_SERVREG_NOTIF_STATE_UPDATED_IND_MSG_V01_MAX_MSG_LEN
49
50#define SERVREG_NOTIF_REGISTER_LISTENER_REQ \
51 QMI_SERVREG_NOTIF_REGISTER_LISTENER_REQ_V01
52#define SERVREG_NOTIF_REGISTER_LISTENER_REQ_MSG_LEN \
53 QMI_SERVREG_NOTIF_REGISTER_LISTENER_REQ_MSG_V01_MAX_MSG_LEN
54#define SERVREG_NOTIF_REGISTER_LISTENER_RESP \
55 QMI_SERVREG_NOTIF_REGISTER_LISTENER_RESP_V01
56#define SERVREG_NOTIF_REGISTER_LISTENER_RESP_MSG_LEN \
57 QMI_SERVREG_NOTIF_REGISTER_LISTENER_RESP_MSG_V01_MAX_MSG_LEN
58
59#define QMI_STATE_MIN_VAL QMI_SERVREG_NOTIF_SERVICE_STATE_ENUM_TYPE_MIN_VAL_V01
60#define QMI_STATE_MAX_VAL QMI_SERVREG_NOTIF_SERVICE_STATE_ENUM_TYPE_MAX_VAL_V01
61
62#define SERVER_TIMEOUT 500
63#define MAX_STRING_LEN 100
64
65/*
66 * Per user service data structure
67 * struct service_notif_info - notifier struct for each unique service path
68 * service_path - service provider path/location
69 * instance_id - service instance id specific to a subsystem
70 * service_notif_rcvr_list - list of clients interested in this service
71 * providers notifications
72 * curr_state: Current state of the service
73 */
74struct service_notif_info {
75 char service_path[SERVREG_NOTIF_NAME_LENGTH];
76 int instance_id;
77 struct srcu_notifier_head service_notif_rcvr_list;
78 struct list_head list;
79 int curr_state;
80};
81static LIST_HEAD(service_list);
82static DEFINE_MUTEX(service_list_lock);
83
84struct ind_req_resp {
85 char service_path[SERVREG_NOTIF_NAME_LENGTH];
86 int transaction_id;
Avaneesh Kumar Dwivedieda8e2b2017-04-26 17:08:54 +053087 int curr_state;
Channagoud Kadabi4d480b02016-12-20 11:57:51 -080088};
89
90/*
91 * Per Root Process Domain (Root service) data structure
92 * struct qmi_client_info - QMI client info for each subsystem/instance id
93 * instance_id - service instance id specific to a subsystem (Root PD)
94 * clnt_handle - unique QMI client handle
95 * service_connected - indicates if QMI service is up on the subsystem
96 * ssr_handle - The SSR handle provided by the SSR driver for the subsystem
97 * on which the remote root PD runs.
98 */
99struct qmi_client_info {
100 int instance_id;
Avaneesh Kumar Dwivedi7b506d22017-03-17 16:41:13 +0530101 enum pd_subsys_state subsys_state;
Channagoud Kadabi4d480b02016-12-20 11:57:51 -0800102 struct work_struct svc_arrive;
103 struct work_struct svc_exit;
104 struct work_struct svc_rcv_msg;
105 struct work_struct ind_ack;
Avaneesh Kumar Dwivedibcb8f372017-03-25 18:01:09 +0530106 struct work_struct qmi_handle_free;
Channagoud Kadabi4d480b02016-12-20 11:57:51 -0800107 struct workqueue_struct *svc_event_wq;
Kyle Yan879dc5d2017-11-30 13:36:53 -0800108 struct rw_semaphore qmi_client_handle_rwlock;
Channagoud Kadabi4d480b02016-12-20 11:57:51 -0800109 struct qmi_handle *clnt_handle;
110 struct notifier_block notifier;
111 void *ssr_handle;
112 struct notifier_block ssr_notifier;
113 bool service_connected;
114 struct list_head list;
115 struct ind_req_resp ind_msg;
116};
117static LIST_HEAD(qmi_client_list);
118static DEFINE_MUTEX(qmi_list_lock);
Channagoud Kadabi4d480b02016-12-20 11:57:51 -0800119
120static DEFINE_MUTEX(notif_add_lock);
121
122static void root_service_clnt_recv_msg(struct work_struct *work);
123static void root_service_service_arrive(struct work_struct *work);
124static void root_service_exit_work(struct work_struct *work);
125
Avaneesh Kumar Dwivedibcb8f372017-03-25 18:01:09 +0530126static void free_qmi_handle(struct work_struct *work)
127{
128 struct qmi_client_info *data = container_of(work,
129 struct qmi_client_info, qmi_handle_free);
130
Kyle Yan879dc5d2017-11-30 13:36:53 -0800131 down_write(&data->qmi_client_handle_rwlock);
Avaneesh Kumar Dwivedibcb8f372017-03-25 18:01:09 +0530132 data->service_connected = false;
133 qmi_handle_destroy(data->clnt_handle);
134 data->clnt_handle = NULL;
Kyle Yan879dc5d2017-11-30 13:36:53 -0800135 up_write(&data->qmi_client_handle_rwlock);
Avaneesh Kumar Dwivedibcb8f372017-03-25 18:01:09 +0530136}
137
Channagoud Kadabi4d480b02016-12-20 11:57:51 -0800138static struct service_notif_info *_find_service_info(const char *service_path)
139{
140 struct service_notif_info *service_notif;
141
142 mutex_lock(&service_list_lock);
143 list_for_each_entry(service_notif, &service_list, list)
144 if (!strcmp(service_notif->service_path, service_path)) {
145 mutex_unlock(&service_list_lock);
146 return service_notif;
147 }
148 mutex_unlock(&service_list_lock);
149 return NULL;
150}
151
152static int service_notif_queue_notification(struct service_notif_info
153 *service_notif,
154 enum qmi_servreg_notif_service_state_enum_type_v01 notif_type,
155 void *info)
156{
157 int ret;
158
159 if (service_notif->curr_state == notif_type)
160 return 0;
161
162 ret = srcu_notifier_call_chain(&service_notif->service_notif_rcvr_list,
163 notif_type, info);
164 return ret;
165}
166
167static void root_service_clnt_recv_msg(struct work_struct *work)
168{
169 int ret;
170 struct qmi_client_info *data = container_of(work,
171 struct qmi_client_info, svc_rcv_msg);
172
Kyle Yan879dc5d2017-11-30 13:36:53 -0800173 down_read(&data->qmi_client_handle_rwlock);
Channagoud Kadabi4d480b02016-12-20 11:57:51 -0800174 do {
175 pr_debug("Polling for QMI recv msg(instance-id: %d)\n",
176 data->instance_id);
177 } while ((ret = qmi_recv_msg(data->clnt_handle)) == 0);
Kyle Yan879dc5d2017-11-30 13:36:53 -0800178 up_read(&data->qmi_client_handle_rwlock);
Channagoud Kadabi4d480b02016-12-20 11:57:51 -0800179
180 pr_debug("Notified about a Receive event (instance-id: %d)\n",
181 data->instance_id);
182}
183
184static void root_service_clnt_notify(struct qmi_handle *handle,
185 enum qmi_event_type event, void *notify_priv)
186{
187 struct qmi_client_info *data = container_of(notify_priv,
188 struct qmi_client_info, svc_arrive);
189
190 switch (event) {
191 case QMI_RECV_MSG:
192 schedule_work(&data->svc_rcv_msg);
193 break;
194 default:
195 break;
196 }
197}
198
199static void send_ind_ack(struct work_struct *work)
200{
201 struct qmi_client_info *data = container_of(work,
202 struct qmi_client_info, ind_ack);
203 struct qmi_servreg_notif_set_ack_req_msg_v01 req;
204 struct msg_desc req_desc, resp_desc;
205 struct qmi_servreg_notif_set_ack_resp_msg_v01 resp = { { 0, 0 } };
Avaneesh Kumar Dwivedieda8e2b2017-04-26 17:08:54 +0530206 struct service_notif_info *service_notif;
207 enum pd_subsys_state state = USER_PD_STATE_CHANGE;
Channagoud Kadabi4d480b02016-12-20 11:57:51 -0800208 int rc;
209
Avaneesh Kumar Dwivedieda8e2b2017-04-26 17:08:54 +0530210 service_notif = _find_service_info(data->ind_msg.service_path);
211 if (!service_notif)
212 return;
213 if ((int)data->ind_msg.curr_state < QMI_STATE_MIN_VAL ||
214 (int)data->ind_msg.curr_state > QMI_STATE_MAX_VAL)
215 pr_err("Unexpected indication notification state %d\n",
216 data->ind_msg.curr_state);
217 else {
218 mutex_lock(&notif_add_lock);
219 mutex_lock(&service_list_lock);
220 rc = service_notif_queue_notification(service_notif,
221 data->ind_msg.curr_state, &state);
222 if (rc & NOTIFY_STOP_MASK)
223 pr_err("Notifier callback aborted for %s with error %d\n",
224 data->ind_msg.service_path, rc);
225 service_notif->curr_state = data->ind_msg.curr_state;
226 mutex_unlock(&service_list_lock);
227 mutex_unlock(&notif_add_lock);
228 }
229
Channagoud Kadabi4d480b02016-12-20 11:57:51 -0800230 req.transaction_id = data->ind_msg.transaction_id;
231 snprintf(req.service_name, ARRAY_SIZE(req.service_name), "%s",
232 data->ind_msg.service_path);
233
234 req_desc.msg_id = SERVREG_NOTIF_SET_ACK_REQ;
235 req_desc.max_msg_len = SERVREG_NOTIF_SET_ACK_REQ_MSG_LEN;
236 req_desc.ei_array = qmi_servreg_notif_set_ack_req_msg_v01_ei;
237
238 resp_desc.msg_id = SERVREG_NOTIF_SET_ACK_RESP;
239 resp_desc.max_msg_len = SERVREG_NOTIF_SET_ACK_RESP_MSG_LEN;
240 resp_desc.ei_array = qmi_servreg_notif_set_ack_resp_msg_v01_ei;
241
Kyle Yan879dc5d2017-11-30 13:36:53 -0800242 down_read(&data->qmi_client_handle_rwlock);
Channagoud Kadabi4d480b02016-12-20 11:57:51 -0800243 rc = qmi_send_req_wait(data->clnt_handle, &req_desc,
244 &req, sizeof(req), &resp_desc, &resp,
245 sizeof(resp), SERVER_TIMEOUT);
Kyle Yan879dc5d2017-11-30 13:36:53 -0800246 up_read(&data->qmi_client_handle_rwlock);
Channagoud Kadabi4d480b02016-12-20 11:57:51 -0800247 if (rc < 0) {
248 pr_err("%s: Sending Ack failed/server timeout, ret - %d\n",
249 data->ind_msg.service_path, rc);
250 return;
251 }
252
253 /* Check the response */
Puja Gupta1c4c1f02017-05-15 11:15:07 -0700254 if (resp.resp.result != QMI_RESULT_SUCCESS_V01)
255 pr_err("QMI request failed 0x%x\n", resp.resp.error);
Satya Durga Srinivasu Prabhala1a9cc0c2017-01-19 18:24:00 -0800256 pr_info("Indication ACKed for transid %d, service %s, instance %d!\n",
Channagoud Kadabi4d480b02016-12-20 11:57:51 -0800257 data->ind_msg.transaction_id, data->ind_msg.service_path,
258 data->instance_id);
259}
260
261static void root_service_service_ind_cb(struct qmi_handle *handle,
262 unsigned int msg_id, void *msg,
263 unsigned int msg_len, void *ind_cb_priv)
264{
265 struct qmi_client_info *data = (struct qmi_client_info *)ind_cb_priv;
Channagoud Kadabi4d480b02016-12-20 11:57:51 -0800266 struct msg_desc ind_desc;
267 struct qmi_servreg_notif_state_updated_ind_msg_v01 ind_msg = {
268 QMI_STATE_MIN_VAL, "", 0xFFFF };
269 int rc;
270
271 ind_desc.msg_id = SERVREG_NOTIF_STATE_UPDATED_IND_MSG;
272 ind_desc.max_msg_len = SERVREG_NOTIF_STATE_UPDATED_IND_MSG_LEN;
273 ind_desc.ei_array = qmi_servreg_notif_state_updated_ind_msg_v01_ei;
274 rc = qmi_kernel_decode(&ind_desc, &ind_msg, msg, msg_len);
275 if (rc < 0) {
276 pr_err("Failed to decode message rc:%d\n", rc);
277 return;
278 }
279
Satya Durga Srinivasu Prabhala1a9cc0c2017-01-19 18:24:00 -0800280 pr_info("Indication received from %s, state: 0x%x, trans-id: %d\n",
Channagoud Kadabi4d480b02016-12-20 11:57:51 -0800281 ind_msg.service_name, ind_msg.curr_state,
282 ind_msg.transaction_id);
283
Channagoud Kadabi4d480b02016-12-20 11:57:51 -0800284 data->ind_msg.transaction_id = ind_msg.transaction_id;
Avaneesh Kumar Dwivedieda8e2b2017-04-26 17:08:54 +0530285 data->ind_msg.curr_state = ind_msg.curr_state;
Channagoud Kadabi4d480b02016-12-20 11:57:51 -0800286 snprintf(data->ind_msg.service_path,
287 ARRAY_SIZE(data->ind_msg.service_path), "%s",
288 ind_msg.service_name);
289 schedule_work(&data->ind_ack);
290}
291
292static int send_notif_listener_msg_req(struct service_notif_info *service_notif,
293 struct qmi_client_info *data,
294 bool register_notif, int *curr_state)
295{
296 struct qmi_servreg_notif_register_listener_req_msg_v01 req;
297 struct qmi_servreg_notif_register_listener_resp_msg_v01
298 resp = { { 0, 0 } };
299 struct msg_desc req_desc, resp_desc;
300 int rc;
301
302 snprintf(req.service_name, ARRAY_SIZE(req.service_name), "%s",
303 service_notif->service_path);
304 req.enable = register_notif;
305
306 req_desc.msg_id = SERVREG_NOTIF_REGISTER_LISTENER_REQ;
307 req_desc.max_msg_len = SERVREG_NOTIF_REGISTER_LISTENER_REQ_MSG_LEN;
308 req_desc.ei_array = qmi_servreg_notif_register_listener_req_msg_v01_ei;
309
310 resp_desc.msg_id = SERVREG_NOTIF_REGISTER_LISTENER_RESP;
311 resp_desc.max_msg_len = SERVREG_NOTIF_REGISTER_LISTENER_RESP_MSG_LEN;
312 resp_desc.ei_array =
313 qmi_servreg_notif_register_listener_resp_msg_v01_ei;
314
Kyle Yan879dc5d2017-11-30 13:36:53 -0800315 down_read(&data->qmi_client_handle_rwlock);
Channagoud Kadabi4d480b02016-12-20 11:57:51 -0800316 rc = qmi_send_req_wait(data->clnt_handle, &req_desc, &req, sizeof(req),
317 &resp_desc, &resp, sizeof(resp),
318 SERVER_TIMEOUT);
Kyle Yan879dc5d2017-11-30 13:36:53 -0800319 up_read(&data->qmi_client_handle_rwlock);
Channagoud Kadabi4d480b02016-12-20 11:57:51 -0800320 if (rc < 0) {
321 pr_err("%s: Message sending failed/server timeout, ret - %d\n",
322 service_notif->service_path, rc);
323 return rc;
324 }
325
326 /* Check the response */
Puja Gupta1c4c1f02017-05-15 11:15:07 -0700327 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
328 pr_err("QMI request failed 0x%x\n", resp.resp.error);
Channagoud Kadabi4d480b02016-12-20 11:57:51 -0800329 return -EREMOTEIO;
330 }
331
332 if ((int) resp.curr_state < QMI_STATE_MIN_VAL ||
333 (int) resp.curr_state > QMI_STATE_MAX_VAL) {
334 pr_err("Invalid indication notification state %d\n",
335 resp.curr_state);
336 rc = -EINVAL;
337 }
338 *curr_state = resp.curr_state;
339 return rc;
340}
341
342static int register_notif_listener(struct service_notif_info *service_notif,
343 struct qmi_client_info *data,
344 int *curr_state)
345{
346 return send_notif_listener_msg_req(service_notif, data, true,
347 curr_state);
348}
349
350static void root_service_service_arrive(struct work_struct *work)
351{
352 struct service_notif_info *service_notif = NULL;
353 struct qmi_client_info *data = container_of(work,
354 struct qmi_client_info, svc_arrive);
355 int rc;
356 int curr_state;
357
Kyle Yan879dc5d2017-11-30 13:36:53 -0800358 down_read(&data->qmi_client_handle_rwlock);
Channagoud Kadabi4d480b02016-12-20 11:57:51 -0800359 /* Create a Local client port for QMI communication */
360 data->clnt_handle = qmi_handle_create(root_service_clnt_notify, work);
361 if (!data->clnt_handle) {
362 pr_err("QMI client handle alloc failed (instance-id: %d)\n",
363 data->instance_id);
Kyle Yan879dc5d2017-11-30 13:36:53 -0800364 up_read(&data->qmi_client_handle_rwlock);
Channagoud Kadabi4d480b02016-12-20 11:57:51 -0800365 return;
366 }
367
368 /* Connect to the service on the root PD service */
369 rc = qmi_connect_to_service(data->clnt_handle,
370 SERVREG_NOTIF_SERVICE_ID, SERVREG_NOTIF_SERVICE_VERS,
371 data->instance_id);
372 if (rc < 0) {
373 pr_err("Could not connect to service(instance-id: %d) rc:%d\n",
374 data->instance_id, rc);
375 qmi_handle_destroy(data->clnt_handle);
376 data->clnt_handle = NULL;
Kyle Yan879dc5d2017-11-30 13:36:53 -0800377 up_read(&data->qmi_client_handle_rwlock);
Channagoud Kadabi4d480b02016-12-20 11:57:51 -0800378 return;
379 }
380 data->service_connected = true;
Kyle Yan879dc5d2017-11-30 13:36:53 -0800381 up_read(&data->qmi_client_handle_rwlock);
Channagoud Kadabi4d480b02016-12-20 11:57:51 -0800382 pr_info("Connection established between QMI handle and %d service\n",
383 data->instance_id);
Avaneesh Kumar Dwivedieda8e2b2017-04-26 17:08:54 +0530384 /* Register for indication messages about service */
385 rc = qmi_register_ind_cb(data->clnt_handle,
386 root_service_service_ind_cb, (void *)data);
387 if (rc < 0)
388 pr_err("Indication callback register failed(instance-id: %d) rc:%d\n",
389 data->instance_id, rc);
Channagoud Kadabi4d480b02016-12-20 11:57:51 -0800390 mutex_lock(&notif_add_lock);
391 mutex_lock(&service_list_lock);
392 list_for_each_entry(service_notif, &service_list, list) {
393 if (service_notif->instance_id == data->instance_id) {
Puja Gupta51be7062017-02-08 14:30:34 -0800394 enum pd_subsys_state state = ROOT_PD_UP;
Channagoud Kadabi4d480b02016-12-20 11:57:51 -0800395 rc = register_notif_listener(service_notif, data,
396 &curr_state);
397 if (rc) {
398 pr_err("Notifier registration failed for %s rc:%d\n",
399 service_notif->service_path, rc);
400 } else {
401 rc = service_notif_queue_notification(
Puja Gupta51be7062017-02-08 14:30:34 -0800402 service_notif, curr_state, &state);
Channagoud Kadabi4d480b02016-12-20 11:57:51 -0800403 if (rc & NOTIFY_STOP_MASK)
404 pr_err("Notifier callback aborted for %s error:%d\n",
405 service_notif->service_path, rc);
406 service_notif->curr_state = curr_state;
407 }
408 }
409 }
410 mutex_unlock(&service_list_lock);
411 mutex_unlock(&notif_add_lock);
412}
413
414static void root_service_service_exit(struct qmi_client_info *data,
415 enum pd_subsys_state state)
416{
417 struct service_notif_info *service_notif = NULL;
418 int rc;
419
420 /*
421 * Send service down notifications to all clients
422 * of registered for notifications for that service.
423 */
424 mutex_lock(&notif_add_lock);
425 mutex_lock(&service_list_lock);
426 list_for_each_entry(service_notif, &service_list, list) {
427 if (service_notif->instance_id == data->instance_id) {
428 rc = service_notif_queue_notification(service_notif,
429 SERVREG_NOTIF_SERVICE_STATE_DOWN_V01,
430 &state);
431 if (rc & NOTIFY_STOP_MASK)
432 pr_err("Notifier callback aborted for %s with error %d\n",
433 service_notif->service_path, rc);
434 service_notif->curr_state =
435 SERVREG_NOTIF_SERVICE_STATE_DOWN_V01;
436 }
437 }
438 mutex_unlock(&service_list_lock);
439 mutex_unlock(&notif_add_lock);
440
441 /*
442 * Destroy client handle and try connecting when
443 * service comes up again.
444 */
Avaneesh Kumar Dwivedibcb8f372017-03-25 18:01:09 +0530445 queue_work(data->svc_event_wq, &data->qmi_handle_free);
Channagoud Kadabi4d480b02016-12-20 11:57:51 -0800446}
447
448static void root_service_exit_work(struct work_struct *work)
449{
450 struct qmi_client_info *data = container_of(work,
451 struct qmi_client_info, svc_exit);
Avaneesh Kumar Dwivedie36650a2017-03-07 15:13:31 +0530452 root_service_service_exit(data, data->subsys_state);
Channagoud Kadabi4d480b02016-12-20 11:57:51 -0800453}
454
455static int service_event_notify(struct notifier_block *this,
456 unsigned long code,
457 void *_cmd)
458{
459 struct qmi_client_info *data = container_of(this,
460 struct qmi_client_info, notifier);
461
462 switch (code) {
463 case QMI_SERVER_ARRIVE:
464 pr_debug("Root PD service UP\n");
465 queue_work(data->svc_event_wq, &data->svc_arrive);
466 break;
467 case QMI_SERVER_EXIT:
468 pr_debug("Root PD service DOWN\n");
Avaneesh Kumar Dwivedie36650a2017-03-07 15:13:31 +0530469 data->subsys_state = ROOT_PD_DOWN;
Channagoud Kadabi4d480b02016-12-20 11:57:51 -0800470 queue_work(data->svc_event_wq, &data->svc_exit);
471 break;
472 default:
473 break;
474 }
475 return 0;
476}
477
478static int ssr_event_notify(struct notifier_block *this,
479 unsigned long code,
480 void *data)
481{
482 struct qmi_client_info *info = container_of(this,
483 struct qmi_client_info, ssr_notifier);
484 struct notif_data *notif = data;
485
486 switch (code) {
487 case SUBSYS_BEFORE_SHUTDOWN:
Puja Gupta51be7062017-02-08 14:30:34 -0800488 pr_debug("Root PD DOWN(SSR notification), state:%d\n",
Channagoud Kadabi4d480b02016-12-20 11:57:51 -0800489 notif->crashed);
Puja Gupta51be7062017-02-08 14:30:34 -0800490 switch (notif->crashed) {
491 case CRASH_STATUS_ERR_FATAL:
Avaneesh Kumar Dwivedie36650a2017-03-07 15:13:31 +0530492 info->subsys_state = ROOT_PD_ERR_FATAL;
Puja Gupta51be7062017-02-08 14:30:34 -0800493 break;
494 case CRASH_STATUS_WDOG_BITE:
Avaneesh Kumar Dwivedie36650a2017-03-07 15:13:31 +0530495 info->subsys_state = ROOT_PD_WDOG_BITE;
Puja Gupta51be7062017-02-08 14:30:34 -0800496 break;
497 default:
Avaneesh Kumar Dwivedie36650a2017-03-07 15:13:31 +0530498 info->subsys_state = ROOT_PD_SHUTDOWN;
Puja Gupta51be7062017-02-08 14:30:34 -0800499 break;
500 }
Avaneesh Kumar Dwivedibcb8f372017-03-25 18:01:09 +0530501 root_service_service_exit(info, info->subsys_state);
Channagoud Kadabi4d480b02016-12-20 11:57:51 -0800502 break;
503 default:
504 break;
505 }
506 return NOTIFY_DONE;
507}
508
509static void *add_service_notif(const char *service_path, int instance_id,
510 int *curr_state)
511{
512 struct service_notif_info *service_notif;
513 struct qmi_client_info *tmp, *qmi_data;
514 long int rc;
515 char subsys[SERVREG_NOTIF_NAME_LENGTH];
516
517 rc = find_subsys(service_path, subsys);
518 if (rc < 0) {
519 pr_err("Could not find subsys for %s\n", service_path);
520 return ERR_PTR(rc);
521 }
522
523 service_notif = kzalloc(sizeof(struct service_notif_info), GFP_KERNEL);
524 if (!service_notif)
525 return ERR_PTR(-ENOMEM);
526
527 strlcpy(service_notif->service_path, service_path,
528 ARRAY_SIZE(service_notif->service_path));
529 service_notif->instance_id = instance_id;
530
531 /* If we already have a connection to the root PD on which the remote
532 * service we are interested in notifications about runs, then use
533 * the existing QMI connection.
534 */
535 mutex_lock(&qmi_list_lock);
536 list_for_each_entry(tmp, &qmi_client_list, list) {
537 if (tmp->instance_id == instance_id) {
538 if (tmp->service_connected) {
539 rc = register_notif_listener(service_notif, tmp,
540 curr_state);
541 if (rc) {
542 mutex_unlock(&qmi_list_lock);
543 pr_err("Register notifier failed: %s",
544 service_path);
545 kfree(service_notif);
546 return ERR_PTR(rc);
547 }
548 service_notif->curr_state = *curr_state;
549 }
550 mutex_unlock(&qmi_list_lock);
551 goto add_service_list;
552 }
553 }
554 mutex_unlock(&qmi_list_lock);
555
556 qmi_data = kzalloc(sizeof(struct qmi_client_info), GFP_KERNEL);
557 if (!qmi_data) {
558 kfree(service_notif);
559 return ERR_PTR(-ENOMEM);
560 }
561
562 qmi_data->instance_id = instance_id;
Kyle Yan879dc5d2017-11-30 13:36:53 -0800563 init_rwsem(&qmi_data->qmi_client_handle_rwlock);
Channagoud Kadabi4d480b02016-12-20 11:57:51 -0800564 qmi_data->clnt_handle = NULL;
565 qmi_data->notifier.notifier_call = service_event_notify;
566
567 qmi_data->svc_event_wq = create_singlethread_workqueue(subsys);
568 if (!qmi_data->svc_event_wq) {
569 rc = -ENOMEM;
570 goto exit;
571 }
572
573 INIT_WORK(&qmi_data->svc_arrive, root_service_service_arrive);
574 INIT_WORK(&qmi_data->svc_exit, root_service_exit_work);
575 INIT_WORK(&qmi_data->svc_rcv_msg, root_service_clnt_recv_msg);
576 INIT_WORK(&qmi_data->ind_ack, send_ind_ack);
Avaneesh Kumar Dwivedibcb8f372017-03-25 18:01:09 +0530577 INIT_WORK(&qmi_data->qmi_handle_free, free_qmi_handle);
Channagoud Kadabi4d480b02016-12-20 11:57:51 -0800578
579 *curr_state = service_notif->curr_state =
580 SERVREG_NOTIF_SERVICE_STATE_UNINIT_V01;
581
582 rc = qmi_svc_event_notifier_register(SERVREG_NOTIF_SERVICE_ID,
583 SERVREG_NOTIF_SERVICE_VERS, qmi_data->instance_id,
584 &qmi_data->notifier);
585 if (rc < 0) {
586 pr_err("Notifier register failed (instance-id: %d)\n",
587 qmi_data->instance_id);
588 goto exit;
589 }
590 qmi_data->ssr_notifier.notifier_call = ssr_event_notify;
591 qmi_data->ssr_handle = subsys_notif_register_notifier(subsys,
592 &qmi_data->ssr_notifier);
593 if (IS_ERR(qmi_data->ssr_handle)) {
594 pr_err("SSR notif register for %s failed(instance-id: %d)\n",
595 subsys, qmi_data->instance_id);
596 rc = PTR_ERR(qmi_data->ssr_handle);
597 goto exit;
598 }
599
600 mutex_lock(&qmi_list_lock);
601 INIT_LIST_HEAD(&qmi_data->list);
602 list_add_tail(&qmi_data->list, &qmi_client_list);
603 mutex_unlock(&qmi_list_lock);
604
605add_service_list:
606 srcu_init_notifier_head(&service_notif->service_notif_rcvr_list);
607
608 mutex_lock(&service_list_lock);
609 INIT_LIST_HEAD(&service_notif->list);
610 list_add_tail(&service_notif->list, &service_list);
611 mutex_unlock(&service_list_lock);
612
613 return service_notif;
614exit:
615 if (qmi_data->svc_event_wq)
616 destroy_workqueue(qmi_data->svc_event_wq);
617 kfree(qmi_data);
618 kfree(service_notif);
619 return ERR_PTR(rc);
620}
621
622static int send_pd_restart_req(const char *service_path,
623 struct qmi_client_info *data)
624{
625 struct qmi_servreg_notif_restart_pd_req_msg_v01 req;
626 struct qmi_servreg_notif_register_listener_resp_msg_v01
627 resp = { { 0, 0 } };
628 struct msg_desc req_desc, resp_desc;
629 int rc;
630
631 snprintf(req.service_name, ARRAY_SIZE(req.service_name), "%s",
632 service_path);
633
634 req_desc.msg_id = QMI_SERVREG_NOTIF_RESTART_PD_REQ_V01;
635 req_desc.max_msg_len =
636 QMI_SERVREG_NOTIF_RESTART_PD_REQ_MSG_V01_MAX_MSG_LEN;
637 req_desc.ei_array = qmi_servreg_notif_restart_pd_req_msg_v01_ei;
638
639 resp_desc.msg_id = QMI_SERVREG_NOTIF_RESTART_PD_RESP_V01;
640 resp_desc.max_msg_len =
641 QMI_SERVREG_NOTIF_RESTART_PD_RESP_MSG_V01_MAX_MSG_LEN;
642 resp_desc.ei_array = qmi_servreg_notif_restart_pd_resp_msg_v01_ei;
643
644 rc = qmi_send_req_wait(data->clnt_handle, &req_desc, &req,
645 sizeof(req), &resp_desc, &resp, sizeof(resp),
646 SERVER_TIMEOUT);
647 if (rc < 0) {
648 pr_err("%s: Message sending failed/server timeout, ret - %d\n",
649 service_path, rc);
650 return rc;
651 }
652
Puja Gupta4f27f592017-02-24 17:19:38 -0800653 /* Check response if PDR is disabled */
Puja Gupta1c4c1f02017-05-15 11:15:07 -0700654 if (resp.resp.result == QMI_RESULT_FAILURE_V01 &&
655 resp.resp.error == QMI_ERR_DISABLED_V01) {
656 pr_err("PD restart is disabled 0x%x\n", resp.resp.error);
Puja Gupta4f27f592017-02-24 17:19:38 -0800657 return -EOPNOTSUPP;
658 }
659 /* Check the response for other error case*/
Puja Gupta1c4c1f02017-05-15 11:15:07 -0700660 if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
Channagoud Kadabi4d480b02016-12-20 11:57:51 -0800661 pr_err("QMI request for PD restart failed 0x%x\n",
Puja Gupta1c4c1f02017-05-15 11:15:07 -0700662 resp.resp.error);
Channagoud Kadabi4d480b02016-12-20 11:57:51 -0800663 return -EREMOTEIO;
664 }
665
666 return rc;
667
668}
669
670/* service_notif_pd_restart() - Request PD restart
671 * @service_path: Individual service identifier path for which restart is
672 * being requested.
673 * @instance_id: Instance id specific to a subsystem.
674 *
675 * @return: >=0 on success, standard Linux error codes on failure.
676 */
677int service_notif_pd_restart(const char *service_path, int instance_id)
678{
679 struct qmi_client_info *tmp;
680 int rc = 0;
681
682 list_for_each_entry(tmp, &qmi_client_list, list) {
683 if (tmp->instance_id == instance_id) {
684 if (tmp->service_connected) {
685 pr_info("Restarting service %s, instance-id %d\n",
686 service_path, instance_id);
687 rc = send_pd_restart_req(service_path, tmp);
688 } else
689 pr_info("Service %s is not connected\n",
690 service_path);
691 }
692 }
693 return rc;
694}
695EXPORT_SYMBOL(service_notif_pd_restart);
696
697/* service_notif_register_notifier() - Register a notifier for a service
698 * On success, it returns back a handle. It takes the following arguments:
699 * service_path: Individual service identifier path for which a client
700 * registers for notifications.
701 * instance_id: Instance id specific to a subsystem.
702 * current_state: Current state of service returned by the registration
703 * process.
704 * notifier block: notifier callback for service events.
705 */
706void *service_notif_register_notifier(const char *service_path, int instance_id,
707 struct notifier_block *nb, int *curr_state)
708{
709 struct service_notif_info *service_notif;
710 int ret = 0;
711
712 if (!service_path || !instance_id || !nb)
713 return ERR_PTR(-EINVAL);
714
715 mutex_lock(&notif_add_lock);
716 service_notif = _find_service_info(service_path);
717 if (!service_notif) {
718 service_notif = (struct service_notif_info *)add_service_notif(
719 service_path,
720 instance_id,
721 curr_state);
722 if (IS_ERR(service_notif))
723 goto exit;
724 }
725
726 ret = srcu_notifier_chain_register(
727 &service_notif->service_notif_rcvr_list, nb);
728 *curr_state = service_notif->curr_state;
729 if (ret < 0)
730 service_notif = ERR_PTR(ret);
731exit:
732 mutex_unlock(&notif_add_lock);
733 return service_notif;
734}
735EXPORT_SYMBOL(service_notif_register_notifier);
736
737/* service_notif_unregister_notifier() - Unregister a notifier for a service.
738 * service_notif_handle - The notifier handler that was provided by the
739 * service_notif_register_notifier function when the
740 * client registered for notifications.
741 * nb - The notifier block that was previously used during the registration.
742 */
743int service_notif_unregister_notifier(void *service_notif_handle,
744 struct notifier_block *nb)
745{
746 struct service_notif_info *service_notif;
747
748 if (!service_notif_handle || !nb)
749 return -EINVAL;
750
751 service_notif = (struct service_notif_info *)service_notif_handle;
752 if (service_notif < 0)
753 return -EINVAL;
754
755 return srcu_notifier_chain_unregister(
756 &service_notif->service_notif_rcvr_list, nb);
757}
758EXPORT_SYMBOL(service_notif_unregister_notifier);