blob: 029158e0a4fa2c23fbd53f8097c163d6759dbd1e [file] [log] [blame]
Shad Ansari2f7f9be2017-06-07 13:34:53 -07001/*
2<:copyright-BRCM:2016:DUAL/GPL:standard
3
4 Broadcom Proprietary and Confidential.(c) 2016 Broadcom
5 All Rights Reserved
6
7Unless you and Broadcom execute a separate written software license
8agreement governing use of this software, this software is licensed
9to you under the terms of the GNU General Public License version 2
10(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php,
11with the following added to such license:
12
13 As a special exception, the copyright holders of this software give
14 you permission to link this software with independent modules, and
15 to copy and distribute the resulting executable under terms of your
16 choice, provided that you also meet, for each linked independent
17 module, the terms and conditions of the license of that module.
18 An independent module is a module which is not derived from this
19 software. The special exception does not apply to any modifications
20 of the software.
21
22Not withstanding the above, under no circumstances may you combine
23this software in any way with any other Broadcom software provided
24under a license other than the GPL, without Broadcom's express prior
25written consent.
26
27:>
28 */
29
30#include <bcmos_system.h>
31#include <bcmtr_interface.h>
32#include <bcmtr_debug.h>
33
34#include "bcmtr_header.h"
35#include "bcmtr_internal.h"
36
37typedef struct
38{
39 bcmtr_conn *conn; /**< Connection dynamic info (allocated on connect) */
40 bcmtr_handler msg_handler[BCMOLT_GROUP_ID__NUM_OF][BCMTR_MAX_INSTANCES];
41 F_bcmtr_tx_overflow overflow_cb; /**< Callback to be called in case of tx drop because of queue overflow */
42} bcmtr_conn_info;
43
44static bcmtr_conn_info conn_info[BCMTR_MAX_OLTS]; /* Store connection info separately per OLT */
45
46static bcmos_errno _bcmtr_connect(bcmolt_devid device, bcmtr_conn **conn, bcmos_bool raw_mode);
47
48static bcmos_mutex conn_lock;
49
50/* Get existing connection. If none - setup new.
51 * If raw_mode is TRUE, the connection is intended for use by raw interface
52 * bcmtr_proxy_xx(). In this case RX task is not created
53 */
54static bcmos_errno _bcmtr_conn_get_any(bcmolt_devid device, bcmtr_conn **conn, bcmos_bool is_raw)
55{
56 bcmos_errno err;
57 if (device >= BCMTR_MAX_OLTS)
58 {
59 return BCM_ERR_RANGE;
60 }
61 *conn = conn_info[device].conn;
62 if (*conn)
63 {
64 return BCM_ERR_OK;
65 }
66 err = _bcmtr_connect(device, &conn_info[device].conn, is_raw);
67 *conn = conn_info[device].conn;
68 return err;
69}
70
71/* Get existing connection. If none - setup new */
72bcmos_errno _bcmtr_conn_get(bcmolt_devid device, bcmtr_conn **conn)
73{
74 return _bcmtr_conn_get_any(device, conn, BCMOS_FALSE);
75}
76
77/* Free reassemble block */
78static void _bcmtr_reass_block_free(bcmtr_reass **prb)
79{
80 bcmtr_reass *reass = *prb;
81 int i;
82
83 for (i=0; i<reass->num_fragments; i++)
84 {
85 bcmolt_buf_free(&reass->fragments[i]);
86 }
87 bcmos_free(reass);
88 *prb = NULL;
89}
90
91/* Free transport header. Called under transport lock */
92static void _bcmtr_tmsg_free(bcmtr_msg *tmsg, bcmtr_msg_list *cur_list)
93{
94 /* Remove from the list it is in, if any */
95 if (cur_list)
96 TAILQ_REMOVE(cur_list, tmsg, l);
97
98 bcmolt_buf_free(&tmsg->tx_buf);
99 bcmolt_buf_free(&tmsg->rx_buf);
100 if (tmsg->reass)
101 _bcmtr_reass_block_free(&tmsg->reass);
102 memset(tmsg, 0, BCMTR_HDR_CLEAR_SIZE);
103
104 /* Request-response or autonomous ? */
105 TAILQ_INSERT_TAIL(tmsg->free_list, tmsg, l);
106}
107
108/* Unpack message. *unpacked is set=NULL in case of error */
109static inline bcmos_errno _bcmtr_msg_unpack(bcmtr_conn *conn, bcmolt_buf *buf, bcmtr_hdr *hdr, uint32_t ts, bcmolt_msg **msg)
110{
111 int16_t err;
112 uint8_t *packed = buf->curr;
113 uint32_t packed_length = bcmolt_buf_get_remaining_size(buf);
114
115 /* Unpack */
116 BUG_ON(!buf->start);
117 err = bcmolt_msg_unpack(buf, msg);
118 if (err < 0)
119 {
120 BCMTR_CLD_CHECK_NOTIFY(conn->device, hdr, BCMTR_CLD_EV_RECV_DISCARD, ts, packed, packed_length, NULL);
121 ++conn->stat.unpack_errors;
122 return err;
123 }
124
125 BCMTR_CLD_CHECK_NOTIFY(conn->device, hdr, BCMTR_CLD_EV_RECV, ts, packed, packed_length, *msg);
126
127 return BCM_ERR_OK;
128}
129
130/* Transport IPC release callback */
131static void _bcmtr_ipc_msg_release(bcmos_msg *m)
132{
133 BCMOS_TRACE_ERR("We shouldn't be here!\n");
134}
135
136/* Transport IPC message handler.
137 * Called in context of the target module as part
138 * of dispatching message to the user task.
139 * It unpacks message, releases transport header and calls user callback
140 */
141static void _bcmtr_ipc_msg_handler(bcmos_module_id module_id, bcmos_msg *m)
142{
143 bcmos_errno err;
144
145 bcmtr_msg *tmsg = m->data;
146 bcmtr_conn *conn = tmsg->conn;
147 bcmolt_msg *msg = NULL;
148
149 /* Unpack */
150 err = _bcmtr_msg_unpack(conn, &tmsg->rx_buf, &tmsg->hdr, tmsg->timestamp, &msg);
151 if (err != BCM_ERR_OK)
152 {
153 BCMOS_TRACE_ERR(
154 "Unpack error for module %d. Error %s (%d)\n",
155 module_id,
156 bcmos_strerror(err),
157 err);
158 msg = NULL;
159 }
160
161 if (msg != NULL)
162 {
163 bcmtr_handler *h = &conn_info[conn->device].msg_handler[tmsg->hdr.msg_id][tmsg->hdr.instance];
164 msg->subch = tmsg->subch;
165 if (h->app_cb)
166 {
167 msg->corr_tag = tmsg->hdr.corr_tag;
168 h->app_cb(conn->device, msg);
169 }
170 else
171 {
172 bcmolt_msg_free(msg);
173 ++conn->stat.no_rx_handler;
174 }
175 }
176
177 /* Release transport header under conn lock */
178 bcmos_mutex_lock(&conn->mutex);
179 _bcmtr_tmsg_free(tmsg, NULL);
180 bcmos_mutex_unlock(&conn->mutex);
181}
182
183/* Init IPC header in transport message */
184static void _bcmtr_tmsg_ipc_init(bcmtr_msg *tmsg)
185{
186 tmsg->ipc_hdr.start = tmsg->ipc_hdr.data = tmsg;
187 tmsg->ipc_hdr.type = BCMOS_MSG_ID_INTERNAL_IPC;
188 tmsg->ipc_hdr.release = _bcmtr_ipc_msg_release;
189 tmsg->ipc_hdr.handler = _bcmtr_ipc_msg_handler;
190}
191
192/* Pre-allocate transport header array, put all blocks on free lists */
193static int _bcmtr_tmsg_list_alloc(bcmtr_conn *conn)
194{
195 int n_hdr, i;
196 bcmtr_msg *tmsg;
197
198 n_hdr = conn->cfg.max_requests + conn->cfg.max_autos;
199 conn->tmsg_array = bcmos_calloc(sizeof(bcmtr_msg) * n_hdr);
200 if (!conn->tmsg_array)
201 return BCM_ERR_NOMEM;
202
203 tmsg = conn->tmsg_array;
204 for (i=0; i<conn->cfg.max_requests; i++, tmsg++)
205 {
206 bcmos_errno rc;
207
208 TAILQ_INSERT_TAIL(&conn->free_req_list, tmsg, l);
209 tmsg->free_list = &conn->free_req_list;
210 rc = bcmos_sem_create(&tmsg->sem, 0, 0, NULL);
211 if (rc != BCM_ERR_OK)
212 return rc;
213 _bcmtr_tmsg_ipc_init(tmsg);
214 tmsg->conn = conn;
215 }
216 for (i=0; i<conn->cfg.max_autos; i++, tmsg++)
217 {
218 TAILQ_INSERT_TAIL(&conn->free_auto_list, tmsg, l);
219 tmsg->free_list = &conn->free_auto_list;
220 _bcmtr_tmsg_ipc_init(tmsg);
221 tmsg->conn = conn;
222 }
223 return BCM_ERR_OK;
224}
225
226/* Cleanup function - free transport headers */
227static void _bcmtr_tmsg_list_free(bcmtr_conn *conn)
228{
229 bcmtr_msg *tmsg, *tmp_tmsg;
230
231 if (!conn->tmsg_array)
232 return;
233
234 TAILQ_FOREACH_SAFE(tmsg, &conn->msg_list, l, tmp_tmsg)
235 {
236 bcmolt_msg *msg = tmsg->msg;
237 /* Release waiting task if request-response */
238 if (msg && tmsg->err == BCM_ERR_IN_PROGRESS)
239 {
240 msg->err = BCM_ERR_COMM_FAIL;
241 bcmos_sem_post(&tmsg->sem);
242 }
243 }
244 TAILQ_FOREACH_SAFE(tmsg, &conn->free_req_list, l, tmp_tmsg)
245 {
246 bcmos_sem_destroy(&tmsg->sem);
247 }
248 bcmos_free(conn->tmsg_array);
249}
250
251/* Allocate transport header from the given free list.
252 * Must be called under lock
253 */
254static inline bcmtr_msg *_bcmtr_msg_get_free(bcmtr_msg_list *free_list)
255{
256 bcmtr_msg *tmsg = TAILQ_FIRST(free_list);
257 if (tmsg)
258 TAILQ_REMOVE(free_list, tmsg, l);
259 return tmsg;
260}
261
262/* Find message by correlation tag
263 * Called under lock
264 */
265static bcmtr_msg *_bcmtr_msg_get_by_corr_tag(const bcmtr_conn *conn, const bcmtr_hdr *hdr)
266{
267 bcmtr_msg *tmsg;
268
269 TAILQ_FOREACH(tmsg, &conn->msg_list, l)
270 {
271 if (tmsg->hdr.corr_tag==hdr->corr_tag && tmsg->hdr.msg_id==hdr->msg_id && tmsg->err == BCM_ERR_IN_PROGRESS)
272 break;
273 }
274 return tmsg;
275}
276
277/* Message reassembler. Returns TRUE if message reassembling is completed */
278static bcmos_bool _bcmtr_reassemble(bcmtr_conn *conn, bcmtr_msg *tmsg, bcmolt_buf *buf)
279{
280 bcmtr_hdr *hdr = &tmsg->hdr;
281 uint16_t frag_num = hdr->frag_number;
282 bcmos_bool done = BCMOS_FALSE;
283 bcmos_bool is_last;
284
285 is_last = !hdr->more_fragments;
286
287 /* Single-buffer message ? */
288 if (is_last && !frag_num)
289 {
290 tmsg->rx_buf = *buf;
291 tmsg->err = BCM_ERR_OK;
292 buf->start = NULL;
293 return BCMOS_TRUE;
294 }
295
296 /*
297 * Multi-part message
298 */
299
300 /* Discard if invalid fragment number or duplicate */
301 if (frag_num >= conn->cfg.max_fragments ||
302 (tmsg->reass && tmsg->reass->fragments[frag_num].start) )
303 {
304 bcmolt_buf_free(buf);
305 /* If last out-of range fragment was received report it.
306 * We want to avoid request retransmission in this case */
307 if (frag_num >= conn->cfg.max_fragments)
308 {
309 tmsg->err = BCM_ERR_TOO_MANY_FRAGS;
310 return is_last;
311 }
312 ++conn->stat.frag_invalid;
313 return BCMOS_FALSE;
314 }
315
316 /* Allocate reassembly buffer if not done yet and store fragment */
317 if (!tmsg->reass)
318 {
319 tmsg->reass = bcmos_calloc(sizeof(bcmtr_reass) + conn->cfg.max_fragments * sizeof(bcmolt_buf));
320 if (!tmsg->reass)
321 {
322 tmsg->err = BCM_ERR_NOMEM;
323 ++conn->stat.msg_no_mem;
324 bcmolt_buf_free(buf);
325 return BCMOS_FALSE;
326 }
327 tmsg->reass->fragments = (bcmolt_buf *)((long)tmsg->reass + sizeof(bcmtr_reass));
328 }
329 tmsg->reass->total_size += bcmolt_buf_get_remaining_size(buf);
330 tmsg->reass->fragments[frag_num] = *buf;
331 buf->start = NULL;
332 tmsg->reass->num_fragments++;
333 if (is_last)
334 tmsg->reass->max_fragment = frag_num;
335 done = (tmsg->reass->max_fragment && (tmsg->reass->num_fragments > tmsg->reass->max_fragment));
336 ++conn->stat.frag_received;
337
338 /* Reassemble if done */
339 if (done)
340 {
341 /* Allocate big flat buffer */
342 if (bcmolt_buf_alloc(&tmsg->rx_buf, tmsg->reass->total_size, BCMTR_BUF_ENDIAN) == BCM_ERR_OK)
343 {
344 int i;
345 uint8_t *body = tmsg->rx_buf.start;
346
347 for (i=0; i<tmsg->reass->num_fragments; i++)
348 {
349 uint32_t frag_size = bcmolt_buf_get_remaining_size(&tmsg->reass->fragments[i]);
350 BUG_ON(!tmsg->reass->fragments[i].curr);
351 memcpy(body, tmsg->reass->fragments[i].curr, frag_size);
352 body += frag_size;
353 }
354 tmsg->err = BCM_ERR_OK;
355 }
356 else
357 {
358 /* Reassembly buffer allocation failed */
359 tmsg->err = BCM_ERR_NOMEM;
360 }
361 }
362 else
363 {
364 /* More fragments expected. Update timestamp to prolong timing out */
365 tmsg->timestamp = bcmos_timestamp();
366 }
367
368 return done;
369}
370
371/* Notify application that message is ready */
372static inline void _bcmtr_notify_ready(bcmtr_conn *conn, bcmtr_msg *tmsg)
373{
374 bcmos_sem_post(&tmsg->sem);
375}
376
377/* Notify rx request/response message
378 * called under connection lock
379 */
380static inline void _bcmtr_notify_rx_response(bcmtr_conn *conn, bcmtr_msg *tmsg)
381{
382 ++conn->stat.msg_resp_received;
383
384 /* Now unlock and notify application. Autonomous handler is only called if message is OK.
385 The lock has been taken in _bcmtr_rx_packet */
386 bcmos_mutex_unlock(&conn->mutex);
387
388 /* Release waiting application. It will take care of unpacking */
389 _bcmtr_notify_ready(conn, tmsg);
390}
391
392/* Notify rx autonomous message
393 * called under connection lock
394 */
395static inline void _bcmtr_notify_rx_req_auto(bcmtr_conn *conn, bcmtr_msg *tmsg)
396{
397 bcmolt_buf rx_buf;
398 bcmolt_msg *msg = NULL;
399 bcmolt_group_id msg_id = tmsg->hdr.msg_id;
400 bcmtr_handler *h;
401 uint16_t corr_tag;
402 bcmtr_hdr hdr = tmsg->hdr;
403 uint32_t ts = tmsg->timestamp;
404 bcmolt_subchannel subch = tmsg->subch;
405 bcmos_errno err;
406
407 if (msg_id >= BCMOLT_GROUP_ID__NUM_OF)
408 {
409 BCMOS_TRACE_ERR("Unexpected msg group id: %u\n", tmsg->hdr.msg_id);
410 _bcmtr_tmsg_free(tmsg, NULL);
411 bcmos_mutex_unlock(&conn->mutex);
412 return;
413 }
414 if (tmsg->hdr.instance >= BCMTR_MAX_INSTANCES)
415 {
416 BCMOS_TRACE_ERR("Unexpected instance id: %u\n", tmsg->hdr.instance);
417 _bcmtr_tmsg_free(tmsg, NULL);
418 bcmos_mutex_unlock(&conn->mutex);
419 return;
420 }
421 h = &conn_info[conn->device].msg_handler[tmsg->hdr.msg_id][tmsg->hdr.instance];
422 BUG_ON(!h->app_cb);
423 ++conn->stat.msg_req_auto_received;
424
425 /* If dispatch is required - do it.
426 * The message will be unpacked in the context of the receiver
427 */
428 if ((h->flags & BCMOLT_AUTO_FLAGS_DISPATCH))
429 {
430 err = bcmos_msg_send_to_module(h->module, &tmsg->ipc_hdr, 0);
431 if (err)
432 {
433 BCMOS_TRACE_ERR("Can't deliver message to module %d. Error %s(%d)\n",
434 h->module, bcmos_strerror(err), err);
435 _bcmtr_tmsg_free(tmsg, NULL);
436 }
437 bcmos_mutex_unlock(&conn->mutex);
438 return;
439 }
440
441 /* No dispatch. Unpacking in the context of rx thread */
442 corr_tag = tmsg->hdr.corr_tag;
443 /* Make sure that rx_buf is not released by the following _bcmtr_msg_free.
444 * It is needed for unpack and will be released separately later. */
445 rx_buf = tmsg->rx_buf;
446 tmsg->rx_buf.start = NULL;
447 _bcmtr_tmsg_free(tmsg, NULL);
448
449 /* Release connection lock taken in _bcmtr_rx_packet */
450 bcmos_mutex_unlock(&conn->mutex);
451
452 /* Unpack and deliver */
453 _bcmtr_msg_unpack(conn, &rx_buf, &hdr, ts, &msg);
454
455 bcmolt_buf_free(&rx_buf);
456
457 if (msg)
458 {
459 msg->corr_tag = corr_tag;
460 msg->subch = subch;
461 h->app_cb(conn->device, msg);
462 }
463}
464
465/* Handle rx data. Returns number of messages that was identified and reassembled. Can be 0 or 1 */
466static int _bcmtr_rx_packet(bcmtr_conn *conn, bcmolt_subchannel subch, bcmolt_buf *buf)
467{
468 bcmtr_hdr hdr;
469 bcmtr_msg *tmsg;
470 bcmos_bool msg_done;
471 bcmos_bool is_response;
472
473 /* Transport lock. This lock is needed to
474 * - allocate/release transport header
475 * - update statistics
476 */
477 bcmos_mutex_lock(&conn->mutex);
478
479 /* If some data was received - handle it */
480 if (buf->len < BCMTR_HDR_SIZE)
481 {
482 /* Message is too short */
483 ++conn->stat.msg_comm_err;
484 goto rx_free_buf_and_error_exit;
485 }
486
487 if (NULL == buf->curr)
488 {
489 BCMOS_TRACE_ERR("Invalid buffer received!\n");
490 goto rx_done;
491 }
492
493 bcmtr_header_unpack(buf->curr, &hdr);
494 bcmolt_buf_skip(buf, BCMTR_HDR_SIZE);
495 is_response = (hdr.dir == BCMOLT_MSG_DIR_RESPONSE);
496
497 /* Find transport header. If not found - allocate new for autonomous message */
498 tmsg = _bcmtr_msg_get_by_corr_tag(conn, &hdr);
499 if (!tmsg)
500 {
501 if (!is_response)
502 {
503 /* Allocate new transport block */
504 tmsg = _bcmtr_msg_get_free(&conn->free_auto_list);
505 if (!tmsg)
506 {
507 ++conn->stat.msg_too_many_auto;
508 goto rx_free_buf_and_error_exit;
509 }
510 tmsg->err = BCM_ERR_IN_PROGRESS;
511 TAILQ_INSERT_TAIL(&conn->msg_list, tmsg, l);
512 }
513 else
514 {
515 /* Response, but no request - discard */
516 ++conn->stat.msg_no_req;
517 BCMTR_CLD_CHECK_NOTIFY(conn->device, &hdr, BCMTR_CLD_EV_RECV_DISCARD,
518 bcmos_timestamp(), buf->curr, bcmolt_buf_get_remaining_size(buf), NULL);
519 goto rx_free_buf_and_error_exit;
520 }
521 }
522
523 /* Reassemble. "buf" should not be used following this call */
524 tmsg->hdr = hdr;
525 tmsg->subch = subch;
526 msg_done = _bcmtr_reassemble(conn, tmsg, buf);
527
528 /* If expects more parts - nothing more to do here */
529 if (!msg_done)
530 goto rx_done;
531
532 if (tmsg->err && !is_response)
533 {
534 _bcmtr_tmsg_free(tmsg, &conn->msg_list);
535 goto rx_done;
536 }
537
538 /* Done with the message. Get it out of pending message queue to avoid race condition
539 * when timeout happens while the message is in flight to the destination task.
540 */
541 TAILQ_REMOVE(&conn->msg_list, tmsg, l);
542
543 /* Notify rx. conn->mutex is still taken. It will be released
544 inside _bcmtr_notify_rx_response(), _bcmtr_notify_rx_req_auto() */
545 tmsg->timestamp = bcmos_timestamp();
546 if (is_response)
547 {
548 _bcmtr_notify_rx_response(conn, tmsg);
549 }
550 else
551 {
552 _bcmtr_notify_rx_req_auto(conn, tmsg);
553 }
554
555 return 1;
556
557 /* Error return */
558rx_free_buf_and_error_exit:
559 bcmos_mutex_unlock(&conn->mutex);
560 bcmolt_buf_free(buf);
561 return 0;
562
563 /* return without a buffer */
564rx_done:
565 bcmos_mutex_unlock(&conn->mutex);
566 return 0;
567}
568
569/*
570 * Low-level fragment and send function.
571 * It allocates a series of buffers up to MAX_MTU size, copies original data into them and sends.
572 * The original buffer stays intact - in case it should be retransmitted
573 */
574static bcmos_errno _bcmtr_fragment_and_send(bcmtr_conn *conn, bcmtr_msg *tmsg, bcmtr_send_flags flags)
575{
576 uint32_t frag_number = 0;
577 bcmos_errno err = BCM_ERR_OK;
578 uint32_t data_offset = conn->cfg.plugin_cfg.headroom + BCMTR_HDR_SIZE;
579 uint32_t data_len = bcmolt_buf_get_used(&tmsg->tx_buf) - data_offset;
580 uint8_t *data = tmsg->tx_buf.start + data_offset;
581
582 do
583 {
584 uint32_t send_len = data_len + BCMTR_HDR_SIZE;
585 bcmolt_buf frag;
586
587 if (send_len > conn->cfg.max_mtu)
588 {
589 send_len = conn->cfg.max_mtu;
590 tmsg->hdr.more_fragments = BCMOS_TRUE;
591 }
592 else
593 {
594 tmsg->hdr.more_fragments = BCMOS_FALSE;
595 }
596
597 err = bcmolt_buf_alloc(&frag, send_len + conn->cfg.plugin_cfg.headroom, BCMTR_BUF_ENDIAN);
598 if (err)
599 break;
600
601 tmsg->hdr.frag_number = frag_number++;
602
603 /* Pack correlation tag, command and length */
604 bcmtr_header_pack(&tmsg->hdr, frag.start + conn->cfg.plugin_cfg.headroom);
605 bcmolt_buf_skip(&frag, data_offset);
606 if (bcmolt_buf_write(&frag, data, send_len - BCMTR_HDR_SIZE))
607 {
608 /* Send using customer-provided driver */
609 err = conn->driver.send(conn->drv_priv, tmsg->msg->subch, &frag, flags);
610 }
611 else
612 {
613 err = BCM_ERR_OVERFLOW;
614 }
615 bcmolt_buf_free(&frag);
616 if (err)
617 {
618 break;
619 }
620
621 data_len -= (send_len - BCMTR_HDR_SIZE);
622 data += (send_len - BCMTR_HDR_SIZE);
623
624 } while (data_len);
625
626 return err;
627}
628
629/* Check for time-outs. returns number of messages timed out */
630static int _bcmtr_check_timeout(bcmtr_conn *conn)
631{
632 bcmtr_msg *tmsg, *tmp;
633 uint32_t now;
634 int nmsg = 0;
635 bcmos_errno err;
636
637 /* Transport lock */
638 bcmos_mutex_lock(&conn->mutex);
639
640 now = bcmos_timestamp();
641 TAILQ_FOREACH_SAFE(tmsg, &conn->msg_list, l, tmp)
642 {
643 bcmolt_msg *msg = tmsg->msg;
644
645 if (now - tmsg->timestamp <= conn->cfg.msg_timeout)
646 continue;
647
648 /* Retransmit ? */
649 if (msg && tmsg->tx_count <= conn->cfg.max_retries)
650 {
651 tmsg->timestamp = bcmos_timestamp();
652 BCMTR_CLD_CHECK_NOTIFY(
653 conn->device,
654 &tmsg->hdr,
655 BCMTR_CLD_EV_RESEND,
656 tmsg->timestamp,
657 tmsg->tx_buf.start + conn->cfg.plugin_cfg.headroom + BCMTR_HDR_SIZE,
658 tmsg->tx_buf.len - (conn->cfg.plugin_cfg.headroom + BCMTR_HDR_SIZE),
659 msg);
660
661 /* Fragment and send or send directly, depending on message length */
662 if (bcmolt_buf_get_used(&tmsg->tx_buf) > conn->cfg.max_mtu)
663 {
664 err = _bcmtr_fragment_and_send(conn, tmsg, BCMTR_SEND_FLAGS_PRI_NORMAL);
665 }
666 else
667 {
668 err = conn->driver.send(conn->drv_priv, msg->subch, &tmsg->tx_buf, BCMTR_SEND_FLAGS_PRI_NORMAL);
669 }
670 if (err)
671 {
672 ++conn->stat.msg_comm_err;
673 }
674 ++tmsg->tx_count;
675 continue;
676 }
677
678 /* Giving up */
679 /* Release waiting task if request-response - unless it has already been done */
680 if (msg)
681 {
682 if (tmsg->err == BCM_ERR_IN_PROGRESS)
683 {
684 tmsg->err = BCM_ERR_TIMEOUT;
685 }
686 BCMTR_CLD_CHECK_NOTIFY(
687 conn->device,
688 &tmsg->hdr,
689 BCMTR_CLD_EV_TIMEOUT,
690 bcmos_timestamp(),
691 tmsg->tx_buf.start + conn->cfg.plugin_cfg.headroom + BCMTR_HDR_SIZE,
692 tmsg->tx_buf.len - (conn->cfg.plugin_cfg.headroom + BCMTR_HDR_SIZE),
693 msg);
694 TAILQ_REMOVE(&conn->msg_list, tmsg, l);
695 _bcmtr_notify_ready(conn, tmsg);
696 ++conn->stat.msg_req_timeout;
697 }
698 else
699 {
700 _bcmtr_tmsg_free(tmsg, &conn->msg_list);
701 ++conn->stat.msg_reass_timeout;
702 }
703 ++nmsg;
704 }
705 conn->last_timeout_check = bcmos_timestamp();
706
707 /* Release transport lock */
708 bcmos_mutex_unlock(&conn->mutex);
709
710 return nmsg;
711}
712
713/* Check for receive and timeouts */
714static int _bcmtr_rx_poll(bcmtr_conn *conn, int *pnmsg)
715{
716 bcmolt_buf buf;
717 int nmsg = 0, nmsg_prev;
718 bcmos_errno rc = BCM_ERR_OK;
719 bcmolt_subchannel subch;
720
721 do
722 {
723 nmsg_prev = nmsg;
724
725 /* Plugin driver's recv - get pending rx packet is any.
726 * The function is not allowed to block for more then BCMTR_MSG_TIMEOUT ms
727 */
728 rc = conn->driver.recv(conn->drv_priv, &subch, &buf);
729 if (rc != BCM_ERR_OK)
730 {
731 if (rc == BCM_ERR_NOMEM)
732 {
733 ++conn->stat.msg_no_mem;
734 }
735 }
736 else
737 {
738 nmsg += _bcmtr_rx_packet(conn, subch, &buf);
739 }
740
741 /* Check for timeouts if any */
742 if (bcmos_timestamp() - conn->last_timeout_check > conn->timeout_check_period)
743 {
744 /* Check requests waiting for acknowledge and multy-part messages for timeout.
745 * Timed-out requests are retransmitted.
746 */
747 nmsg += _bcmtr_check_timeout(conn);
748 }
749 } while(nmsg_prev != nmsg);
750
751 *pnmsg = nmsg;
752
753 return rc;
754}
755
756/* Rx thread handler */
757static int _bcmtr_rx_thread_handler(long arg)
758{
759 bcmtr_conn *conn = (bcmtr_conn *)arg;
760 int nmsgs;
761 int rc;
762
763 while(!conn->kill_request)
764 {
765 rc = _bcmtr_rx_poll(conn, &nmsgs);
766 if (rc == BCM_ERR_COMM_FAIL)
767 bcmos_usleep(1000);
768 }
769 conn->kill_done = 1;
770
771 return 0;
772}
773
774/*
775 * Internal transport interface
776 */
777
778
779/** Default message handler - discard */
780static void _bcmtr_dft_msg_handler(bcmolt_devid olt, bcmolt_msg *msg)
781{
782 bcmtr_conn *conn = conn_info[olt].conn;
783
784 /* ToDo: log */
785
786 if (conn)
787 ++conn->stat.no_rx_handler;
788
789 bcmolt_msg_free(msg);
790}
791
792static bcmos_errno _bcmtr_create_rx_thread(bcmtr_conn *conn, bcmos_bool raw_mode)
793{
794 bcmos_errno err = BCM_ERR_OK;
795
796 if (conn->cfg.rx_thread_priority >= 0 && !raw_mode)
797 {
798 bcmos_task_parm parm = {
799 .priority = conn->cfg.rx_thread_priority,
800 .stack_size = BCMTR_RX_THREAD_STACK,
801 .handler = _bcmtr_rx_thread_handler,
802 .name = conn->name,
803 .data = (long)conn
804 };
805 conn->kill_request = BCMOS_FALSE;
806 conn->kill_done = BCMOS_FALSE;
807 err = bcmos_task_create(&conn->rx_thread, &parm);
808 if (err == BCM_ERR_OK)
809 conn->rx_thread_created = BCMOS_TRUE;
810 }
811
812 return err;
813}
814
815static void _bcmtr_destroy_rx_thread(bcmtr_conn *conn)
816{
817 /* Kill rx thread if any */
818 if (conn->rx_thread_created)
819 {
820 conn->kill_request = 1;
821 while(!conn->kill_done)
822 bcmos_usleep(1000);
823 bcmos_task_destroy(&conn->rx_thread);
824 }
825}
826
827static bcmos_errno _bcmtr_connect(bcmolt_devid device, bcmtr_conn **pconn, bcmos_bool raw_mode)
828{
829 bcmtr_conn *conn;
830 bcmos_errno err = BCM_ERR_OK;
831
832 /* Init OS abstraction - just in case */
833 err = bcmos_init();
834 if (err != BCM_ERR_OK && err != BCM_ERR_ALREADY)
835 {
836 return err;
837 }
838
839 bcmos_mutex_lock(&conn_lock);
840
841 /* Allocate */
842 conn = bcmos_calloc(sizeof(*conn));
843 if (!conn)
844 {
845 bcmos_mutex_unlock(&conn_lock);
846 return BCM_ERR_NOMEM;
847 }
848
849 /* Get configuration */
850 err = bcmtr_cfg_get(device, &conn->cfg, &conn->driver);
851 if (err)
852 {
853 bcmos_mutex_unlock(&conn_lock);
854 bcmos_free(conn);
855 return err;
856 }
857
858 snprintf(conn->name, sizeof(conn->name), "tr_rx%u", device);
859 TAILQ_INIT(&conn->free_req_list);
860 TAILQ_INIT(&conn->free_auto_list);
861 TAILQ_INIT(&conn->msg_list);
862 bcmos_mutex_create(&conn->mutex, 0, NULL);
863
864 /* Convert timeouts to us */
865 conn->cfg.msg_timeout *= 1000;
866 conn->cfg.msg_ready_timeout *= 1000;
867 conn->cfg.msg_wait_timeout *= 1000;
868
869 /* Set defaults */
870 conn->timeout_check_period = conn->cfg.msg_wait_timeout;
871 conn->last_timeout_check = bcmos_timestamp();
872
873 /* Allocate and initialize transport blocks and put onto free request and autonomous lists */
874 err = _bcmtr_tmsg_list_alloc(conn);
875
876 /* Open/connect on driver level */
877 err = err ? err : conn->driver.open(device, &conn->cfg.plugin_cfg, &conn->drv_priv);
878 if (err)
879 {
880 bcmos_mutex_destroy(&conn->mutex);
881 goto cleanup;
882 }
883
884 conn->connected = BCMOS_TRUE;
885
886 /* Create rx thread if necessary */
887 err = _bcmtr_create_rx_thread(conn, raw_mode);
888 if (err)
889 {
890 conn->driver.close(conn->drv_priv);
891 bcmos_mutex_destroy(&conn->mutex);
892 goto cleanup;
893 }
894 conn->device = device;
895
896 *pconn = conn;
897 bcmos_mutex_unlock(&conn_lock);
898
899 return BCM_ERR_OK;
900
901cleanup:
902 _bcmtr_tmsg_list_free(conn);
903 bcmos_free(conn);
904 bcmos_mutex_unlock(&conn_lock);
905 return err;
906}
907
908/** Query whether or not the device is currently connected */
909bcmos_errno bcmtr_is_connected(bcmolt_devid device, bcmos_bool *is_connected)
910{
911 bcmtr_conn *conn;
912 if (device >= BCMTR_MAX_OLTS)
913 {
914 return BCM_ERR_RANGE;
915 }
916 conn = conn_info[device].conn;
917 *is_connected = (conn != NULL && conn->connected);
918 return BCM_ERR_OK;
919}
920
921/** Open transport channel */
922bcmos_errno bcmtr_connect(bcmolt_devid device)
923{
924 bcmtr_conn *conn;
925 return _bcmtr_conn_get(device, &conn);
926}
927
928/** Close transport channel */
929bcmos_errno bcmtr_disconnect(bcmolt_devid device)
930{
931 bcmtr_conn *conn;
932
933 if (device >= BCMTR_MAX_OLTS)
934 {
935 return BCM_ERR_RANGE;
936 }
937 bcmos_mutex_lock(&conn_lock);
938 conn = conn_info[device].conn;
939 if (!conn)
940 {
941 bcmos_mutex_unlock(&conn_lock);
942 return BCM_ERR_NOT_CONNECTED;
943 }
944 conn_info[device].conn = NULL;
945
946 /* Kill rx thread if any */
947 _bcmtr_destroy_rx_thread(conn);
948
949 bcmos_mutex_lock(&conn->mutex);
950 /* Close connection */
951 if (conn->driver.close)
952 {
953 conn->driver.close(conn->drv_priv);
954 }
955
956 /* Release all pending messages */
957 bcmos_usleep(100000);
958 _bcmtr_tmsg_list_free(conn);
959
960 bcmos_mutex_unlock(&conn->mutex);
961 bcmos_mutex_destroy(&conn->mutex);
962 bcmos_free(conn);
963
964 bcmos_mutex_unlock(&conn_lock);
965
966 return BCM_ERR_OK;
967}
968
969/* Low-level disconnect that breaks "physical" connection, but doesn't destroy connection structure and registrations */
970bcmos_errno bcmtr_driver_disconnect(bcmolt_devid device)
971{
972 bcmtr_conn *conn;
973 bcmos_errno err = BCM_ERR_OK;
974
975 if (device >= BCMTR_MAX_OLTS)
976 {
977 return BCM_ERR_RANGE;
978 }
979
980 bcmos_mutex_lock(&conn_lock);
981
982 conn = conn_info[device].conn;
983 if (conn == NULL || !conn->connected)
984 {
985 bcmos_mutex_unlock(&conn_lock);
986 return BCM_ERR_NOT_CONNECTED;
987 }
988
989 _bcmtr_destroy_rx_thread(conn);
990
991 bcmos_mutex_lock(&conn->mutex);
992
993 /* Close driver connection */
994 if (conn->driver.close != NULL)
995 {
996 err = conn->driver.close(conn->drv_priv);
997 if (err != BCM_ERR_OK)
998 {
999 BCMOS_TRACE_ERR("Failed to close transport driver: %s (%d)\n", bcmos_strerror(err), err);
1000 }
1001 }
1002
1003 conn->connected = BCMOS_FALSE;
1004
1005 bcmos_mutex_unlock(&conn->mutex);
1006
1007 bcmos_mutex_unlock(&conn_lock);
1008
1009 return err;
1010}
1011
1012/* Repair/reconnect the driver-level connection for an already-connected device */
1013bcmos_errno bcmtr_driver_reconnect(bcmolt_devid device)
1014{
1015 bcmtr_conn *conn;
1016 bcmos_errno err;
1017
1018 if (device >= BCMTR_MAX_OLTS)
1019 {
1020 return BCM_ERR_RANGE;
1021 }
1022 bcmos_mutex_lock(&conn_lock);
1023 conn = conn_info[device].conn;
1024 if (conn == NULL)
1025 {
1026 bcmos_mutex_unlock(&conn_lock);
1027 return BCM_ERR_NOT_CONNECTED;
1028 }
1029
1030 if (conn->connected)
1031 {
1032 bcmtr_driver_disconnect(device);
1033 }
1034 bcmos_mutex_lock(&conn->mutex);
1035
1036 /* Re-open driver connection */
1037 err = conn->driver.open(device, &conn->cfg.plugin_cfg, &conn->drv_priv);
1038 if (err != BCM_ERR_OK)
1039 {
1040 BCMOS_TRACE_ERR("Failed to re-open transport driver: %s (%d)\n", bcmos_strerror(err), err);
1041 }
1042
1043 err = _bcmtr_create_rx_thread(conn, BCMOS_FALSE);
1044 if (err != BCM_ERR_OK)
1045 {
1046 BCMOS_TRACE_ERR("Failed to create RX transport task: %s (%d)\n", bcmos_strerror(err), err);
1047 conn->driver.close(conn->drv_priv);
1048 }
1049 conn->connected = BCMOS_TRUE;
1050
1051 bcmos_mutex_unlock(&conn->mutex);
1052
1053 bcmos_mutex_unlock(&conn_lock);
1054
1055 return err;
1056}
1057
1058/* Register for notification that transmit failed because tx_queue was full */
1059bcmos_errno bcmtr_tx_overflow_cb_register(bcmolt_devid device, F_bcmtr_tx_overflow cb)
1060{
1061 if (device >= BCMTR_MAX_OLTS)
1062 {
1063 return BCM_ERR_RANGE;
1064 }
1065
1066 conn_info[device].overflow_cb = cb;
1067 return BCM_ERR_OK;
1068}
1069
1070/* Send message.
1071 * Internal function. Called under connection lock
1072 */
1073static bcmos_errno _bcmtr_send(bcmtr_conn *conn, bcmolt_msg *msg, bcmolt_buf *tx_buf, bcmtr_send_flags flags, bcmtr_msg **ptmsg)
1074{
1075 bcmtr_msg *tmsg;
1076 bcmos_errno err;
1077
1078 if (!conn->connected)
1079 {
1080 ++conn->stat.not_connected;
1081 return BCM_ERR_NOT_CONNECTED;
1082 }
1083
1084 /* Allocate message transport header */
1085 tmsg = _bcmtr_msg_get_free(&conn->free_req_list);
1086 if (!tmsg)
1087 {
1088 ++conn->stat.msg_no_mem;
1089 return BCM_ERR_TOO_MANY_REQS;
1090 }
1091 tmsg->msg = msg;
1092
1093 /* Fill transport header */
1094 err = bcmtr_header_fill(tmsg->msg, &tmsg->hdr);
1095 if (err)
1096 return err;
1097
1098 tmsg->err = BCM_ERR_IN_PROGRESS;
1099 tmsg->tx_count = 1;
1100
1101 /* Save transmit buffer. It will be released together with transport header */
1102 tmsg->tx_buf = *tx_buf;
1103
1104 tmsg->timestamp = bcmos_timestamp();
1105
1106 if (bcmolt_buf_get_used(tx_buf) > conn->cfg.max_mtu)
1107 {
1108 err = _bcmtr_fragment_and_send(conn, tmsg, flags);
1109 }
1110 else
1111 {
1112 /* Pack correlation tag, command and length */
1113 bcmtr_header_pack(&tmsg->hdr, tmsg->tx_buf.start + conn->cfg.plugin_cfg.headroom);
1114
1115 /* Send using customer-provided driver */
1116 err = conn->driver.send(conn->drv_priv, msg->subch, &tmsg->tx_buf, flags);
1117 }
1118 BCMTR_CLD_CHECK_NOTIFY(
1119 conn->device,
1120 &tmsg->hdr,
1121 BCMTR_CLD_EV_SEND,
1122 tmsg->timestamp,
1123 tmsg->tx_buf.start + conn->cfg.plugin_cfg.headroom + BCMTR_HDR_SIZE,
1124 tmsg->tx_buf.len - (conn->cfg.plugin_cfg.headroom + BCMTR_HDR_SIZE),
1125 msg);
1126 if (err != BCM_ERR_OK)
1127 {
1128 ++conn->stat.msg_comm_err;
1129 goto cleanup;
1130 }
1131
1132 tx_buf->start = NULL; /* Ownership passed to tmsg */
1133 ++conn->stat.msg_sent;
1134
1135 if (ptmsg)
1136 {
1137 *ptmsg = tmsg;
1138 }
1139 else
1140 {
1141 _bcmtr_tmsg_free(tmsg, NULL);
1142 }
1143
1144 return BCM_ERR_OK;
1145
1146 /* error */
1147cleanup:
1148 tmsg->tx_buf.start = NULL; /* prevent tx buffer de-allocation */
1149 _bcmtr_tmsg_free(tmsg, NULL);
1150 return err;
1151}
1152
1153
1154/* Allocate tx buffer and pack */
1155static bcmos_errno _bcmtr_pack(const bcmtr_conn *conn, bcmolt_msg *msg, bcmolt_buf *buf)
1156{
1157 int32_t len = bcmolt_msg_get_packed_length(msg);
1158 uint32_t headroom = conn->cfg.plugin_cfg.headroom;
1159 bcmos_errno err;
1160
1161 if (len < 0)
1162 return (bcmos_errno)len;
1163
1164 /* Reallocate if too big */
1165 len += BCMTR_HDR_SIZE + headroom;
1166 if (buf->start)
1167 {
1168 if (buf->len < len)
1169 {
1170 /* ToDo: reallocate */
1171 return BCM_ERR_OVERFLOW;
1172 }
1173 else
1174 {
1175 bcmolt_buf_init(buf, len, buf->start, BCMTR_BUF_ENDIAN);
1176 }
1177 }
1178 else
1179 {
1180 err = bcmolt_buf_alloc(buf, len, BCMTR_BUF_ENDIAN);
1181 if (err)
1182 {
1183 return err;
1184 }
1185 }
1186
1187 /* Reserve room for header */
1188 buf->curr = buf->start + BCMTR_HDR_SIZE + headroom;
1189
1190 /* Pack */
1191 err = bcmolt_msg_pack(msg, buf);
1192
1193 return err;
1194}
1195
1196/*
1197 * External message interface
1198 */
1199
1200/* Send message. Don't expect response. */
1201bcmos_errno bcmtr_send(bcmolt_devid device, bcmolt_msg *msg, bcmtr_send_flags flags)
1202{
1203 bcmtr_conn *conn;
1204 bcmos_errno err;
1205 bcmolt_buf tx_buf = {};
1206
1207 err = _bcmtr_conn_get(device, &conn);
1208 if (err)
1209 return err;
1210
1211 /* Allocate transport buffer and pack */
1212 err = _bcmtr_pack(conn, msg, &tx_buf);
1213 if (err)
1214 {
1215 bcmolt_buf_free(&tx_buf);
1216 return err;
1217 }
1218
1219 bcmos_mutex_lock(&conn->mutex);
1220 err = _bcmtr_send(conn, msg, &tx_buf, flags, NULL);
1221 bcmos_mutex_unlock(&conn->mutex);
1222 if (err)
1223 {
1224 bcmolt_buf_free(&tx_buf);
1225 if (err == BCM_ERR_QUEUE_FULL && conn_info[device].overflow_cb)
1226 conn_info[device].overflow_cb(conn->device, flags);
1227 }
1228
1229 return err;
1230}
1231
1232static bcmos_errno bcmtr_call_err(bcmolt_msg *msg, bcmos_errno err, const char *err_text)
1233{
1234 msg->dir = BCMOLT_MSG_DIR_RESPONSE;
1235 msg->err = err;
1236 if (err_text != NULL)
1237 {
1238 strncpy(msg->err_text, err_text, BCMOLT_MAX_ERR_TEXT_LENGTH-1);
1239 msg->err_text[BCMOLT_MAX_ERR_TEXT_LENGTH-1] = 0;
1240 }
1241 return err;
1242}
1243
1244/* Send message and wait for response */
1245bcmos_errno bcmtr_call(bcmolt_devid device, bcmolt_msg *msg)
1246{
1247 static uint32_t corr_tag = 0;
1248 bcmos_task *task;
1249 bcmtr_msg *tmsg = NULL;
1250 bcmtr_conn *conn;
1251 bcmolt_buf tx_buf = {};
1252 bcmos_errno err;
1253 uint8_t instance;
1254
1255 msg->err = BCM_ERR_OK;
1256 msg->dir = BCMOLT_MSG_DIR_REQUEST;
1257 msg->corr_tag = ++corr_tag;
1258
1259 err = _bcmtr_conn_get(device, &conn);
1260 if (err)
1261 {
1262 return bcmtr_call_err(msg, err, NULL);
1263 }
1264
1265 /* prevent sleeping in RX thread (this would cause it to never wake up) */
1266 task = bcmos_task_current();
1267 if (task == &conn->rx_thread)
1268 {
1269 return bcmtr_call_err(msg, BCM_ERR_COMM_FAIL, "Cannot call API functions from PCI RX thread");
1270 }
1271
1272 instance = bcmolt_msg_instance(msg);
1273 if (instance >= BCMTR_MAX_INSTANCES)
1274 {
1275 return bcmtr_call_err(msg, BCM_ERR_KEY_RANGE, "Invalid PON index");
1276 }
1277
1278 /* Allocate transport buffer and pack */
1279 err = _bcmtr_pack(conn, msg, &tx_buf);
1280 if (err)
1281 {
1282 bcmolt_buf_free(&tx_buf);
1283 return bcmtr_call_err(msg, err, NULL);
1284 }
1285
1286 /* transmit request under connection lock */
1287 bcmos_mutex_lock(&conn->mutex);
1288 err = _bcmtr_send(conn, msg, &tx_buf, BCMTR_SEND_FLAGS_CALL, &tmsg);
1289 if (!tmsg)
1290 {
1291 bcmos_mutex_unlock(&conn->mutex);
1292 bcmolt_buf_free(&tx_buf);
1293 return bcmtr_call_err(msg, err, NULL);
1294 }
1295 TAILQ_INSERT_TAIL(&conn->msg_list, tmsg, l);
1296 bcmos_mutex_unlock(&conn->mutex);
1297
1298 /* Wait for response or timeout.
1299 * Message timeout is enforced by audit rather than semaphore timeout option
1300 */
1301 bcmos_sem_wait(&tmsg->sem, BCMOS_WAIT_FOREVER);
1302
1303 /* Connection could've been killed while we are waiting here.
1304 * It is indicated by COMM_FAILURE in msg->err.
1305 * In this case transport header (tmsg) is already released
1306 */
1307 if (msg->err == BCM_ERR_COMM_FAIL)
1308 {
1309 return bcmtr_call_err(msg, msg->err, NULL);
1310 }
1311
1312 err = tmsg->err;
1313 if (!err)
1314 {
1315 err = _bcmtr_msg_unpack(conn, &tmsg->rx_buf, &tmsg->hdr, tmsg->timestamp, &msg);
1316 }
1317
1318 /* Take connection lock again in order to release transport header safely */
1319 bcmos_mutex_lock(&conn->mutex);
1320 _bcmtr_tmsg_free(tmsg, NULL);
1321 bcmos_mutex_unlock(&conn->mutex);
1322
1323 return bcmtr_call_err(msg, err ? err : msg->err, NULL);
1324}
1325
1326
1327#ifdef BCM_SUBSYSTEM_HOST
1328/* Send (un)registration info to the mux */
1329static bcmos_errno _bcmtr_send_to_mux(bcmtr_conn *conn, bcmtr_hdr *hdr)
1330{
1331 bcmolt_buf buf;
1332 uint8_t packed_hdr[BCMTR_HDR_SIZE];
1333 bcmos_errno err;
1334
1335 err = bcmolt_buf_alloc(&buf, BCMTR_HDR_SIZE + conn->cfg.plugin_cfg.headroom, BCMTR_BUF_ENDIAN);
1336 if (err)
1337 {
1338 return err;
1339 }
1340 bcmolt_buf_skip(&buf, conn->cfg.plugin_cfg.headroom);
1341 bcmtr_header_pack(hdr, packed_hdr);
1342 if (bcmolt_buf_write(&buf, packed_hdr, BCMTR_HDR_SIZE))
1343 err = conn->driver.send(conn->drv_priv, 0, &buf, BCMTR_SEND_FLAGS_PRI_NORMAL);
1344 else
1345 err = BCM_ERR_OVERFLOW;
1346 bcmolt_buf_free(&buf);
1347 return err;
1348}
1349#endif
1350
1351/** Register message handler
1352 *
1353 * \param[in] device OLT device index
1354 * \param[in] parm Registration parameters
1355 * \returns BCM_ERR_OK or error code
1356 */
1357bcmos_errno bcmtr_msg_handler_register(bcmolt_devid device, const bcmtr_handler_parm *parm)
1358{
1359 bcmtr_conn *conn;
1360 bcmos_errno err = BCM_ERR_OK;
1361 bcmolt_group_id msg_id;
1362 bcmtr_handler *h;
1363
1364 if (device >= BCMTR_MAX_OLTS || !parm || !parm->app_cb || parm->instance >= BCMTR_MAX_INSTANCES)
1365 {
1366 return BCM_ERR_PARM;
1367 }
1368
1369 if ((unsigned)parm->object >= BCMOLT_OBJ_ID__NUM_OF)
1370 {
1371 bcmtr_handler_parm p1 = *parm;
1372
1373 for (p1.object = 0; p1.object < BCMOLT_OBJ_ID__NUM_OF && !err; p1.object++)
1374 {
1375 err = bcmtr_msg_handler_register(device, &p1);
1376 /* Ignore RANGE error that indicates that the object being iterated doesn't have this group */
1377 /* Ignore ALREADY error that indicates that registration is already present for specific message and was skipped */
1378 if ((err == BCM_ERR_RANGE) || (err == BCM_ERR_ALREADY))
1379 {
1380 err = BCM_ERR_OK;
1381 }
1382 }
1383 return err;
1384 }
1385
1386 if ((unsigned)parm->subgroup == BCMOLT_SUBGROUP_ANY)
1387 {
1388 bcmtr_handler_parm p1 = *parm;
1389
1390 for (p1.subgroup = 0;
1391 bcmolt_group_id_combine(p1.object, p1.group, p1.subgroup, &msg_id) == BCM_ERR_OK &&
1392 (err == BCM_ERR_OK || err == BCM_ERR_ALREADY);
1393 p1.subgroup++)
1394 {
1395 err = bcmtr_msg_handler_register(device, &p1);
1396 }
1397 if (err == BCM_ERR_ALREADY)
1398 {
1399 err = BCM_ERR_OK;
1400 }
1401 return err;
1402 }
1403
1404 /* Specific object/group/subgroup */
1405 err = bcmolt_group_id_combine(parm->object, parm->group, parm->subgroup, &msg_id);
1406 if (err)
1407 return err;
1408 h = &conn_info[device].msg_handler[msg_id][parm->instance];
1409
1410 /* Refuse new registration if already registered */
1411 if (h->app_cb != _bcmtr_dft_msg_handler)
1412 {
1413 return BCM_ERR_ALREADY;
1414 }
1415
1416 h->app_cb = parm->app_cb;
1417 h->flags = parm->flags;
1418 if ((parm->flags & BCMOLT_AUTO_FLAGS_DISPATCH))
1419 {
1420 if (parm->module != BCMOS_MODULE_ID_NONE)
1421 {
1422 h->module = parm->module;
1423 }
1424 else
1425 {
1426 h->module = bcmos_module_current();
1427 }
1428 }
1429 else
1430 {
1431 h->module = BCMOS_MODULE_ID_NONE;
1432 }
1433
1434#ifdef BCM_SUBSYSTEM_HOST
1435 /* On the host, automatically connect on message handler registration */
1436 err = _bcmtr_conn_get(device, &conn);
1437 if (err)
1438 return err;
1439
1440 /* Registration with tr-mux is per device, per-instance, per-object */
1441 if (!parm->subgroup)
1442 {
1443 /* Send registration info to the mux driver. It is just a header */
1444 bcmtr_hdr hdr;
1445 memset(&hdr, 0, sizeof(hdr));
1446 hdr.msg_id = msg_id;
1447 hdr.auto_proxy_reg = 1;
1448 hdr.instance = parm->instance;
1449 err = _bcmtr_send_to_mux(conn, &hdr);
1450 if (err)
1451 {
1452 bcmtr_msg_handler_unregister(device, parm);
1453 }
1454 }
1455#else
1456 (void)conn;
1457#endif
1458
1459 return err;
1460}
1461
1462/* Unregister autonomous message handler */
1463bcmos_errno bcmtr_msg_handler_unregister(bcmolt_devid device, const bcmtr_handler_parm *parm)
1464{
1465 bcmtr_conn *conn;
1466 bcmos_errno err = BCM_ERR_OK;
1467 bcmolt_group_id msg_id;
1468 bcmtr_handler *h;
1469
1470 if (device >= BCMTR_MAX_OLTS || !parm || parm->instance >= BCMTR_MAX_INSTANCES)
1471 {
1472 return BCM_ERR_PARM;
1473 }
1474
1475 if ((unsigned)parm->object >= BCMOLT_OBJ_ID__NUM_OF)
1476 {
1477 bcmtr_handler_parm p1 = *parm;
1478 for (p1.object = 0; p1.object < BCMOLT_OBJ_ID__NUM_OF && !err; p1.object++)
1479 {
1480 err = bcmtr_msg_handler_unregister(device, &p1);
1481 /* Ignore RANGE error that indicates that the object being iterated doesn't have this group */
1482 if (err == BCM_ERR_RANGE)
1483 {
1484 err = BCM_ERR_OK;
1485 }
1486 }
1487 return err;
1488 }
1489
1490 if ((unsigned)parm->subgroup == BCMOLT_SUBGROUP_ANY)
1491 {
1492 bcmtr_handler_parm p1 = *parm;
1493
1494 for (p1.subgroup = 0;
1495 bcmolt_group_id_combine(p1.object, p1.group, p1.subgroup, &msg_id) == BCM_ERR_OK && !err;
1496 p1.subgroup++)
1497 {
1498 err = bcmtr_msg_handler_unregister(device, &p1);
1499 }
1500 return err;
1501 }
1502
1503 err = bcmolt_group_id_combine(parm->object, parm->group, parm->subgroup, &msg_id);
1504 if (err)
1505 return err;
1506
1507 h = &conn_info[device].msg_handler[msg_id][parm->instance];
1508 h->app_cb = _bcmtr_dft_msg_handler;
1509 h->flags = BCMOLT_AUTO_FLAGS_NONE;
1510 h->module = BCMOS_MODULE_ID_NONE;
1511
1512#ifdef BCM_SUBSYSTEM_HOST
1513 /* On the host, automatically connect on message handler (de)registration */
1514 err = _bcmtr_conn_get(device, &conn);
1515 if (err)
1516 return err;
1517
1518 /* Registration with tr-mux is per device, per-instance, per-object */
1519 if (!parm->subgroup)
1520 {
1521 /* Send un-registration info to the mux driver. It is just a header */
1522 bcmtr_hdr hdr;
1523 memset(&hdr, 0, sizeof(hdr));
1524 hdr.msg_id = msg_id;
1525 hdr.auto_proxy_unreg = 1;
1526 hdr.instance = parm->instance;
1527 err = _bcmtr_send_to_mux(conn, &hdr);
1528 }
1529#else
1530 (void)conn;
1531#endif
1532
1533 return err;
1534}
1535
1536/** Get registration info
1537 *
1538 * \param[in] device OLT device index
1539 * \param[in,out] parm Registration parameters.
1540 * instance, group, object, subgroup must be set
1541 * \returns BCM_ERR_OK or error code
1542 */
1543bcmos_errno bcmtr_msg_handler_register_get(bcmolt_devid device, bcmtr_handler_parm *parm)
1544{
1545 bcmos_errno err;
1546 bcmolt_group_id msg_id;
1547 bcmtr_handler *h;
1548
1549 if (device >= BCMTR_MAX_OLTS ||
1550 !parm ||
1551 parm->instance >= BCMTR_MAX_INSTANCES ||
1552 (unsigned)parm->object >= BCMOLT_OBJ_ID__NUM_OF ||
1553 (unsigned)parm->subgroup == BCMOLT_SUBGROUP_ANY)
1554 {
1555 return BCM_ERR_PARM;
1556 }
1557
1558 err = bcmolt_group_id_combine(parm->object, parm->group, parm->subgroup, &msg_id);
1559 if (err)
1560 return err;
1561
1562 h = &conn_info[device].msg_handler[msg_id][parm->instance];
1563 parm->app_cb = (h->app_cb == _bcmtr_dft_msg_handler) ? NULL : h->app_cb;
1564 parm->flags = h->flags;
1565 parm->module = h->module;
1566
1567 return BCM_ERR_OK;
1568}
1569
1570/* Get transport statistics */
1571bcmos_errno bcmtr_stat_get(bcmolt_devid device, bcmtr_stat *stat)
1572{
1573 bcmtr_conn *conn;
1574 bcmos_errno err;
1575
1576 if (!stat)
1577 {
1578 return BCM_ERR_PARM;
1579 }
1580 err = _bcmtr_conn_get(device, &conn);
1581 if (err)
1582 {
1583 return err;
1584 }
1585 bcmos_mutex_lock(&conn->mutex);
1586 *stat = conn->stat;
1587 memset(&conn->stat, 0, sizeof(conn->stat));
1588 bcmos_mutex_unlock(&conn->mutex);
1589
1590 return BCM_ERR_OK;
1591}
1592
1593#if defined(SIMULATION_BUILD) && defined(LINUX_USER_SPACE) && defined(BCM_SUBSYSTEM_EMBEDDED) && defined(BCMTR_UDP_SUPPORT)
1594static int _bcmtr_assign_random_port(void)
1595{
1596 int port;
1597
1598 srand(bcmos_timestamp());
1599 port = rand() % 50000;
1600 if (port < 20000)
1601 {
1602 port += 20000;
1603 }
1604
1605 return port;
1606}
1607#endif
1608
1609/* Connect device in raw mode. Receive task is NOT created */
1610bcmos_errno bcmtr_proxy_connect(bcmolt_devid device, uint32_t *headroom)
1611{
1612 bcmtr_conn *conn;
1613 bcmos_errno rc;
1614
1615 rc = _bcmtr_conn_get_any(device, &conn, BCMOS_TRUE);
1616 if (!rc)
1617 {
1618 *headroom = conn->cfg.plugin_cfg.headroom;
1619 }
1620 return rc;
1621}
1622
1623/* Send data to device in raw mode */
1624bcmos_errno bcmtr_proxy_send(bcmolt_devid device, bcmolt_buf *tx_buf)
1625{
1626 bcmtr_conn *conn;
1627 bcmos_errno rc;
1628 rc = _bcmtr_conn_get_any(device, &conn, BCMOS_TRUE);
1629 if (rc)
1630 return rc;
1631 rc = conn->driver.send(conn->drv_priv, 0, tx_buf, BCMTR_SEND_FLAGS_NONE);
1632 return rc;
1633}
1634
1635/* Receive data from device in raw mode */
1636bcmos_errno bcmtr_proxy_receive(bcmolt_devid device, bcmolt_buf *rx_buf)
1637{
1638 bcmtr_conn *conn;
1639 bcmolt_subchannel subch;
1640 bcmos_errno rc;
1641 rc = _bcmtr_conn_get_any(device, &conn, BCMOS_TRUE);
1642 if (rc)
1643 return rc;
1644 rc = conn->driver.recv(conn->drv_priv, &subch, rx_buf);
1645 return rc;
1646}
1647
1648/** Initialize transport library.
1649 * \returns BCM_ERR_OK or error code
1650 */
1651bcmos_errno bcmtr_init(void)
1652{
1653 bcmos_errno err = BCM_ERR_OK;
1654 bcmolt_devid device;
1655
1656 bcmos_printf("bcmtr_init: init transport library\n");
1657
1658#if defined(BCMTR_UDP_SUPPORT)
1659
1660 /* Set defaults and add configuration command */
1661 if (!bcmtr_host_ip)
1662 bcmtr_host_ip = BCMTR_TR_UDP_HOST_IP;
1663 if (!bcmtr_host_udp_port)
1664 bcmtr_host_udp_port = BCMTR_TR_UDP_HOST_PORT;
1665 for (device = 0; device < BCMTR_MAX_OLTS; device++)
1666 {
1667 if (!bcmtr_olt_ip[device])
1668 bcmtr_olt_ip[device] = BCMTR_TR_UDP_OLT_IP + device;
1669 if (!bcmtr_olt_udp_port[device])
1670 bcmtr_olt_udp_port[device] = BCMTR_TR_UDP_OLT_PORT;
1671 }
1672
1673 /* A hack to allow multiple simulations run on the same PC */
1674#if defined(SIMULATION_BUILD) && defined(LINUX_USER_SPACE) && defined(BCM_SUBSYSTEM_EMBEDDED)
1675 {
1676 bcmtr_olt_udp_port[0] = _bcmtr_assign_random_port();
1677 }
1678#endif
1679#endif
1680
1681 /* Initialize handlers */
1682 for (device = 0; device < BCMTR_MAX_OLTS; device++)
1683 {
1684 bcmolt_group_id group;
1685 for (group = 0; group < BCMOLT_GROUP_ID__NUM_OF; group++)
1686 {
1687 int inst;
1688 for (inst = 0; inst < BCMTR_MAX_INSTANCES; inst++)
1689 {
1690 conn_info[device].msg_handler[group][inst].app_cb = _bcmtr_dft_msg_handler;
1691 }
1692 }
1693 }
1694
1695 bcmos_mutex_create(&conn_lock, 0, NULL);
1696
1697 return err;
1698}
1699
1700/** Release resources used by transport library.
1701 * \returns BCM_ERR_OK or error code
1702 */
1703bcmos_errno bcmtr_exit(void)
1704{
1705 int i;
1706
1707 for (i = 0; i < BCMTR_MAX_OLTS; i++)
1708 bcmtr_disconnect(i);
1709
1710 bcmos_mutex_destroy(&conn_lock);
1711
1712 return BCM_ERR_OK;
1713}