blob: f3dfd9b7c1ad849c35817728d5fce1d1bdd6978d [file] [log] [blame]
Shihuan Liufbc51862018-02-27 23:21:14 -08001/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include <linux/debugfs.h>
14#include <linux/export.h>
15#include <linux/fs.h>
16#include <linux/if_ether.h>
17#include <linux/ioctl.h>
18#include <linux/kernel.h>
19#include <linux/module.h>
20#include <linux/msm_ipa.h>
21#include <linux/mutex.h>
22#include <linux/skbuff.h>
23#include <linux/types.h>
24#include <linux/ipv6.h>
25#include <net/addrconf.h>
26#include <linux/ipa.h>
27#include <linux/cdev.h>
28#include <linux/ipa_odu_bridge.h>
29#include "../ipa_common_i.h"
30#ifdef CONFIG_IPA3
31#include "../ipa_v3/ipa_pm.h"
32#endif
33
34#define IPA_GSB_DRV_NAME "ipa_gsb"
35
36#define MAX_SUPPORTED_IFACE 5
37
38#define IPA_GSB_DBG(fmt, args...) \
39 do { \
40 pr_debug(IPA_GSB_DRV_NAME " %s:%d " fmt, \
41 __func__, __LINE__, ## args); \
42 IPA_IPC_LOGGING(ipa_get_ipc_logbuf(), \
43 IPA_GSB_DRV_NAME " %s:%d " fmt, ## args); \
44 IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
45 IPA_GSB_DRV_NAME " %s:%d " fmt, ## args); \
46 } while (0)
47
48#define IPA_GSB_DBG_LOW(fmt, args...) \
49 do { \
50 pr_debug(IPA_GSB_DRV_NAME " %s:%d " fmt, \
51 __func__, __LINE__, ## args); \
52 IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
53 IPA_GSB_DRV_NAME " %s:%d " fmt, ## args); \
54 } while (0)
55
56#define IPA_GSB_ERR(fmt, args...) \
57 do { \
58 pr_err(IPA_GSB_DRV_NAME " %s:%d " fmt, \
59 __func__, __LINE__, ## args); \
60 IPA_IPC_LOGGING(ipa_get_ipc_logbuf(), \
61 IPA_GSB_DRV_NAME " %s:%d " fmt, ## args); \
62 IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
63 IPA_GSB_DRV_NAME " %s:%d " fmt, ## args); \
64 } while (0)
65
66#define IPA_GSB_MAX_MSG_LEN 512
67static char dbg_buff[IPA_GSB_MAX_MSG_LEN];
68
69#define IPA_GSB_SKB_HEADROOM 256
Bojun Panffaf7202018-05-31 16:16:25 -070070#define IPA_GSB_SKB_DUMMY_HEADER 42
71#define IPA_GSB_AGGR_BYTE_LIMIT 14
Shihuan Liufbc51862018-02-27 23:21:14 -080072#define IPA_GSB_AGGR_TIME_LIMIT 1
73
74static struct dentry *dent;
75static struct dentry *dfile_stats;
76
77/**
78 * struct stats - driver statistics,
79 * @num_ul_packets: number of uplink packets
80 * @num_dl_packets: number of downlink packets
81 * @num_insufficient_headroom_packets: number of
82 packets with insufficient headroom
83 */
84struct stats {
85 u64 num_ul_packets;
86 u64 num_dl_packets;
87 u64 num_insufficient_headroom_packets;
88};
89
90/**
91 * struct ipa_gsb_mux_hdr - ipa gsb mux header,
92 * @iface_hdl: interface handle
93 * @qmap_id: qmap id
94 * @pkt_size: packet size
95 */
96struct ipa_gsb_mux_hdr {
97 u8 iface_hdl;
98 u8 qmap_id;
99 u16 pkt_size;
100};
101
102/**
103 * struct ipa_gsb_iface_info - GSB interface information
104 * @netdev_name: network interface name
105 * @device_ethaddr: network interface ethernet address
106 * @priv: client's private data. to be used in client's callbacks
107 * @tx_dp_notify: client callback for handling IPA ODU_PROD callback
108 * @send_dl_skb: client callback for sending skb in downlink direction
109 * @iface_stats: statistics, how many packets were transmitted
110 * using the SW bridge.
111 * @partial_hdr_hdl: handle for partial header
112 * @wakeup_request: client callback to wakeup
113 * @is_conencted: is interface connected ?
114 * @is_resumed: is interface resumed ?
115 * @iface_hdl: interface handle
116 */
117struct ipa_gsb_iface_info {
118 char netdev_name[IPA_RESOURCE_NAME_MAX];
119 u8 device_ethaddr[ETH_ALEN];
120 void *priv;
121 ipa_notify_cb tx_dp_notify;
122 int (*send_dl_skb)(void *priv, struct sk_buff *skb);
123 struct stats iface_stats;
124 uint32_t partial_hdr_hdl[IPA_IP_MAX];
125 void (*wakeup_request)(void *);
126 bool is_connected;
127 bool is_resumed;
128 u8 iface_hdl;
129};
130
131/**
132 * struct ipa_gsb_context - GSB driver context information
133 * @logbuf: buffer of ipc logging
134 * @logbuf_low: buffer of ipc logging (low priority)
Bojun Panc8580822018-06-27 17:33:31 -0700135 * @lock: global mutex lock for global variables
Shihuan Liufbc51862018-02-27 23:21:14 -0800136 * @prod_hdl: handle for prod pipe
137 * @cons_hdl: handle for cons pipe
138 * @ipa_sys_desc_size: sys pipe desc size
139 * @num_iface: number of interface
140 * @iface_hdl: interface handles
141 * @num_connected_iface: number of connected interface
142 * @num_resumed_iface: number of resumed interface
143 * @iface: interface information
Bojun Panc8580822018-06-27 17:33:31 -0700144 * @iface_lock: interface mutex lock for control path
145 * @iface_spinlock: interface spinlock for data path
Shihuan Liufbc51862018-02-27 23:21:14 -0800146 * @pm_hdl: IPA PM handle
147 */
148struct ipa_gsb_context {
149 void *logbuf;
150 void *logbuf_low;
151 struct mutex lock;
152 u32 prod_hdl;
153 u32 cons_hdl;
154 u32 ipa_sys_desc_size;
155 int num_iface;
156 bool iface_hdl[MAX_SUPPORTED_IFACE];
157 int num_connected_iface;
158 int num_resumed_iface;
159 struct ipa_gsb_iface_info *iface[MAX_SUPPORTED_IFACE];
Bojun Panc8580822018-06-27 17:33:31 -0700160 struct mutex iface_lock[MAX_SUPPORTED_IFACE];
161 spinlock_t iface_spinlock[MAX_SUPPORTED_IFACE];
Shihuan Liufbc51862018-02-27 23:21:14 -0800162 u32 pm_hdl;
Mohammed Javidaf75b0c2018-09-20 16:17:16 +0530163 atomic_t disconnect_in_progress;
Shihuan Liufbc51862018-02-27 23:21:14 -0800164};
165
166static struct ipa_gsb_context *ipa_gsb_ctx;
167
168#ifdef CONFIG_DEBUG_FS
169static ssize_t ipa_gsb_debugfs_stats(struct file *file,
170 char __user *ubuf,
171 size_t count,
172 loff_t *ppos)
173{
174 int i, nbytes = 0;
175
176 for (i = 0; i < MAX_SUPPORTED_IFACE; i++)
177 if (ipa_gsb_ctx->iface[i] != NULL) {
178 nbytes += scnprintf(&dbg_buff[nbytes],
179 IPA_GSB_MAX_MSG_LEN - nbytes,
180 "netdev: %s\n",
181 ipa_gsb_ctx->iface[i]->netdev_name);
182
183 nbytes += scnprintf(&dbg_buff[nbytes],
184 IPA_GSB_MAX_MSG_LEN - nbytes,
185 "UL packets: %lld\n",
186 ipa_gsb_ctx->iface[i]->
187 iface_stats.num_ul_packets);
188
189 nbytes += scnprintf(&dbg_buff[nbytes],
190 IPA_GSB_MAX_MSG_LEN - nbytes,
191 "DL packets: %lld\n",
192 ipa_gsb_ctx->iface[i]->
193 iface_stats.num_dl_packets);
194
195 nbytes += scnprintf(&dbg_buff[nbytes],
196 IPA_GSB_MAX_MSG_LEN - nbytes,
197 "packets with insufficient headroom: %lld\n",
198 ipa_gsb_ctx->iface[i]->
199 iface_stats.num_insufficient_headroom_packets);
200 }
201 return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, nbytes);
202}
203
204static const struct file_operations ipa_gsb_stats_ops = {
205 .read = ipa_gsb_debugfs_stats,
206};
207
208static void ipa_gsb_debugfs_init(void)
209{
210 const mode_t read_only_mode = 00444;
211
212 dent = debugfs_create_dir("ipa_gsb", NULL);
213 if (IS_ERR(dent)) {
214 IPA_GSB_ERR("fail to create folder ipa_gsb\n");
215 return;
216 }
217
218 dfile_stats =
219 debugfs_create_file("stats", read_only_mode, dent,
220 NULL, &ipa_gsb_stats_ops);
221 if (!dfile_stats || IS_ERR(dfile_stats)) {
222 IPA_GSB_ERR("fail to create file stats\n");
223 goto fail;
224 }
225
226 return;
227
228fail:
229 debugfs_remove_recursive(dent);
230}
231
232static void ipa_gsb_debugfs_destroy(void)
233{
234 debugfs_remove_recursive(dent);
235}
236#else
237static void ipa_gsb_debugfs_init(void)
238{
239}
240
241static void ipa_gsb_debugfs_destroy(void)
242{
243}
244#endif
245
246static int ipa_gsb_driver_init(struct odu_bridge_params *params)
247{
Bojun Panc8580822018-06-27 17:33:31 -0700248 int i;
Shihuan Liufbc51862018-02-27 23:21:14 -0800249 if (!ipa_is_ready()) {
250 IPA_GSB_ERR("IPA is not ready\n");
251 return -EFAULT;
252 }
253
254 ipa_gsb_ctx = kzalloc(sizeof(*ipa_gsb_ctx),
255 GFP_KERNEL);
256
257 if (!ipa_gsb_ctx)
258 return -ENOMEM;
259
260 mutex_init(&ipa_gsb_ctx->lock);
Bojun Panc8580822018-06-27 17:33:31 -0700261 for (i = 0; i < MAX_SUPPORTED_IFACE; i++) {
262 mutex_init(&ipa_gsb_ctx->iface_lock[i]);
263 spin_lock_init(&ipa_gsb_ctx->iface_spinlock[i]);
264 }
Shihuan Liufbc51862018-02-27 23:21:14 -0800265 ipa_gsb_debugfs_init();
266
267 return 0;
268}
269
270static int ipa_gsb_commit_partial_hdr(struct ipa_gsb_iface_info *iface_info)
271{
272 int i;
273 struct ipa_ioc_add_hdr *hdr;
274
275 if (!iface_info) {
276 IPA_GSB_ERR("invalid input\n");
277 return -EINVAL;
278 }
279
280 hdr = kzalloc(sizeof(struct ipa_ioc_add_hdr) +
281 2 * sizeof(struct ipa_hdr_add), GFP_KERNEL);
282 if (!hdr)
283 return -ENOMEM;
284
285 hdr->commit = 1;
286 hdr->num_hdrs = 2;
287
288 snprintf(hdr->hdr[0].name, sizeof(hdr->hdr[0].name),
289 "%s_ipv4", iface_info->netdev_name);
290 snprintf(hdr->hdr[1].name, sizeof(hdr->hdr[1].name),
291 "%s_ipv6", iface_info->netdev_name);
Bojun Panffaf7202018-05-31 16:16:25 -0700292 /*
293 * partial header:
294 * [hdl][QMAP ID][pkt size][Dummy Header][ETH header]
295 */
Shihuan Liufbc51862018-02-27 23:21:14 -0800296 for (i = IPA_IP_v4; i < IPA_IP_MAX; i++) {
Bojun Panffaf7202018-05-31 16:16:25 -0700297 /*
298 * Optimization: add dummy header to reserve space
299 * for rndis header, so we can do the skb_clone
300 * instead of deep copy.
301 */
302 hdr->hdr[i].hdr_len = ETH_HLEN +
303 sizeof(struct ipa_gsb_mux_hdr) +
304 IPA_GSB_SKB_DUMMY_HEADER;
Shihuan Liufbc51862018-02-27 23:21:14 -0800305 hdr->hdr[i].type = IPA_HDR_L2_ETHERNET_II;
306 hdr->hdr[i].is_partial = 1;
307 hdr->hdr[i].is_eth2_ofst_valid = 1;
Bojun Panffaf7202018-05-31 16:16:25 -0700308 hdr->hdr[i].eth2_ofst = sizeof(struct ipa_gsb_mux_hdr) +
309 IPA_GSB_SKB_DUMMY_HEADER;
Shihuan Liufbc51862018-02-27 23:21:14 -0800310 /* populate iface handle */
311 hdr->hdr[i].hdr[0] = iface_info->iface_hdl;
312 /* populate src ETH address */
Bojun Panffaf7202018-05-31 16:16:25 -0700313 memcpy(&hdr->hdr[i].hdr[10 + IPA_GSB_SKB_DUMMY_HEADER],
314 iface_info->device_ethaddr, 6);
Shihuan Liufbc51862018-02-27 23:21:14 -0800315 /* populate Ethertype */
316 if (i == IPA_IP_v4)
Bojun Panffaf7202018-05-31 16:16:25 -0700317 *(u16 *)(hdr->hdr[i].hdr + 16 +
318 IPA_GSB_SKB_DUMMY_HEADER) = htons(ETH_P_IP);
Shihuan Liufbc51862018-02-27 23:21:14 -0800319 else
Bojun Panffaf7202018-05-31 16:16:25 -0700320 *(u16 *)(hdr->hdr[i].hdr + 16 +
321 IPA_GSB_SKB_DUMMY_HEADER) = htons(ETH_P_IPV6);
Shihuan Liufbc51862018-02-27 23:21:14 -0800322 }
323
324 if (ipa_add_hdr(hdr)) {
325 IPA_GSB_ERR("fail to add partial headers\n");
326 kfree(hdr);
327 return -EFAULT;
328 }
329
330 for (i = IPA_IP_v4; i < IPA_IP_MAX; i++)
331 iface_info->partial_hdr_hdl[i] =
332 hdr->hdr[i].hdr_hdl;
333
334 IPA_GSB_DBG("added partial hdr hdl for ipv4: %d\n",
335 iface_info->partial_hdr_hdl[IPA_IP_v4]);
336 IPA_GSB_DBG("added partial hdr hdl for ipv6: %d\n",
337 iface_info->partial_hdr_hdl[IPA_IP_v6]);
338
339 kfree(hdr);
340 return 0;
341}
342
343static void ipa_gsb_delete_partial_hdr(struct ipa_gsb_iface_info *iface_info)
344{
345 struct ipa_ioc_del_hdr *del_hdr;
346
347 del_hdr = kzalloc(sizeof(struct ipa_ioc_del_hdr) +
348 2 * sizeof(struct ipa_hdr_del), GFP_KERNEL);
349 if (!del_hdr)
350 return;
351
352 del_hdr->commit = 1;
353 del_hdr->num_hdls = 2;
354 del_hdr->hdl[IPA_IP_v4].hdl = iface_info->partial_hdr_hdl[IPA_IP_v4];
355 del_hdr->hdl[IPA_IP_v6].hdl = iface_info->partial_hdr_hdl[IPA_IP_v6];
356
357 if (ipa_del_hdr(del_hdr) != 0)
358 IPA_GSB_ERR("failed to delete partial hdr\n");
359
360 IPA_GSB_DBG("deleted partial hdr hdl for ipv4: %d\n",
361 iface_info->partial_hdr_hdl[IPA_IP_v4]);
362 IPA_GSB_DBG("deleted partial hdr hdl for ipv6: %d\n",
363 iface_info->partial_hdr_hdl[IPA_IP_v6]);
364
365 kfree(del_hdr);
366}
367
368static int ipa_gsb_reg_intf_props(struct ipa_gsb_iface_info *iface_info)
369{
370 struct ipa_tx_intf tx;
371 struct ipa_rx_intf rx;
372 struct ipa_ioc_tx_intf_prop tx_prop[2];
373 struct ipa_ioc_rx_intf_prop rx_prop[2];
374
375 /* populate tx prop */
376 tx.num_props = 2;
377 tx.prop = tx_prop;
378
379 memset(tx_prop, 0, sizeof(tx_prop));
380 tx_prop[0].ip = IPA_IP_v4;
381 tx_prop[0].dst_pipe = IPA_CLIENT_ODU_EMB_CONS;
382 tx_prop[0].hdr_l2_type = IPA_HDR_L2_ETHERNET_II;
383 snprintf(tx_prop[0].hdr_name, sizeof(tx_prop[0].hdr_name),
384 "%s_ipv4", iface_info->netdev_name);
385
386 tx_prop[1].ip = IPA_IP_v6;
387 tx_prop[1].dst_pipe = IPA_CLIENT_ODU_EMB_CONS;
388 tx_prop[1].hdr_l2_type = IPA_HDR_L2_ETHERNET_II;
389 snprintf(tx_prop[1].hdr_name, sizeof(tx_prop[1].hdr_name),
390 "%s_ipv6", iface_info->netdev_name);
391
392 /* populate rx prop */
393 rx.num_props = 2;
394 rx.prop = rx_prop;
395
396 memset(rx_prop, 0, sizeof(rx_prop));
397 rx_prop[0].ip = IPA_IP_v4;
398 rx_prop[0].src_pipe = IPA_CLIENT_ODU_PROD;
399 rx_prop[0].hdr_l2_type = IPA_HDR_L2_ETHERNET_II;
400 rx_prop[0].attrib.attrib_mask |= IPA_FLT_META_DATA;
401 rx_prop[0].attrib.meta_data = iface_info->iface_hdl;
402 rx_prop[0].attrib.meta_data_mask = 0xFF;
403
404 rx_prop[1].ip = IPA_IP_v6;
405 rx_prop[1].src_pipe = IPA_CLIENT_ODU_PROD;
406 rx_prop[1].hdr_l2_type = IPA_HDR_L2_ETHERNET_II;
407 rx_prop[1].attrib.attrib_mask |= IPA_FLT_META_DATA;
408 rx_prop[1].attrib.meta_data = iface_info->iface_hdl;
409 rx_prop[1].attrib.meta_data_mask = 0xFF;
410
411 if (ipa_register_intf(iface_info->netdev_name, &tx, &rx)) {
412 IPA_GSB_ERR("fail to add interface prop\n");
413 return -EFAULT;
414 }
415
416 return 0;
417}
418
419static void ipa_gsb_dereg_intf_props(struct ipa_gsb_iface_info *iface_info)
420{
421 if (ipa_deregister_intf(iface_info->netdev_name) != 0)
422 IPA_GSB_ERR("fail to dereg intf props\n");
423
424 IPA_GSB_DBG("deregistered iface props for %s\n",
425 iface_info->netdev_name);
426}
427
428static void ipa_gsb_pm_cb(void *user_data, enum ipa_pm_cb_event event)
429{
430 int i;
431
432 if (event != IPA_PM_REQUEST_WAKEUP) {
433 IPA_GSB_ERR("Unexpected event %d\n", event);
434 WARN_ON(1);
435 return;
436 }
437
Bojun Panffaf7202018-05-31 16:16:25 -0700438 IPA_GSB_DBG_LOW("wake up clients\n");
Shihuan Liufbc51862018-02-27 23:21:14 -0800439 for (i = 0; i < MAX_SUPPORTED_IFACE; i++)
440 if (ipa_gsb_ctx->iface[i] != NULL)
441 ipa_gsb_ctx->iface[i]->wakeup_request(
442 ipa_gsb_ctx->iface[i]->priv);
443}
444
445static int ipa_gsb_register_pm(void)
446{
447 struct ipa_pm_register_params reg_params;
448 int ret;
449
450 memset(&reg_params, 0, sizeof(reg_params));
451 reg_params.name = "ipa_gsb";
452 reg_params.callback = ipa_gsb_pm_cb;
453 reg_params.user_data = NULL;
454 reg_params.group = IPA_PM_GROUP_DEFAULT;
455
456 ret = ipa_pm_register(&reg_params,
457 &ipa_gsb_ctx->pm_hdl);
458 if (ret) {
459 IPA_GSB_ERR("fail to register with PM %d\n", ret);
460 goto fail_pm_reg;
461 }
462 IPA_GSB_DBG("ipa pm hdl: %d\n", ipa_gsb_ctx->pm_hdl);
463
464 ret = ipa_pm_associate_ipa_cons_to_client(ipa_gsb_ctx->pm_hdl,
465 IPA_CLIENT_ODU_EMB_CONS);
466 if (ret) {
467 IPA_GSB_ERR("fail to associate cons with PM %d\n", ret);
468 goto fail_pm_cons;
469 }
470
471 return 0;
472
473fail_pm_cons:
474 ipa_pm_deregister(ipa_gsb_ctx->pm_hdl);
475 ipa_gsb_ctx->pm_hdl = ~0;
476fail_pm_reg:
477 return ret;
478}
479
480int ipa_bridge_init(struct ipa_bridge_init_params *params, u32 *hdl)
481{
482 int i, ret;
483 struct ipa_gsb_iface_info *new_intf;
484
485 if (!params || !params->wakeup_request || !hdl ||
486 !params->info.netdev_name || !params->info.tx_dp_notify ||
487 !params->info.send_dl_skb) {
Bojun Panc8580822018-06-27 17:33:31 -0700488 IPA_GSB_ERR("Invalid parameters\n");
Shihuan Liufbc51862018-02-27 23:21:14 -0800489 return -EINVAL;
490 }
491
492 IPA_GSB_DBG("netdev_name: %s\n", params->info.netdev_name);
493
494 if (ipa_gsb_ctx == NULL) {
495 ret = ipa_gsb_driver_init(&params->info);
496 if (ret) {
497 IPA_GSB_ERR("fail to init ipa gsb driver\n");
498 return -EFAULT;
499 }
500 ipa_gsb_ctx->ipa_sys_desc_size =
501 params->info.ipa_desc_size;
502 IPA_GSB_DBG("desc size: %d\n", ipa_gsb_ctx->ipa_sys_desc_size);
503 }
504
505 mutex_lock(&ipa_gsb_ctx->lock);
506
507 if (params->info.ipa_desc_size != ipa_gsb_ctx->ipa_sys_desc_size) {
508 IPA_GSB_ERR("unmatch: orig desc size %d, new desc size %d\n",
509 ipa_gsb_ctx->ipa_sys_desc_size,
510 params->info.ipa_desc_size);
511 mutex_unlock(&ipa_gsb_ctx->lock);
512 return -EFAULT;
513 }
514
515 for (i = 0; i < MAX_SUPPORTED_IFACE; i++)
516 if (ipa_gsb_ctx->iface[i] != NULL &&
517 strnlen(ipa_gsb_ctx->iface[i]->netdev_name,
518 IPA_RESOURCE_NAME_MAX) ==
519 strnlen(params->info.netdev_name,
520 IPA_RESOURCE_NAME_MAX) &&
521 strcmp(ipa_gsb_ctx->iface[i]->netdev_name,
522 params->info.netdev_name) == 0) {
523 IPA_GSB_ERR("intf was added before.\n");
524 mutex_unlock(&ipa_gsb_ctx->lock);
525 return -EFAULT;
526 }
527
528 if (ipa_gsb_ctx->num_iface == MAX_SUPPORTED_IFACE) {
529 IPA_GSB_ERR("reached maximum supported interfaces");
530 mutex_unlock(&ipa_gsb_ctx->lock);
531 return -EFAULT;
532 }
533
534 for (i = 0; i < MAX_SUPPORTED_IFACE; i++)
535 if (ipa_gsb_ctx->iface_hdl[i] == false) {
536 ipa_gsb_ctx->iface_hdl[i] = true;
537 *hdl = i;
538 IPA_GSB_DBG("iface hdl: %d\n", *hdl);
539 break;
540 }
541
542 IPA_GSB_DBG("intf was not added before, proceed.\n");
543 new_intf = kzalloc(sizeof(*new_intf), GFP_KERNEL);
544 if (new_intf == NULL) {
545 ret = -ENOMEM;
546 goto fail_alloc_mem;
547 }
548
549 strlcpy(new_intf->netdev_name, params->info.netdev_name,
550 sizeof(new_intf->netdev_name));
551 new_intf->wakeup_request = params->wakeup_request;
552 new_intf->priv = params->info.priv;
553 new_intf->tx_dp_notify = params->info.tx_dp_notify;
554 new_intf->send_dl_skb = params->info.send_dl_skb;
555 new_intf->iface_hdl = *hdl;
556 memcpy(new_intf->device_ethaddr, params->info.device_ethaddr,
557 sizeof(new_intf->device_ethaddr));
558
559 if (ipa_gsb_commit_partial_hdr(new_intf) != 0) {
560 IPA_GSB_ERR("fail to commit partial hdrs\n");
561 ret = -EFAULT;
562 goto fail_partial_hdr;
563 }
564
565 if (ipa_gsb_reg_intf_props(new_intf) != 0) {
566 IPA_GSB_ERR("fail to register interface props\n");
567 ret = -EFAULT;
568 goto fail_reg_intf_props;
569 }
570
571 if (ipa_gsb_ctx->num_iface == 0) {
572 ret = ipa_gsb_register_pm();
573 if (ret) {
574 IPA_GSB_ERR("fail to register with IPA PM %d\n", ret);
575 ret = -EFAULT;
576 goto fail_register_pm;
577 }
578 }
579
580 ipa_gsb_ctx->iface[*hdl] = new_intf;
581 ipa_gsb_ctx->num_iface++;
582 IPA_GSB_DBG("num_iface %d\n", ipa_gsb_ctx->num_iface);
583 mutex_unlock(&ipa_gsb_ctx->lock);
584 return 0;
585
586fail_register_pm:
587 ipa_gsb_dereg_intf_props(new_intf);
588fail_reg_intf_props:
589 ipa_gsb_delete_partial_hdr(new_intf);
590fail_partial_hdr:
591 kfree(new_intf);
592fail_alloc_mem:
593 ipa_gsb_ctx->iface_hdl[*hdl] = false;
594 mutex_unlock(&ipa_gsb_ctx->lock);
595 return ret;
596}
597EXPORT_SYMBOL(ipa_bridge_init);
598
599static void ipa_gsb_deregister_pm(void)
600{
601 IPA_GSB_DBG("deregister ipa pm hdl: %d\n", ipa_gsb_ctx->pm_hdl);
602 ipa_pm_deactivate_sync(ipa_gsb_ctx->pm_hdl);
603 ipa_pm_deregister(ipa_gsb_ctx->pm_hdl);
604 ipa_gsb_ctx->pm_hdl = ~0;
605}
606
607int ipa_bridge_cleanup(u32 hdl)
608{
Bojun Panc8580822018-06-27 17:33:31 -0700609 int i;
Shihuan Liufbc51862018-02-27 23:21:14 -0800610 if (!ipa_gsb_ctx) {
611 IPA_GSB_ERR("ipa_gsb_ctx was not initialized\n");
612 return -EFAULT;
613 }
614
615 if (hdl >= MAX_SUPPORTED_IFACE) {
616 IPA_GSB_ERR("invalid hdl: %d\n", hdl);
617 return -EINVAL;
618 }
619
Bojun Panc8580822018-06-27 17:33:31 -0700620 mutex_lock(&ipa_gsb_ctx->iface_lock[hdl]);
621 if (!ipa_gsb_ctx->iface[hdl]) {
622 IPA_GSB_ERR("fail to find interface, hdl: %d\n", hdl);
623 mutex_unlock(&ipa_gsb_ctx->iface_lock[hdl]);
Shihuan Liufbc51862018-02-27 23:21:14 -0800624 return -EFAULT;
625 }
626
627 IPA_GSB_DBG("client hdl: %d\n", hdl);
Shihuan Liufbc51862018-02-27 23:21:14 -0800628
629 if (ipa_gsb_ctx->iface[hdl]->is_connected) {
630 IPA_GSB_ERR("cannot cleanup when iface is connected\n");
Bojun Panc8580822018-06-27 17:33:31 -0700631 mutex_unlock(&ipa_gsb_ctx->iface_lock[hdl]);
Shihuan Liufbc51862018-02-27 23:21:14 -0800632 return -EFAULT;
633 }
Shihuan Liufbc51862018-02-27 23:21:14 -0800634 ipa_gsb_dereg_intf_props(ipa_gsb_ctx->iface[hdl]);
635 ipa_gsb_delete_partial_hdr(ipa_gsb_ctx->iface[hdl]);
Bojun Panc8580822018-06-27 17:33:31 -0700636 spin_lock_bh(&ipa_gsb_ctx->iface_spinlock[hdl]);
Shihuan Liufbc51862018-02-27 23:21:14 -0800637 kfree(ipa_gsb_ctx->iface[hdl]);
638 ipa_gsb_ctx->iface[hdl] = NULL;
639 ipa_gsb_ctx->iface_hdl[hdl] = false;
Bojun Panc8580822018-06-27 17:33:31 -0700640 spin_unlock_bh(&ipa_gsb_ctx->iface_spinlock[hdl]);
641 mutex_unlock(&ipa_gsb_ctx->iface_lock[hdl]);
642 mutex_lock(&ipa_gsb_ctx->lock);
Shihuan Liufbc51862018-02-27 23:21:14 -0800643 ipa_gsb_ctx->num_iface--;
644 IPA_GSB_DBG("num_iface %d\n", ipa_gsb_ctx->num_iface);
Shihuan Liufbc51862018-02-27 23:21:14 -0800645 if (ipa_gsb_ctx->num_iface == 0) {
646 ipa_gsb_deregister_pm();
647 ipa_gsb_debugfs_destroy();
648 ipc_log_context_destroy(ipa_gsb_ctx->logbuf);
649 ipc_log_context_destroy(ipa_gsb_ctx->logbuf_low);
650 mutex_unlock(&ipa_gsb_ctx->lock);
Bojun Panc8580822018-06-27 17:33:31 -0700651 mutex_destroy(&ipa_gsb_ctx->lock);
652 for (i = 0; i < MAX_SUPPORTED_IFACE; i++)
653 mutex_destroy(&ipa_gsb_ctx->iface_lock[i]);
Shihuan Liufbc51862018-02-27 23:21:14 -0800654 kfree(ipa_gsb_ctx);
655 ipa_gsb_ctx = NULL;
656 return 0;
657 }
Shihuan Liufbc51862018-02-27 23:21:14 -0800658 mutex_unlock(&ipa_gsb_ctx->lock);
659 return 0;
660}
661EXPORT_SYMBOL(ipa_bridge_cleanup);
662
Shihuan Liufbc51862018-02-27 23:21:14 -0800663static void ipa_gsb_cons_cb(void *priv, enum ipa_dp_evt_type evt,
664 unsigned long data)
665{
666 struct sk_buff *skb;
667 struct sk_buff *skb2;
668 struct ipa_gsb_mux_hdr *mux_hdr;
669 u16 pkt_size, pad_byte;
670 u8 hdl;
671
672 if (evt != IPA_RECEIVE) {
673 IPA_GSB_ERR("unexpected event\n");
674 WARN_ON(1);
675 return;
676 }
677
678 skb = (struct sk_buff *)data;
679
680 while (skb->len) {
681 mux_hdr = (struct ipa_gsb_mux_hdr *)skb->data;
682 pkt_size = mux_hdr->pkt_size;
683 /* 4-byte padding */
Bojun Panffaf7202018-05-31 16:16:25 -0700684 pad_byte = ((pkt_size + sizeof(*mux_hdr) + ETH_HLEN +
685 3 + IPA_GSB_SKB_DUMMY_HEADER) & ~3) -
686 (pkt_size + sizeof(*mux_hdr) +
687 ETH_HLEN + IPA_GSB_SKB_DUMMY_HEADER);
Shihuan Liufbc51862018-02-27 23:21:14 -0800688 hdl = mux_hdr->iface_hdl;
Bojun Panc8580822018-06-27 17:33:31 -0700689 if (hdl >= MAX_SUPPORTED_IFACE) {
690 IPA_GSB_ERR("invalid hdl: %d\n", hdl);
691 break;
692 }
Bojun Panffaf7202018-05-31 16:16:25 -0700693 IPA_GSB_DBG_LOW("pkt_size: %d, pad_byte: %d, hdl: %d\n",
Shihuan Liufbc51862018-02-27 23:21:14 -0800694 pkt_size, pad_byte, hdl);
695
Bojun Panffaf7202018-05-31 16:16:25 -0700696 /* remove 4 byte mux header AND dummy header*/
697 skb_pull(skb, sizeof(*mux_hdr) + IPA_GSB_SKB_DUMMY_HEADER);
698
699 skb2 = skb_clone(skb, GFP_KERNEL);
700 if (!skb2) {
701 IPA_GSB_ERR("skb_clone failed\n");
702 WARN_ON(1);
703 break;
704 }
705 skb_trim(skb2, pkt_size + ETH_HLEN);
Bojun Panc8580822018-06-27 17:33:31 -0700706 spin_lock_bh(&ipa_gsb_ctx->iface_spinlock[hdl]);
707 if (ipa_gsb_ctx->iface[hdl] != NULL) {
708 ipa_gsb_ctx->iface[hdl]->send_dl_skb(
709 ipa_gsb_ctx->iface[hdl]->priv, skb2);
710 ipa_gsb_ctx->iface[hdl]->iface_stats.num_dl_packets++;
711 spin_unlock_bh(&ipa_gsb_ctx->iface_spinlock[hdl]);
712 skb_pull(skb, pkt_size + ETH_HLEN + pad_byte);
713 } else {
714 IPA_GSB_ERR("Invalid hdl: %d, drop the skb\n", hdl);
715 spin_unlock_bh(&ipa_gsb_ctx->iface_spinlock[hdl]);
716 dev_kfree_skb_any(skb2);
717 break;
718 }
Shihuan Liufbc51862018-02-27 23:21:14 -0800719 }
Bojun Panc8580822018-06-27 17:33:31 -0700720
Bojun Panffaf7202018-05-31 16:16:25 -0700721 if (skb) {
722 dev_kfree_skb_any(skb);
723 skb = NULL;
724 }
Shihuan Liufbc51862018-02-27 23:21:14 -0800725}
726
727static void ipa_gsb_tx_dp_notify(void *priv, enum ipa_dp_evt_type evt,
Bojun Panc8580822018-06-27 17:33:31 -0700728 unsigned long data)
Shihuan Liufbc51862018-02-27 23:21:14 -0800729{
730 struct sk_buff *skb;
731 struct ipa_gsb_mux_hdr *mux_hdr;
732 u8 hdl;
733
734 skb = (struct sk_buff *)data;
735
736 if (evt != IPA_WRITE_DONE && evt != IPA_RECEIVE) {
737 IPA_GSB_ERR("unexpected event: %d\n", evt);
738 dev_kfree_skb_any(skb);
739 return;
740 }
741
742 /* fetch iface handle from header */
743 mux_hdr = (struct ipa_gsb_mux_hdr *)skb->data;
744 /* change to host order */
745 *(u32 *)mux_hdr = ntohl(*(u32 *)mux_hdr);
746 hdl = mux_hdr->iface_hdl;
Bojun Panc8580822018-06-27 17:33:31 -0700747 if (!ipa_gsb_ctx->iface[hdl]) {
748 IPA_GSB_ERR("invalid hdl: %d and cb, drop the skb\n", hdl);
749 dev_kfree_skb_any(skb);
750 return;
751 }
Bojun Panffaf7202018-05-31 16:16:25 -0700752 IPA_GSB_DBG_LOW("evt: %d, hdl in tx_dp_notify: %d\n", evt, hdl);
Shihuan Liufbc51862018-02-27 23:21:14 -0800753
754 /* remove 4 byte mux header */
755 skb_pull(skb, sizeof(struct ipa_gsb_mux_hdr));
756 ipa_gsb_ctx->iface[hdl]->tx_dp_notify(
Bojun Panc8580822018-06-27 17:33:31 -0700757 ipa_gsb_ctx->iface[hdl]->priv, evt,
758 (unsigned long)skb);
Shihuan Liufbc51862018-02-27 23:21:14 -0800759}
760
761static int ipa_gsb_connect_sys_pipe(void)
762{
763 struct ipa_sys_connect_params prod_params;
764 struct ipa_sys_connect_params cons_params;
765 int res;
766
767 memset(&prod_params, 0, sizeof(prod_params));
768 memset(&cons_params, 0, sizeof(cons_params));
769
770 /* configure RX EP */
771 prod_params.client = IPA_CLIENT_ODU_PROD;
772 prod_params.ipa_ep_cfg.hdr.hdr_len =
773 ETH_HLEN + sizeof(struct ipa_gsb_mux_hdr);
774 prod_params.ipa_ep_cfg.nat.nat_en = IPA_SRC_NAT;
775 prod_params.ipa_ep_cfg.hdr.hdr_ofst_metadata_valid = 1;
776 prod_params.ipa_ep_cfg.hdr.hdr_ofst_metadata = 0;
777 prod_params.desc_fifo_sz = ipa_gsb_ctx->ipa_sys_desc_size;
778 prod_params.priv = NULL;
779 prod_params.notify = ipa_gsb_tx_dp_notify;
780 res = ipa_setup_sys_pipe(&prod_params,
781 &ipa_gsb_ctx->prod_hdl);
782 if (res) {
783 IPA_GSB_ERR("fail to setup prod sys pipe %d\n", res);
784 goto fail_prod;
785 }
786
787 /* configure TX EP */
788 cons_params.client = IPA_CLIENT_ODU_EMB_CONS;
789 cons_params.ipa_ep_cfg.hdr.hdr_len =
Bojun Panffaf7202018-05-31 16:16:25 -0700790 ETH_HLEN + sizeof(struct ipa_gsb_mux_hdr) +
791 IPA_GSB_SKB_DUMMY_HEADER;
Shihuan Liufbc51862018-02-27 23:21:14 -0800792 cons_params.ipa_ep_cfg.hdr.hdr_ofst_pkt_size_valid = 1;
793 cons_params.ipa_ep_cfg.hdr.hdr_ofst_pkt_size = 2;
794 cons_params.ipa_ep_cfg.hdr_ext.hdr_pad_to_alignment = 2;
795 cons_params.ipa_ep_cfg.hdr_ext.hdr_little_endian = true;
796 cons_params.ipa_ep_cfg.nat.nat_en = IPA_BYPASS_NAT;
797 /* setup aggregation */
798 cons_params.ipa_ep_cfg.aggr.aggr_en = IPA_ENABLE_AGGR;
799 cons_params.ipa_ep_cfg.aggr.aggr = IPA_GENERIC;
800 cons_params.ipa_ep_cfg.aggr.aggr_time_limit =
801 IPA_GSB_AGGR_TIME_LIMIT;
802 cons_params.ipa_ep_cfg.aggr.aggr_byte_limit =
803 IPA_GSB_AGGR_BYTE_LIMIT;
804 cons_params.desc_fifo_sz = ipa_gsb_ctx->ipa_sys_desc_size;
805 cons_params.priv = NULL;
806 cons_params.notify = ipa_gsb_cons_cb;
807 res = ipa_setup_sys_pipe(&cons_params,
808 &ipa_gsb_ctx->cons_hdl);
809 if (res) {
810 IPA_GSB_ERR("fail to setup cons sys pipe %d\n", res);
811 goto fail_cons;
812 }
813
814 IPA_GSB_DBG("prod_hdl = %d, cons_hdl = %d\n",
815 ipa_gsb_ctx->prod_hdl, ipa_gsb_ctx->cons_hdl);
816
817 return 0;
818
819fail_cons:
820 ipa_teardown_sys_pipe(ipa_gsb_ctx->prod_hdl);
821 ipa_gsb_ctx->prod_hdl = 0;
822fail_prod:
823 return res;
824}
825
826int ipa_bridge_connect(u32 hdl)
827{
828 int ret;
829
830 if (!ipa_gsb_ctx) {
831 IPA_GSB_ERR("ipa_gsb_ctx was not initialized\n");
832 return -EFAULT;
833 }
834
Bojun Panc8580822018-06-27 17:33:31 -0700835 if (hdl >= MAX_SUPPORTED_IFACE) {
836 IPA_GSB_ERR("invalid hdl: %d\n", hdl);
837 return -EINVAL;
838 }
839
Shihuan Liufbc51862018-02-27 23:21:14 -0800840 IPA_GSB_DBG("client hdl: %d\n", hdl);
841
Bojun Panc8580822018-06-27 17:33:31 -0700842 mutex_lock(&ipa_gsb_ctx->iface_lock[hdl]);
843 if (!ipa_gsb_ctx->iface[hdl]) {
844 IPA_GSB_ERR("fail to find interface, hdl: %d\n", hdl);
845 mutex_unlock(&ipa_gsb_ctx->iface_lock[hdl]);
846 return -EFAULT;
847 }
Shihuan Liufbc51862018-02-27 23:21:14 -0800848
849 if (ipa_gsb_ctx->iface[hdl]->is_connected) {
850 IPA_GSB_DBG("iface was already connected\n");
Bojun Panc8580822018-06-27 17:33:31 -0700851 mutex_unlock(&ipa_gsb_ctx->iface_lock[hdl]);
Shihuan Liufbc51862018-02-27 23:21:14 -0800852 return 0;
853 }
854
Mohammedc8a44de2018-10-30 19:13:29 +0530855 mutex_lock(&ipa_gsb_ctx->lock);
Shihuan Liufbc51862018-02-27 23:21:14 -0800856 if (ipa_gsb_ctx->num_connected_iface == 0) {
857 ret = ipa_pm_activate_sync(ipa_gsb_ctx->pm_hdl);
858 if (ret) {
859 IPA_GSB_ERR("failed to activate ipa pm\n");
Mohammedc8a44de2018-10-30 19:13:29 +0530860 mutex_unlock(&ipa_gsb_ctx->lock);
Bojun Panc8580822018-06-27 17:33:31 -0700861 mutex_unlock(&ipa_gsb_ctx->iface_lock[hdl]);
Shihuan Liufbc51862018-02-27 23:21:14 -0800862 return ret;
863 }
864 ret = ipa_gsb_connect_sys_pipe();
865 if (ret) {
866 IPA_GSB_ERR("fail to connect pipe\n");
Mohammedc8a44de2018-10-30 19:13:29 +0530867 mutex_unlock(&ipa_gsb_ctx->lock);
Bojun Panc8580822018-06-27 17:33:31 -0700868 mutex_unlock(&ipa_gsb_ctx->iface_lock[hdl]);
Shihuan Liufbc51862018-02-27 23:21:14 -0800869 return ret;
870 }
871 }
872
873 /* connect = connect + resume */
874 ipa_gsb_ctx->iface[hdl]->is_connected = true;
875 ipa_gsb_ctx->iface[hdl]->is_resumed = true;
876
877 ipa_gsb_ctx->num_connected_iface++;
878 IPA_GSB_DBG("connected iface: %d\n",
879 ipa_gsb_ctx->num_connected_iface);
880 ipa_gsb_ctx->num_resumed_iface++;
881 IPA_GSB_DBG("num resumed iface: %d\n",
882 ipa_gsb_ctx->num_resumed_iface);
Mohammedc8a44de2018-10-30 19:13:29 +0530883 mutex_unlock(&ipa_gsb_ctx->lock);
Bojun Panc8580822018-06-27 17:33:31 -0700884 mutex_unlock(&ipa_gsb_ctx->iface_lock[hdl]);
Shihuan Liufbc51862018-02-27 23:21:14 -0800885 return 0;
886}
887EXPORT_SYMBOL(ipa_bridge_connect);
888
889static int ipa_gsb_disconnect_sys_pipe(void)
890{
891 int ret;
892
893 IPA_GSB_DBG("prod_hdl = %d, cons_hdl = %d\n",
894 ipa_gsb_ctx->prod_hdl, ipa_gsb_ctx->cons_hdl);
895
896 ret = ipa_teardown_sys_pipe(ipa_gsb_ctx->prod_hdl);
897 if (ret) {
898 IPA_GSB_ERR("failed to tear down prod pipe\n");
899 return -EFAULT;
900 }
901 ipa_gsb_ctx->prod_hdl = 0;
902
903 ret = ipa_teardown_sys_pipe(ipa_gsb_ctx->cons_hdl);
904 if (ret) {
905 IPA_GSB_ERR("failed to tear down cons pipe\n");
906 return -EFAULT;
907 }
908 ipa_gsb_ctx->cons_hdl = 0;
909
910 return 0;
911}
912
913int ipa_bridge_disconnect(u32 hdl)
914{
Mohammed Javidaf75b0c2018-09-20 16:17:16 +0530915 int ret = 0;
Shihuan Liufbc51862018-02-27 23:21:14 -0800916
917 if (!ipa_gsb_ctx) {
918 IPA_GSB_ERR("ipa_gsb_ctx was not initialized\n");
919 return -EFAULT;
920 }
921
Bojun Panc8580822018-06-27 17:33:31 -0700922 if (hdl >= MAX_SUPPORTED_IFACE) {
923 IPA_GSB_ERR("invalid hdl: %d\n", hdl);
924 return -EINVAL;
925 }
926
Shihuan Liufbc51862018-02-27 23:21:14 -0800927 IPA_GSB_DBG("client hdl: %d\n", hdl);
928
Bojun Panc8580822018-06-27 17:33:31 -0700929 mutex_lock(&ipa_gsb_ctx->iface_lock[hdl]);
Mohammed Javidaf75b0c2018-09-20 16:17:16 +0530930 atomic_set(&ipa_gsb_ctx->disconnect_in_progress, 1);
931
Bojun Panc8580822018-06-27 17:33:31 -0700932 if (!ipa_gsb_ctx->iface[hdl]) {
933 IPA_GSB_ERR("fail to find interface, hdl: %d\n", hdl);
Mohammed Javidaf75b0c2018-09-20 16:17:16 +0530934 ret = -EFAULT;
935 goto fail;
Bojun Panc8580822018-06-27 17:33:31 -0700936 }
Shihuan Liufbc51862018-02-27 23:21:14 -0800937
938 if (!ipa_gsb_ctx->iface[hdl]->is_connected) {
939 IPA_GSB_DBG("iface was not connected\n");
Mohammed Javidaf75b0c2018-09-20 16:17:16 +0530940 ret = 0;
941 goto fail;
Shihuan Liufbc51862018-02-27 23:21:14 -0800942 }
943
Mohammedc8a44de2018-10-30 19:13:29 +0530944 mutex_lock(&ipa_gsb_ctx->lock);
Shihuan Liufbc51862018-02-27 23:21:14 -0800945 if (ipa_gsb_ctx->num_connected_iface == 1) {
946 ret = ipa_gsb_disconnect_sys_pipe();
947 if (ret) {
948 IPA_GSB_ERR("fail to discon pipes\n");
Mohammed Javidaf75b0c2018-09-20 16:17:16 +0530949 ret = -EFAULT;
950 goto fail;
Shihuan Liufbc51862018-02-27 23:21:14 -0800951 }
952
953 ret = ipa_pm_deactivate_sync(ipa_gsb_ctx->pm_hdl);
954 if (ret) {
955 IPA_GSB_ERR("failed to deactivate ipa pm\n");
Mohammed Javidaf75b0c2018-09-20 16:17:16 +0530956 ret = -EFAULT;
957 goto fail;
Shihuan Liufbc51862018-02-27 23:21:14 -0800958 }
959 }
960
961 /* disconnect = suspend + disconnect */
962 ipa_gsb_ctx->iface[hdl]->is_connected = false;
963 ipa_gsb_ctx->num_connected_iface--;
964 IPA_GSB_DBG("connected iface: %d\n",
965 ipa_gsb_ctx->num_connected_iface);
966
967 if (ipa_gsb_ctx->iface[hdl]->is_resumed) {
968 ipa_gsb_ctx->iface[hdl]->is_resumed = false;
969 ipa_gsb_ctx->num_resumed_iface--;
970 IPA_GSB_DBG("num resumed iface: %d\n",
971 ipa_gsb_ctx->num_resumed_iface);
972 }
973
Mohammed Javidaf75b0c2018-09-20 16:17:16 +0530974fail:
Mohammedc8a44de2018-10-30 19:13:29 +0530975 mutex_unlock(&ipa_gsb_ctx->lock);
Mohammed Javidaf75b0c2018-09-20 16:17:16 +0530976 atomic_set(&ipa_gsb_ctx->disconnect_in_progress, 0);
Bojun Panc8580822018-06-27 17:33:31 -0700977 mutex_unlock(&ipa_gsb_ctx->iface_lock[hdl]);
Mohammed Javidaf75b0c2018-09-20 16:17:16 +0530978 return ret;
Shihuan Liufbc51862018-02-27 23:21:14 -0800979}
980EXPORT_SYMBOL(ipa_bridge_disconnect);
981
982int ipa_bridge_resume(u32 hdl)
983{
984 int ret;
985
986 if (!ipa_gsb_ctx) {
987 IPA_GSB_ERR("ipa_gsb_ctx was not initialized\n");
988 return -EFAULT;
989 }
990
Bojun Panc8580822018-06-27 17:33:31 -0700991 if (hdl >= MAX_SUPPORTED_IFACE) {
992 IPA_GSB_ERR("invalid hdl: %d\n", hdl);
993 return -EINVAL;
994 }
995
Bojun Panffaf7202018-05-31 16:16:25 -0700996 IPA_GSB_DBG_LOW("client hdl: %d\n", hdl);
Shihuan Liufbc51862018-02-27 23:21:14 -0800997
Bojun Panc8580822018-06-27 17:33:31 -0700998 mutex_lock(&ipa_gsb_ctx->iface_lock[hdl]);
999 if (!ipa_gsb_ctx->iface[hdl]) {
1000 IPA_GSB_ERR("fail to find interface, hdl: %d\n", hdl);
1001 mutex_unlock(&ipa_gsb_ctx->iface_lock[hdl]);
1002 return -EFAULT;
1003 }
1004
Shihuan Liufbc51862018-02-27 23:21:14 -08001005 if (!ipa_gsb_ctx->iface[hdl]->is_connected) {
1006 IPA_GSB_ERR("iface is not connected\n");
Bojun Panc8580822018-06-27 17:33:31 -07001007 mutex_unlock(&ipa_gsb_ctx->iface_lock[hdl]);
Shihuan Liufbc51862018-02-27 23:21:14 -08001008 return -EFAULT;
1009 }
1010
1011 if (ipa_gsb_ctx->iface[hdl]->is_resumed) {
Bojun Panffaf7202018-05-31 16:16:25 -07001012 IPA_GSB_DBG_LOW("iface was already resumed\n");
Bojun Panc8580822018-06-27 17:33:31 -07001013 mutex_unlock(&ipa_gsb_ctx->iface_lock[hdl]);
Shihuan Liufbc51862018-02-27 23:21:14 -08001014 return 0;
1015 }
1016
Mohammedc8a44de2018-10-30 19:13:29 +05301017 mutex_lock(&ipa_gsb_ctx->lock);
Shihuan Liufbc51862018-02-27 23:21:14 -08001018 if (ipa_gsb_ctx->num_resumed_iface == 0) {
1019 ret = ipa_pm_activate_sync(ipa_gsb_ctx->pm_hdl);
1020 if (ret) {
1021 IPA_GSB_ERR("fail to activate ipa pm\n");
Mohammedc8a44de2018-10-30 19:13:29 +05301022 mutex_unlock(&ipa_gsb_ctx->lock);
Bojun Panc8580822018-06-27 17:33:31 -07001023 mutex_unlock(&ipa_gsb_ctx->iface_lock[hdl]);
Shihuan Liufbc51862018-02-27 23:21:14 -08001024 return ret;
1025 }
1026
1027 ret = ipa_start_gsi_channel(
1028 ipa_gsb_ctx->cons_hdl);
1029 if (ret) {
1030 IPA_GSB_ERR(
1031 "fail to start con ep %d\n",
1032 ret);
Mohammedc8a44de2018-10-30 19:13:29 +05301033 mutex_unlock(&ipa_gsb_ctx->lock);
Bojun Panc8580822018-06-27 17:33:31 -07001034 mutex_unlock(&ipa_gsb_ctx->iface_lock[hdl]);
Shihuan Liufbc51862018-02-27 23:21:14 -08001035 return ret;
1036 }
1037 }
1038
1039 ipa_gsb_ctx->iface[hdl]->is_resumed = true;
1040 ipa_gsb_ctx->num_resumed_iface++;
Bojun Panffaf7202018-05-31 16:16:25 -07001041 IPA_GSB_DBG_LOW("num resumed iface: %d\n",
Shihuan Liufbc51862018-02-27 23:21:14 -08001042 ipa_gsb_ctx->num_resumed_iface);
1043
Mohammedc8a44de2018-10-30 19:13:29 +05301044 mutex_unlock(&ipa_gsb_ctx->lock);
Bojun Panc8580822018-06-27 17:33:31 -07001045 mutex_unlock(&ipa_gsb_ctx->iface_lock[hdl]);
Shihuan Liufbc51862018-02-27 23:21:14 -08001046 return 0;
1047}
1048EXPORT_SYMBOL(ipa_bridge_resume);
1049
1050int ipa_bridge_suspend(u32 hdl)
1051{
1052 int ret;
1053
1054 if (!ipa_gsb_ctx) {
1055 IPA_GSB_ERR("ipa_gsb_ctx was not initialized\n");
1056 return -EFAULT;
1057 }
1058
Bojun Panc8580822018-06-27 17:33:31 -07001059 if (hdl >= MAX_SUPPORTED_IFACE) {
1060 IPA_GSB_ERR("invalid hdl: %d\n", hdl);
1061 return -EINVAL;
1062 }
1063
Bojun Panffaf7202018-05-31 16:16:25 -07001064 IPA_GSB_DBG_LOW("client hdl: %d\n", hdl);
Shihuan Liufbc51862018-02-27 23:21:14 -08001065
Bojun Panc8580822018-06-27 17:33:31 -07001066 mutex_lock(&ipa_gsb_ctx->iface_lock[hdl]);
1067 if (!ipa_gsb_ctx->iface[hdl]) {
1068 IPA_GSB_ERR("fail to find interface, hdl: %d\n", hdl);
1069 mutex_unlock(&ipa_gsb_ctx->iface_lock[hdl]);
1070 return -EFAULT;
1071 }
1072
Shihuan Liufbc51862018-02-27 23:21:14 -08001073 if (!ipa_gsb_ctx->iface[hdl]->is_connected) {
1074 IPA_GSB_ERR("iface is not connected\n");
Bojun Panc8580822018-06-27 17:33:31 -07001075 mutex_unlock(&ipa_gsb_ctx->iface_lock[hdl]);
Shihuan Liufbc51862018-02-27 23:21:14 -08001076 return -EFAULT;
1077 }
1078
1079 if (!ipa_gsb_ctx->iface[hdl]->is_resumed) {
Bojun Panffaf7202018-05-31 16:16:25 -07001080 IPA_GSB_DBG_LOW("iface was already suspended\n");
Bojun Panc8580822018-06-27 17:33:31 -07001081 mutex_unlock(&ipa_gsb_ctx->iface_lock[hdl]);
Shihuan Liufbc51862018-02-27 23:21:14 -08001082 return 0;
1083 }
1084
Mohammedc8a44de2018-10-30 19:13:29 +05301085 mutex_lock(&ipa_gsb_ctx->lock);
Shihuan Liufbc51862018-02-27 23:21:14 -08001086 if (ipa_gsb_ctx->num_resumed_iface == 1) {
1087 ret = ipa_stop_gsi_channel(
1088 ipa_gsb_ctx->cons_hdl);
1089 if (ret) {
1090 IPA_GSB_ERR(
1091 "fail to stop cons ep %d\n",
1092 ret);
Mohammedc8a44de2018-10-30 19:13:29 +05301093 mutex_unlock(&ipa_gsb_ctx->lock);
Bojun Panc8580822018-06-27 17:33:31 -07001094 mutex_unlock(&ipa_gsb_ctx->iface_lock[hdl]);
Shihuan Liufbc51862018-02-27 23:21:14 -08001095 return ret;
1096 }
1097
1098 ret = ipa_pm_deactivate_sync(ipa_gsb_ctx->pm_hdl);
1099 if (ret) {
1100 IPA_GSB_ERR("fail to deactivate ipa pm\n");
1101 ipa_start_gsi_channel(ipa_gsb_ctx->cons_hdl);
Mohammedc8a44de2018-10-30 19:13:29 +05301102 mutex_unlock(&ipa_gsb_ctx->lock);
Bojun Panc8580822018-06-27 17:33:31 -07001103 mutex_unlock(&ipa_gsb_ctx->iface_lock[hdl]);
Shihuan Liufbc51862018-02-27 23:21:14 -08001104 return ret;
1105 }
1106 }
1107
1108 ipa_gsb_ctx->iface[hdl]->is_resumed = false;
1109 ipa_gsb_ctx->num_resumed_iface--;
Bojun Panffaf7202018-05-31 16:16:25 -07001110 IPA_GSB_DBG_LOW("num resumed iface: %d\n",
Shihuan Liufbc51862018-02-27 23:21:14 -08001111 ipa_gsb_ctx->num_resumed_iface);
1112
Mohammedc8a44de2018-10-30 19:13:29 +05301113 mutex_unlock(&ipa_gsb_ctx->lock);
Bojun Panc8580822018-06-27 17:33:31 -07001114 mutex_unlock(&ipa_gsb_ctx->iface_lock[hdl]);
Shihuan Liufbc51862018-02-27 23:21:14 -08001115 return 0;
1116}
1117EXPORT_SYMBOL(ipa_bridge_suspend);
1118
1119int ipa_bridge_set_perf_profile(u32 hdl, u32 bandwidth)
1120{
1121 int ret;
1122
Bojun Panc8580822018-06-27 17:33:31 -07001123 if (!ipa_gsb_ctx) {
1124 IPA_GSB_ERR("ipa_gsb_ctx was not initialized\n");
1125 return -EFAULT;
1126 }
1127
1128 if (hdl >= MAX_SUPPORTED_IFACE) {
1129 IPA_GSB_ERR("invalid hdl: %d\n", hdl);
1130 return -EINVAL;
1131 }
1132
Shihuan Liufbc51862018-02-27 23:21:14 -08001133 IPA_GSB_DBG("client hdl: %d, BW: %d\n", hdl, bandwidth);
1134
Bojun Panc8580822018-06-27 17:33:31 -07001135 mutex_lock(&ipa_gsb_ctx->iface_lock[hdl]);
Shihuan Liufbc51862018-02-27 23:21:14 -08001136
1137 ret = ipa_pm_set_perf_profile(ipa_gsb_ctx->pm_hdl,
1138 bandwidth);
1139 if (ret)
1140 IPA_GSB_ERR("fail to set perf profile\n");
1141
Bojun Panc8580822018-06-27 17:33:31 -07001142 mutex_unlock(&ipa_gsb_ctx->iface_lock[hdl]);
Shihuan Liufbc51862018-02-27 23:21:14 -08001143 return ret;
1144}
1145EXPORT_SYMBOL(ipa_bridge_set_perf_profile);
1146
1147int ipa_bridge_tx_dp(u32 hdl, struct sk_buff *skb,
1148 struct ipa_tx_meta *metadata)
1149{
1150 struct ipa_gsb_mux_hdr *mux_hdr;
1151 struct sk_buff *skb2;
1152 int ret;
1153
Bojun Panffaf7202018-05-31 16:16:25 -07001154 IPA_GSB_DBG_LOW("client hdl: %d\n", hdl);
Shihuan Liufbc51862018-02-27 23:21:14 -08001155
Bojun Panc8580822018-06-27 17:33:31 -07001156 if (!ipa_gsb_ctx->iface[hdl]) {
1157 IPA_GSB_ERR("fail to find interface, hdl: %d\n", hdl);
1158 return -EFAULT;
1159 }
1160
Mohammed Javidaf75b0c2018-09-20 16:17:16 +05301161 if (unlikely(atomic_read(&ipa_gsb_ctx->disconnect_in_progress))) {
1162 IPA_GSB_ERR("ipa bridge disconnect_in_progress\n");
1163 return -EFAULT;
1164 }
1165
Shihuan Liufbc51862018-02-27 23:21:14 -08001166 /* make sure skb has enough headroom */
1167 if (unlikely(skb_headroom(skb) < sizeof(struct ipa_gsb_mux_hdr))) {
Bojun Panffaf7202018-05-31 16:16:25 -07001168 IPA_GSB_DBG_LOW("skb doesn't have enough headroom\n");
Shihuan Liufbc51862018-02-27 23:21:14 -08001169 skb2 = skb_copy_expand(skb, sizeof(struct ipa_gsb_mux_hdr),
1170 0, GFP_ATOMIC);
1171 if (!skb2) {
1172 dev_kfree_skb_any(skb);
1173 return -ENOMEM;
1174 }
1175 dev_kfree_skb_any(skb);
1176 skb = skb2;
1177 ipa_gsb_ctx->iface[hdl]->iface_stats.
1178 num_insufficient_headroom_packets++;
1179 }
1180
1181 /* add 4 byte header for mux */
1182 mux_hdr = (struct ipa_gsb_mux_hdr *)skb_push(skb,
1183 sizeof(struct ipa_gsb_mux_hdr));
1184 mux_hdr->iface_hdl = (u8)hdl;
1185 /* change to network order */
1186 *(u32 *)mux_hdr = htonl(*(u32 *)mux_hdr);
1187
1188 ret = ipa_tx_dp(IPA_CLIENT_ODU_PROD, skb, metadata);
1189 if (ret) {
1190 IPA_GSB_ERR("tx dp failed %d\n", ret);
1191 return -EFAULT;
1192 }
1193 ipa_gsb_ctx->iface[hdl]->iface_stats.num_ul_packets++;
1194
1195 return 0;
1196}
1197EXPORT_SYMBOL(ipa_bridge_tx_dp);
1198
1199MODULE_LICENSE("GPL v2");
1200MODULE_DESCRIPTION("ipa gsb driver");