blob: 57f38d3aa5d49597f04fce1bdde96e25d77813e8 [file] [log] [blame]
Channagoud Kadabi4d480b02016-12-20 11:57:51 -08001/*
Gaurav Kohlic3fe5a72017-02-22 18:34:12 +05302 * 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) "servloc: %s: " fmt, __func__
16
17#include <linux/kernel.h>
18#include <linux/module.h>
19#include <linux/mutex.h>
20#include <linux/string.h>
21#include <linux/completion.h>
22#include <linux/slab.h>
23#include <linux/of.h>
24#include <linux/device.h>
25#include <linux/delay.h>
26#include <linux/workqueue.h>
Channagoud Kadabi4d480b02016-12-20 11:57:51 -080027
28#include <soc/qcom/msm_qmi_interface.h>
29#include <soc/qcom/service-locator.h>
30#include "service-locator-private.h"
31
32#define SERVREG_LOC_SERVICE_INSTANCE_ID 1
33
Channagoud Kadabi4d480b02016-12-20 11:57:51 -080034#define QMI_SERVREG_LOC_SERVER_INITIAL_TIMEOUT 2000
35#define QMI_SERVREG_LOC_SERVER_TIMEOUT 2000
36#define INITIAL_TIMEOUT 100000
37
38#define LOCATOR_NOT_PRESENT 0
39#define LOCATOR_PRESENT 1
40
41static u32 locator_status = LOCATOR_NOT_PRESENT;
42static bool service_inited;
43
44module_param_named(enable, locator_status, uint, 0644);
45
46static void service_locator_svc_arrive(struct work_struct *work);
47static void service_locator_svc_exit(struct work_struct *work);
48static void service_locator_recv_msg(struct work_struct *work);
49static void pd_locator_work(struct work_struct *work);
50
51struct workqueue_struct *servloc_wq;
52
53struct pd_qmi_data {
54 struct work_struct svc_arrive;
55 struct work_struct svc_exit;
56 struct work_struct svc_rcv_msg;
57 struct notifier_block notifier;
58 struct completion service_available;
59 struct mutex service_mutex;
60 struct qmi_handle *clnt_handle;
61};
62
63struct pd_qmi_work {
64 struct work_struct pd_loc_work;
65 struct pd_qmi_client_data *pdc;
66 struct notifier_block *notifier;
67};
68DEFINE_MUTEX(service_init_mutex);
69struct pd_qmi_data service_locator;
70
71/* Please refer soc/qcom/service-locator.h for use about APIs defined here */
72
73static int service_locator_svc_event_notify(struct notifier_block *this,
74 unsigned long code,
75 void *_cmd)
76{
77 switch (code) {
78 case QMI_SERVER_ARRIVE:
79 queue_work(servloc_wq, &service_locator.svc_arrive);
80 break;
81 case QMI_SERVER_EXIT:
82 queue_work(servloc_wq, &service_locator.svc_exit);
83 break;
84 default:
85 break;
86 }
87 return 0;
88}
89
90static void service_locator_clnt_notify(struct qmi_handle *handle,
91 enum qmi_event_type event, void *notify_priv)
92{
93 switch (event) {
94 case QMI_RECV_MSG:
95 schedule_work(&service_locator.svc_rcv_msg);
96 break;
97 default:
98 break;
99 }
100}
101
102static void service_locator_svc_arrive(struct work_struct *work)
103{
104 int rc = 0;
105
106 /* Create a Local client port for QMI communication */
107 mutex_lock(&service_locator.service_mutex);
108 service_locator.clnt_handle =
109 qmi_handle_create(service_locator_clnt_notify, NULL);
110 if (!service_locator.clnt_handle) {
111 service_locator.clnt_handle = NULL;
Gaurav Kohlic3fe5a72017-02-22 18:34:12 +0530112 complete_all(&service_locator.service_available);
Channagoud Kadabi4d480b02016-12-20 11:57:51 -0800113 mutex_unlock(&service_locator.service_mutex);
114 pr_err("Service locator QMI client handle alloc failed!\n");
115 return;
116 }
117
118 /* Connect to service */
119 rc = qmi_connect_to_service(service_locator.clnt_handle,
120 SERVREG_LOC_SERVICE_ID_V01, SERVREG_LOC_SERVICE_VERS_V01,
121 SERVREG_LOC_SERVICE_INSTANCE_ID);
122 if (rc) {
123 qmi_handle_destroy(service_locator.clnt_handle);
124 service_locator.clnt_handle = NULL;
Gaurav Kohlic3fe5a72017-02-22 18:34:12 +0530125 complete_all(&service_locator.service_available);
Channagoud Kadabi4d480b02016-12-20 11:57:51 -0800126 mutex_unlock(&service_locator.service_mutex);
127 pr_err("Unable to connnect to service rc:%d\n", rc);
128 return;
129 }
130 if (!service_inited)
131 complete_all(&service_locator.service_available);
132 mutex_unlock(&service_locator.service_mutex);
133 pr_info("Connection established with the Service locator\n");
134}
135
136static void service_locator_svc_exit(struct work_struct *work)
137{
138 mutex_lock(&service_locator.service_mutex);
139 qmi_handle_destroy(service_locator.clnt_handle);
140 service_locator.clnt_handle = NULL;
Gaurav Kohlic3fe5a72017-02-22 18:34:12 +0530141 complete_all(&service_locator.service_available);
Channagoud Kadabi4d480b02016-12-20 11:57:51 -0800142 mutex_unlock(&service_locator.service_mutex);
143 pr_info("Connection with service locator lost\n");
144}
145
146static void service_locator_recv_msg(struct work_struct *work)
147{
148 int ret;
149
150 do {
151 pr_debug("Notified about a Receive event\n");
152 ret = qmi_recv_msg(service_locator.clnt_handle);
153 if (ret < 0)
154 pr_err("Error receiving message rc:%d. Retrying...\n",
155 ret);
156 } while (ret == 0);
157
158}
159
160static void store_get_domain_list_response(struct pd_qmi_client_data *pd,
161 struct qmi_servreg_loc_get_domain_list_resp_msg_v01 *resp,
162 int offset)
163{
164 int i;
165
166 for (i = offset; i < resp->domain_list_len; i++) {
167 pd->domain_list[i].instance_id =
168 resp->domain_list[i].instance_id;
169 strlcpy(pd->domain_list[i].name, resp->domain_list[i].name,
170 QMI_SERVREG_LOC_NAME_LENGTH_V01 + 1);
171 pd->domain_list[i].service_data_valid =
172 resp->domain_list[i].service_data_valid;
173 pd->domain_list[i].service_data =
174 resp->domain_list[i].service_data;
175 }
176}
177
178static int servreg_loc_send_msg(struct msg_desc *req_desc,
179 struct msg_desc *resp_desc,
180 struct qmi_servreg_loc_get_domain_list_req_msg_v01 *req,
181 struct qmi_servreg_loc_get_domain_list_resp_msg_v01 *resp,
182 struct pd_qmi_client_data *pd)
183{
184 int rc;
185
186 /*
187 * Send msg and get response. There is a chance that the service went
188 * away since the time we last checked for it to be available and
189 * actually made this call. In that case the call just fails.
190 */
191 rc = qmi_send_req_wait(service_locator.clnt_handle, req_desc, req,
192 sizeof(*req), resp_desc, resp, sizeof(*resp),
193 msecs_to_jiffies(QMI_SERVREG_LOC_SERVER_TIMEOUT));
194 if (rc < 0) {
195 pr_err("QMI send req failed for client %s, ret - %d\n",
196 pd->client_name, rc);
197 return rc;
198 }
199
200 /* Check the response */
Puja Gupta1c4c1f02017-05-15 11:15:07 -0700201 if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
Channagoud Kadabi4d480b02016-12-20 11:57:51 -0800202 pr_err("QMI request for client %s failed 0x%x\n",
Puja Gupta1c4c1f02017-05-15 11:15:07 -0700203 pd->client_name, resp->resp.error);
Channagoud Kadabi4d480b02016-12-20 11:57:51 -0800204 return -EREMOTEIO;
205 }
206 return rc;
207}
208
209static int service_locator_send_msg(struct pd_qmi_client_data *pd)
210{
211 struct msg_desc req_desc, resp_desc;
212 struct qmi_servreg_loc_get_domain_list_resp_msg_v01 *resp = NULL;
213 struct qmi_servreg_loc_get_domain_list_req_msg_v01 *req = NULL;
214 int rc;
215 int db_rev_count = 0, domains_read = 0;
216
217 if (!service_locator.clnt_handle) {
218 pr_err("Service locator not available!\n");
219 return -EAGAIN;
220 }
221
Puja Gupta1c4c1f02017-05-15 11:15:07 -0700222 req = kzalloc(sizeof(
Channagoud Kadabi4d480b02016-12-20 11:57:51 -0800223 struct qmi_servreg_loc_get_domain_list_req_msg_v01),
224 GFP_KERNEL);
225 if (!req) {
226 pr_err("Unable to allocate memory for req message\n");
227 rc = -ENOMEM;
228 goto out;
229 }
Puja Gupta1c4c1f02017-05-15 11:15:07 -0700230 resp = kzalloc(sizeof(
Channagoud Kadabi4d480b02016-12-20 11:57:51 -0800231 struct qmi_servreg_loc_get_domain_list_resp_msg_v01),
232 GFP_KERNEL);
233 if (!resp) {
234 pr_err("Unable to allocate memory for resp message\n");
235 rc = -ENOMEM;
236 goto out;
237 }
238 /* Prepare req and response message formats */
239 req_desc.msg_id = QMI_SERVREG_LOC_GET_DOMAIN_LIST_REQ_V01;
240 req_desc.max_msg_len =
241 QMI_SERVREG_LOC_GET_DOMAIN_LIST_REQ_MSG_V01_MAX_MSG_LEN;
242 req_desc.ei_array = qmi_servreg_loc_get_domain_list_req_msg_v01_ei;
243
244 resp_desc.msg_id = QMI_SERVREG_LOC_GET_DOMAIN_LIST_RESP_V01;
245 resp_desc.max_msg_len =
246 QMI_SERVREG_LOC_GET_DOMAIN_LIST_RESP_MSG_V01_MAX_MSG_LEN;
247 resp_desc.ei_array = qmi_servreg_loc_get_domain_list_resp_msg_v01_ei;
248
249 /* Prepare req and response message */
250 strlcpy(req->service_name, pd->service_name,
251 QMI_SERVREG_LOC_NAME_LENGTH_V01 + 1);
252 req->domain_offset_valid = true;
253 req->domain_offset = 0;
254
255 pd->domain_list = NULL;
256 do {
257 req->domain_offset += domains_read;
258 rc = servreg_loc_send_msg(&req_desc, &resp_desc, req, resp,
259 pd);
260 if (rc < 0) {
261 pr_err("send msg failed rc:%d\n", rc);
262 goto out;
263 }
264 if (!domains_read) {
265 db_rev_count = pd->db_rev_count = resp->db_rev_count;
266 pd->total_domains = resp->total_domains;
Puja Gupta57c02ee2017-03-06 15:04:11 -0800267 if (!resp->total_domains) {
268 pr_err("No matching domains found\n");
Puja Gupta57c02ee2017-03-06 15:04:11 -0800269 goto out;
Channagoud Kadabi4d480b02016-12-20 11:57:51 -0800270 }
Puja Gupta57c02ee2017-03-06 15:04:11 -0800271
Channagoud Kadabi4d480b02016-12-20 11:57:51 -0800272 pd->domain_list = kmalloc(
273 sizeof(struct servreg_loc_entry_v01) *
274 resp->total_domains, GFP_KERNEL);
275 if (!pd->domain_list) {
276 pr_err("Cannot allocate domain list\n");
277 rc = -ENOMEM;
278 goto out;
279 }
280 }
281 if (db_rev_count != resp->db_rev_count) {
282 pr_err("Service Locator DB updated for client %s\n",
283 pd->client_name);
284 kfree(pd->domain_list);
285 rc = -EAGAIN;
286 goto out;
287 }
Puja Gupta57c02ee2017-03-06 15:04:11 -0800288 if (resp->domain_list_len > resp->total_domains) {
289 /* Always read total_domains from the response msg */
290 resp->domain_list_len = resp->total_domains;
291 }
Channagoud Kadabi4d480b02016-12-20 11:57:51 -0800292 /* Copy the response*/
293 store_get_domain_list_response(pd, resp, domains_read);
294 domains_read += resp->domain_list_len;
295 } while (domains_read < resp->total_domains);
296 rc = 0;
297out:
298 kfree(req);
299 kfree(resp);
300 return rc;
301}
302
303static int init_service_locator(void)
304{
305 int rc = 0;
306
307 mutex_lock(&service_init_mutex);
308 if (locator_status == LOCATOR_NOT_PRESENT) {
309 pr_err("Service Locator not enabled\n");
310 rc = -ENODEV;
311 goto inited;
312 }
313 if (service_inited)
314 goto inited;
315
316 service_locator.notifier.notifier_call =
317 service_locator_svc_event_notify;
318 init_completion(&service_locator.service_available);
319 mutex_init(&service_locator.service_mutex);
320
321 servloc_wq = create_singlethread_workqueue("servloc_wq");
322 if (!servloc_wq) {
323 rc = -ENOMEM;
324 pr_err("Could not create workqueue\n");
325 goto inited;
326 }
327
328 INIT_WORK(&service_locator.svc_arrive, service_locator_svc_arrive);
329 INIT_WORK(&service_locator.svc_exit, service_locator_svc_exit);
330 INIT_WORK(&service_locator.svc_rcv_msg, service_locator_recv_msg);
331
332 rc = qmi_svc_event_notifier_register(SERVREG_LOC_SERVICE_ID_V01,
333 SERVREG_LOC_SERVICE_VERS_V01, SERVREG_LOC_SERVICE_INSTANCE_ID,
334 &service_locator.notifier);
335 if (rc < 0) {
336 pr_err("Notifier register failed rc:%d\n", rc);
337 goto inited;
338 }
339
340 wait_for_completion(&service_locator.service_available);
341 service_inited = true;
342 mutex_unlock(&service_init_mutex);
343 pr_info("Service locator initialized\n");
344 return 0;
345
346inited:
347 mutex_unlock(&service_init_mutex);
348 return rc;
349}
350
351int get_service_location(char *client_name, char *service_name,
352 struct notifier_block *locator_nb)
353{
354 struct pd_qmi_client_data *pqcd;
355 struct pd_qmi_work *pqw;
356 int rc = 0;
357
358 if (!locator_nb || !client_name || !service_name) {
359 rc = -EINVAL;
360 pr_err("Invalid input!\n");
361 goto err;
362 }
363
364 pqcd = kmalloc(sizeof(struct pd_qmi_client_data), GFP_KERNEL);
365 if (!pqcd) {
366 rc = -ENOMEM;
367 pr_err("Allocation failed\n");
368 goto err;
369 }
370 strlcpy(pqcd->client_name, client_name, ARRAY_SIZE(pqcd->client_name));
371 strlcpy(pqcd->service_name, service_name,
372 ARRAY_SIZE(pqcd->service_name));
373
374 pqw = kmalloc(sizeof(struct pd_qmi_work), GFP_KERNEL);
375 if (!pqw) {
376 rc = -ENOMEM;
377 pr_err("Allocation failed\n");
Satya Durga Srinivasu Prabhala04fe6ab2017-03-22 19:07:12 -0700378 kfree(pqcd);
Channagoud Kadabi4d480b02016-12-20 11:57:51 -0800379 goto err;
380 }
381 pqw->notifier = locator_nb;
382 pqw->pdc = pqcd;
383
384 INIT_WORK(&pqw->pd_loc_work, pd_locator_work);
385 schedule_work(&pqw->pd_loc_work);
386
387err:
388 return rc;
389}
390EXPORT_SYMBOL(get_service_location);
391
392static void pd_locator_work(struct work_struct *work)
393{
394 int rc = 0;
395 struct pd_qmi_client_data *data;
396 struct pd_qmi_work *pdqw = container_of(work, struct pd_qmi_work,
397 pd_loc_work);
398
399 data = pdqw->pdc;
400 rc = init_service_locator();
401 if (rc) {
402 pr_err("Unable to connect to service locator!, rc = %d\n", rc);
403 pdqw->notifier->notifier_call(pdqw->notifier,
404 LOCATOR_DOWN, NULL);
405 goto err;
406 }
407 rc = service_locator_send_msg(data);
408 if (rc) {
409 pr_err("Failed to get process domains for %s for client %s rc:%d\n",
410 data->service_name, data->client_name, rc);
411 pdqw->notifier->notifier_call(pdqw->notifier,
412 LOCATOR_DOWN, NULL);
413 goto err;
414 }
415 pdqw->notifier->notifier_call(pdqw->notifier, LOCATOR_UP, data);
416
417err:
418 kfree(data);
419 kfree(pdqw);
420}
421
422int find_subsys(const char *pd_path, char *subsys)
423{
424 char *start, *end;
425
426 if (!subsys || !pd_path)
427 return -EINVAL;
428
429 start = strnstr(pd_path, "/", QMI_SERVREG_LOC_NAME_LENGTH_V01);
430 if (!start)
431 return -EINVAL;
432 start++;
433 end = strnstr(start, "/", QMI_SERVREG_LOC_NAME_LENGTH_V01);
434 if (!end || start == end)
435 return -EINVAL;
436
437 strlcpy(subsys, start, end - start + 1);
438 return 0;
439}
440EXPORT_SYMBOL(find_subsys);