1 /*
2  * Copyright 2014-2017 Cavium, Inc.
3  * The contents of this file are subject to the terms of the Common Development
4  * and Distribution License, v.1,  (the "License").
5  *
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the License at available
9  * at http://opensource.org/licenses/CDDL-1.0
10  *
11  * See the License for the specific language governing permissions and
12  * limitations under the License.
13  */
14 
15 #include "lm5706.h"
16 
17 
18 #ifndef LM_NON_LEGACY_MODE_SUPPORT
19 /*******************************************************************************
20  * Description:
21  *
22  * Return:
23  ******************************************************************************/
24 lm_status_t
25 lm_send_packet(
26     lm_device_t *pdev,
27     u32_t chain_idx,
28     lm_packet_t *packet,
29     lm_frag_list_t *frags)
30 {
31     u16_t lso_bd_reserved;
32     u16_t ipv6_ext_len;
33     lm_tx_chain_t *txq;
34     tx_bd_t *start_bd;
35     tx_bd_t *last_bd;
36     tx_bd_t *prod_bd;
37     lm_frag_t *frag;
38     u16_t prod_idx;
39     u32_t flags;
40     u32_t cnt;
41 
42     txq = &pdev->tx_info.chain[chain_idx];
43 
44     if(packet == NULL)
45     {
46         // hardcode offset in case of L2_ONLY (e.g Solaris)
47         u32_t cmd_offset = 34*sizeof(u32_t); //  == OFFSETOF(l4_context_t, l4ctx_cmd)
48         MBQ_WR16(
49             pdev,
50             GET_CID(txq->cid_addr),
51             cmd_offset +
52                 OFFSETOF(tcp_context_cmd_cell_te_t, ccell_tx_host_bidx),
53             txq->prod_idx);
54         MBQ_WR32(
55             pdev,
56             GET_CID(txq->cid_addr),
57             cmd_offset +
58                 OFFSETOF(tcp_context_cmd_cell_te_t, ccell_tx_host_bseq),
59             txq->prod_bseq);
60 
61         return LM_STATUS_SUCCESS;
62     }
63 
64     #if DBG
65     if(frags->cnt == 0)
66     {
67         DbgBreakMsg("zero frag_cnt\n");
68 
69         return LM_STATUS_INVALID_PARAMETER;
70     }
71 
72     packet->u1.tx.dbg_start_bd = txq->prod_bd;
73     packet->u1.tx.dbg_start_bd_idx = txq->prod_idx;
74     packet->u1.tx.dbg_frag_cnt = (u16_t) frags->cnt;
75     #endif
76 
77     last_bd = NULL;
78 
79     if(frags->cnt > txq->bd_left)
80     {
81         /* The caller should have done this check before calling this
82          * routine. */
83         DbgBreakMsg("No tx bd left.\n");
84 
85         return LM_STATUS_RESOURCE;
86     }
87 
88     txq->bd_left -= (u16_t) frags->cnt;
89 
90     packet->size = 0;
91     flags = 0;
92 
93     if(packet->u1.tx.flags & LM_TX_FLAG_INSERT_VLAN_TAG)
94     {
95         flags |= TX_BD_FLAGS_VLAN_TAG;
96     }
97 
98     if((packet->u1.tx.flags & LM_TX_FLAG_TCP_LSO_FRAME) == 0)
99     {
100         if(packet->u1.tx.flags & LM_TX_FLAG_COMPUTE_IP_CKSUM)
101         {
102             flags |= TX_BD_FLAGS_IP_CKSUM;
103             LM_INC64(&pdev->tx_info.stats.ip_cso_frames, 1);
104         }
105 
106         if(packet->u1.tx.flags & LM_TX_FLAG_COMPUTE_TCP_UDP_CKSUM)
107         {
108             flags |= TX_BD_FLAGS_TCP_UDP_CKSUM;
109             if(packet->u1.tx.flags & LM_TX_FLAG_IPV6_PACKET)
110             {
111                 LM_INC64(&pdev->tx_info.stats.ipv6_tcp_udp_cso_frames, 1);
112             }
113             else
114             {
115                 LM_INC64(&pdev->tx_info.stats.ipv4_tcp_udp_cso_frames, 1);
116             }
117         }
118     }
119 
120     if(packet->u1.tx.flags & LM_TX_FLAG_DONT_COMPUTE_CRC)
121     {
122         flags |= TX_BD_FLAGS_DONT_GEN_CRC;
123     }
124 
125     if(packet->u1.tx.flags & LM_TX_FLAG_TCP_LSO_FRAME)
126     {
127         if(packet->u1.tx.flags & LM_TX_FLAG_IPV6_PACKET)
128         {
129             /* TCP option length - bottom 4 bits of TX_BD_FLAGS_SW_OPTION_WORD
130              *    in term of the number of 4-byte words.
131              * IP header length - bits 1-2 of bd flag, the upper 2 bits of
132              *    tx_bd_reserved, and the upper 1 bit of
133              *    TX_BD_FLAGS_SW_OPTION_WORD will be used for IPV6 extension
134              *    header length in term of 8-btye words.
135              * TX_BD_FLAGS_SW_FLAGS bit will be used to indicate IPV6 LSO. */
136             flags |= TX_BD_FLAGS_SW_FLAGS;
137 
138             if(packet->u1.tx.flags & LM_TX_FLAG_TCP_LSO_SNAP_FRAME)
139             {
140                 flags |= TX_BD_FLAGS_SW_SNAP;
141             }
142 
143             DbgBreakIf(packet->u1.tx.lso_tcp_hdr_len < 20 ||
144                        packet->u1.tx.lso_tcp_hdr_len > 84 ||
145                        packet->u1.tx.lso_tcp_hdr_len % 4);
146 
147             /* tcp option length in term of number of 32-bit word.  4 bits
148              * are used for the number of words. */
149             flags |= (packet->u1.tx.lso_tcp_hdr_len - 20) << 6;
150 
151             DbgBreakIf(packet->u1.tx.lso_ip_hdr_len < 20 ||
152                        packet->u1.tx.lso_ip_hdr_len > 296 ||
153                       (packet->u1.tx.lso_ip_hdr_len - 40) % 8);
154 
155             /* ipv6 extension header length.  6 bits are used for the number
156              * of 64-bit words. */
157             ipv6_ext_len = packet->u1.tx.lso_ip_hdr_len - 40;
158 
159             DbgBreakIf(ipv6_ext_len & 0x7);
160 
161             /* ext_len in number of 8-byte words. */
162             ipv6_ext_len >>= 3;
163 
164             flags |= (ipv6_ext_len & 0x3) << 1;             /* bit 1-0 */
165 
166             lso_bd_reserved = packet->u1.tx.lso_mss;
167             lso_bd_reserved |= (ipv6_ext_len & 0xc) << 12;  /* bit 3-2 */
168 
169             flags |= (ipv6_ext_len & 0x10) << 8;            /* bit 4  */
170 
171             DbgBreakIf(ipv6_ext_len >> 5);  /* bit 5 & high are invalid. */
172 
173             LM_INC64(&pdev->tx_info.stats.ipv6_lso_frames, 1);
174         }
175         else
176         {
177             flags |= TX_BD_FLAGS_SW_LSO;
178             if(packet->u1.tx.flags & LM_TX_FLAG_TCP_LSO_SNAP_FRAME)
179             {
180                 flags |= TX_BD_FLAGS_SW_SNAP;
181             }
182 
183             DbgBreakIf(packet->u1.tx.lso_ip_hdr_len +
184                 packet->u1.tx.lso_tcp_hdr_len > 120);
185 
186             /* The size of IP and TCP options in term of 32-bit words. */
187             flags |= (packet->u1.tx.lso_ip_hdr_len +
188                 packet->u1.tx.lso_tcp_hdr_len - 40) << 6;
189 
190             lso_bd_reserved = packet->u1.tx.lso_mss;
191 
192             LM_INC64(&pdev->tx_info.stats.ipv4_lso_frames, 1);
193         }
194     }
195     else
196     {
197         lso_bd_reserved = 0;
198     }
199 
200     start_bd = txq->prod_bd;
201     frag = frags->frag_arr;
202 
203     /* Get the pointer to the current BD and its index. */
204     prod_idx = txq->prod_idx;
205     prod_bd = txq->prod_bd;
206 
207     /* This is the number of times we cross a BD page boundary for this
208      * packet.  This and the bd_used value will give us the total number
209      * of BD slots needed to send this packet which is used to determine
210      * if a packet has been sent.  We only need this because unlike L2
211      * completion, LSO completion does not end at a request boundary.
212      * For example, if we had an LSO request that spans BD#100-120.  We
213      * could get a transmit consumer index of 115. */
214     packet->u1.tx.span_pages = 0;
215 
216     /* Initialize the bd's of this packet. */
217     for(cnt = 0; cnt < frags->cnt; cnt++)
218     {
219         DbgBreakIf(frag->size >= 0x10000 || frag->size == 0);
220 
221         prod_bd->tx_bd_haddr_lo = frag->addr.as_u32.low;
222         prod_bd->tx_bd_haddr_hi = frag->addr.as_u32.high;
223         prod_bd->tx_bd_nbytes = (u16_t) frag->size;
224         prod_bd->tx_bd_vlan_tag = packet->u1.tx.vlan_tag;
225         prod_bd->tx_bd_flags = (u16_t) flags;
226 
227         if(packet->u1.tx.flags & LM_TX_FLAG_TCP_LSO_FRAME)
228         {
229             prod_bd->tx_bd_reserved = lso_bd_reserved;
230         }
231         else if(pdev->params.test_mode & TEST_MODE_TX_BD_TAGGING)
232         {
233             prod_bd->tx_bd_reserved = prod_idx & 0x0fff;
234             prod_bd->tx_bd_reserved |= (u16_t) (GET_CID(txq->cid_addr) << 12);
235         }
236 
237         packet->size += frag->size;
238 
239         last_bd = prod_bd;
240         frag++;
241 
242         /* Advance to the next BD. */
243         prod_bd++;
244         prod_idx++;
245         if((prod_idx & MAX_BD_PER_PAGE) == MAX_BD_PER_PAGE)
246         {
247             /* Only increment span_pages when this BDs for this request
248              * cross a page boundary. */
249             if(cnt+1 < frags->cnt)
250             {
251                 packet->u1.tx.span_pages++;
252             }
253 
254             prod_idx++;
255             prod_bd = *((tx_bd_t **) ((tx_bd_next_t *)
256                 prod_bd)->tx_bd_next_reserved);
257         }
258     }
259 
260     /* Set the bd flags of the first and last BDs. */
261     flags |= TX_BD_FLAGS_END;
262     if(packet->u1.tx.flags & LM_TX_FLAG_COAL_NOW)
263     {
264         flags |= TX_BD_FLAGS_COAL_NOW;
265     }
266 
267     last_bd->tx_bd_flags |= (u16_t) flags;
268     start_bd->tx_bd_flags |= TX_BD_FLAGS_START;
269 
270     #if INCLUDE_OFLD_SUPPORT
271     /* We need to do the padding for the catchup path. */
272     if(chain_idx == pdev->tx_info.cu_idx &&
273         packet->size < MIN_ETHERNET_PACKET_SIZE)
274     {
275         last_bd->tx_bd_nbytes +=
276             (u16_t) (MIN_ETHERNET_PACKET_SIZE - packet->size);
277         packet->size = MIN_ETHERNET_PACKET_SIZE;
278     }
279     #endif
280 
281     /* Save the number of BDs used.  Later we need to add this value back
282      * to txq->bd_left when the packet is sent. */
283     packet->u1.tx.bd_used = (u16_t) frags->cnt;
284 
285     packet->u1.tx.next_bd_idx = prod_idx;
286 
287     txq->prod_bd = prod_bd;
288     txq->prod_idx = prod_idx;
289     txq->prod_bseq += packet->size;
290 #if (DBG)
291     if (chain_idx == pdev->tx_info.cu_idx)
292     {
293         DbgBreakIf(packet->size > pdev->params.mtu + 4);
294     }
295     else
296     {
297         DbgBreakIf(packet->size > pdev->params.mtu &&
298             (flags & (TX_BD_FLAGS_SW_LSO | TX_BD_FLAGS_SW_FLAGS)) == 0);
299     }
300 #endif
301     s_list_push_tail(&txq->active_descq, &packet->link);
302 
303     if(!(packet->u1.tx.flags & LM_TX_FLAG_SKIP_MBQ_WRITE))
304     {
305         // hardcode offset in case of L2_ONLY (e.g Solaris)
306         u32_t cmd_offset = 34*sizeof(u32_t); //  == OFFSETOF(l4_context_t, l4ctx_cmd)
307         MBQ_WR16(
308             pdev,
309             GET_CID(txq->cid_addr),
310             cmd_offset +
311                 OFFSETOF(tcp_context_cmd_cell_te_t, ccell_tx_host_bidx),
312             txq->prod_idx);
313         MBQ_WR32(
314             pdev,
315             GET_CID(txq->cid_addr),
316             cmd_offset +
317                 OFFSETOF(tcp_context_cmd_cell_te_t, ccell_tx_host_bseq),
318             txq->prod_bseq);
319     }
320 
321     return LM_STATUS_SUCCESS;
322 } /* lm_send_packet */
323 #else
324 /*******************************************************************************
325  * Description:
326  *
327  * Return:
328  ******************************************************************************/
329 lm_status_t
330 lm_send_packet(
331     lm_device_t *pdev,
332     u32_t chain_idx,
333     lm_packet_t *packet,
334     lm_frag_list_t *frags)
335 {
336     u16_t lso_bd_reserved;
337     u16_t ipv6_ext_len;
338     lm_tx_chain_t *txq;
339     tx_bd_t *start_bd;
340     tx_bd_t *last_bd;
341     tx_bd_t *prod_bd;
342     lm_frag_t *frag;
343     u16_t prod_idx;
344     u32_t flags;
345     u32_t cnt;
346     lm_pkt_tx_info_t *pkt_info;
347 
348     txq = &pdev->tx_info.chain[chain_idx];
349 
350     if(packet == NULL)
351     {
352         // hardcode offset in case of L2_ONLY (e.g Solaris)
353         u32_t cmd_offset = 34*sizeof(u32_t); //  == OFFSETOF(l4_context_t, l4ctx_cmd)
354         MBQ_WR16(
355             pdev,
356             GET_CID(txq->cid_addr),
357             cmd_offset +
358                 OFFSETOF(tcp_context_cmd_cell_te_t, ccell_tx_host_bidx),
359             txq->prod_idx);
360         if(pdev->vars.enable_cu_rate_limiter &&
361            txq->idx == TX_CHAIN_IDX1)
362         {
363             REG_WR_IND(
364                 pdev,
365                 OFFSETOF(reg_space_t, com.com_scratch[0])+COM_HSI_OFFSETOFF(com_cu_host_bseq),
366                 txq->prod_bseq);
367         }
368         else
369         {
370         	MBQ_WR32(
371             	pdev,
372             	GET_CID(txq->cid_addr),
373             	cmd_offset +
374                 OFFSETOF(tcp_context_cmd_cell_te_t, ccell_tx_host_bseq),
375             	txq->prod_bseq);
376         }
377 
378         return LM_STATUS_SUCCESS;
379     }
380 
381     #if DBG
382     if(frags->cnt == 0)
383     {
384         DbgBreakMsg("zero frag_cnt\n");
385 
386         return LM_STATUS_INVALID_PARAMETER;
387     }
388 
389     packet->u1.tx.dbg_start_bd = txq->prod_bd;
390     packet->u1.tx.dbg_start_bd_idx = txq->prod_idx;
391     packet->u1.tx.dbg_frag_cnt = (u16_t) frags->cnt;
392     #endif
393 
394     last_bd = NULL;
395 
396     if(frags->cnt > txq->bd_left)
397     {
398         /* The caller should have done this check before calling this
399          * routine. */
400         DbgBreakMsg("No tx bd left.\n");
401 
402         return LM_STATUS_RESOURCE;
403     }
404 
405     txq->bd_left -= (u16_t) frags->cnt;
406 
407     pkt_info = packet->u1.tx.tx_pkt_info;
408     packet->u1.tx.size = 0;
409     flags = 0;
410 
411     if(pkt_info->flags & LM_TX_FLAG_INSERT_VLAN_TAG)
412     {
413         flags |= TX_BD_FLAGS_VLAN_TAG;
414     }
415 
416     if((pkt_info->flags & LM_TX_FLAG_TCP_LSO_FRAME) == 0)
417     {
418         if(pkt_info->flags & LM_TX_FLAG_COMPUTE_IP_CKSUM)
419         {
420             flags |= TX_BD_FLAGS_IP_CKSUM;
421             LM_INC64(&pdev->tx_info.stats.ip_cso_frames, 1);
422         }
423 
424         if(pkt_info->flags & LM_TX_FLAG_COMPUTE_TCP_UDP_CKSUM)
425         {
426             flags |= TX_BD_FLAGS_TCP_UDP_CKSUM;
427             if(pkt_info->flags & LM_TX_FLAG_IPV6_PACKET)
428             {
429                 LM_INC64(&pdev->tx_info.stats.ipv6_tcp_udp_cso_frames, 1);
430             }
431             else
432             {
433                 LM_INC64(&pdev->tx_info.stats.ipv4_tcp_udp_cso_frames, 1);
434             }
435         }
436     }
437 
438     if(pkt_info->flags & LM_TX_FLAG_DONT_COMPUTE_CRC)
439     {
440         flags |= TX_BD_FLAGS_DONT_GEN_CRC;
441     }
442 
443     if(pkt_info->flags & LM_TX_FLAG_TCP_LSO_FRAME)
444     {
445         if(pkt_info->flags & LM_TX_FLAG_IPV6_PACKET)
446         {
447             /* TCP option length - bottom 4 bits of TX_BD_FLAGS_SW_OPTION_WORD
448              *    in term of the number of 4-byte words.
449              * IP header length - bits 1-2 of bd flag, the upper 2 bits of
450              *    tx_bd_reserved, and the upper 1 bit of
451              *    TX_BD_FLAGS_SW_OPTION_WORD will be used for IPV6 extension
452              *    header length in term of 8-btye words.
453              * TX_BD_FLAGS_SW_FLAGS bit will be used to indicate IPV6 LSO. */
454             flags |= TX_BD_FLAGS_SW_FLAGS;
455 
456             if(pkt_info->flags & LM_TX_FLAG_TCP_LSO_SNAP_FRAME)
457             {
458                 flags |= TX_BD_FLAGS_SW_SNAP;
459             }
460 
461             DbgBreakIf(pkt_info->lso_tcp_hdr_len < 20 ||
462                        pkt_info->lso_tcp_hdr_len > 84 ||
463                        pkt_info->lso_tcp_hdr_len % 4);
464 
465             /* tcp option length in term of number of 32-bit word.  4 bits
466              * are used for the number of words. */
467             flags |= (pkt_info->lso_tcp_hdr_len - 20) << 6;
468 
469             DbgBreakIf(pkt_info->lso_ip_hdr_len < 20 ||
470                        pkt_info->lso_ip_hdr_len > 296 ||
471                       (pkt_info->lso_ip_hdr_len - 40) % 8);
472 
473             /* ipv6 extension header length.  6 bits are used for the number
474              * of 64-bit words. */
475             ipv6_ext_len = pkt_info->lso_ip_hdr_len - 40;
476 
477             DbgBreakIf(ipv6_ext_len & 0x7);
478 
479             /* ext_len in number of 8-byte words. */
480             ipv6_ext_len >>= 3;
481 
482             flags |= (ipv6_ext_len & 0x3) << 1;             /* bit 1-0 */
483 
484             lso_bd_reserved = pkt_info->lso_mss;
485             lso_bd_reserved |= (ipv6_ext_len & 0xc) << 12;  /* bit 3-2 */
486 
487             flags |= (ipv6_ext_len & 0x10) << 8;            /* bit 4  */
488 
489             DbgBreakIf(ipv6_ext_len >> 5);  /* bit 5 & high are invalid. */
490 
491             LM_INC64(&pdev->tx_info.stats.ipv6_lso_frames, 1);
492         }
493         else
494         {
495             flags |= TX_BD_FLAGS_SW_LSO;
496             if(pkt_info->flags & LM_TX_FLAG_TCP_LSO_SNAP_FRAME)
497             {
498                 flags |= TX_BD_FLAGS_SW_SNAP;
499             }
500 
501             DbgBreakIf(pkt_info->lso_ip_hdr_len +
502                 pkt_info->lso_tcp_hdr_len > 120);
503 
504             /* The size of IP and TCP options in term of 32-bit words. */
505             flags |= (pkt_info->lso_ip_hdr_len +
506                 pkt_info->lso_tcp_hdr_len - 40) << 6;
507 
508             lso_bd_reserved = pkt_info->lso_mss;
509 
510             LM_INC64(&pdev->tx_info.stats.ipv4_lso_frames, 1);
511         }
512     }
513     else
514     {
515         lso_bd_reserved = 0;
516     }
517 
518     start_bd = txq->prod_bd;
519     frag = frags->frag_arr;
520 
521     /* Get the pointer to the current BD and its index. */
522     prod_idx = txq->prod_idx;
523     prod_bd = txq->prod_bd;
524 
525     /* This is the number of times we cross a BD page boundary for this
526      * packet.  This and the bd_used value will give us the total number
527      * of BD slots needed to send this packet which is used to determine
528      * if a packet has been sent.  We only need this because unlike L2
529      * completion, LSO completion does not end at a request boundary.
530      * For example, if we had an LSO request that spans BD#100-120.  We
531      * could get a transmit consumer index of 115. */
532     packet->u1.tx.span_pages = 0;
533 
534     /* Initialize the bd's of this packet. */
535     for(cnt = 0; cnt < frags->cnt; cnt++)
536     {
537         DbgBreakIf(frag->size >= 0x10000 || frag->size == 0);
538 
539         prod_bd->tx_bd_haddr_lo = frag->addr.as_u32.low;
540         prod_bd->tx_bd_haddr_hi = frag->addr.as_u32.high;
541         prod_bd->tx_bd_nbytes = (u16_t) frag->size;
542         prod_bd->tx_bd_vlan_tag = pkt_info->vlan_tag;
543         prod_bd->tx_bd_flags = (u16_t) flags;
544 
545         if(pkt_info->flags & LM_TX_FLAG_TCP_LSO_FRAME)
546         {
547             prod_bd->tx_bd_reserved = lso_bd_reserved;
548         }
549         else if(pdev->params.test_mode & TEST_MODE_TX_BD_TAGGING)
550         {
551             prod_bd->tx_bd_reserved = prod_idx & 0x0fff;
552             prod_bd->tx_bd_reserved |= (u16_t) (GET_CID(txq->cid_addr) << 12);
553         }
554 
555         packet->u1.tx.size += frag->size;
556 
557         last_bd = prod_bd;
558         frag++;
559 
560         /* Advance to the next BD. */
561         prod_bd++;
562         prod_idx++;
563         if((prod_idx & MAX_BD_PER_PAGE) == MAX_BD_PER_PAGE)
564         {
565             /* Only increment span_pages when this BDs for this request
566              * cross a page boundary. */
567             if(cnt+1 < frags->cnt)
568             {
569                 packet->u1.tx.span_pages++;
570             }
571 
572             prod_idx++;
573             prod_bd = *((tx_bd_t **) ((tx_bd_next_t *)
574                 prod_bd)->tx_bd_next_reserved);
575         }
576     }
577 
578     /* Set the bd flags of the first and last BDs. */
579     flags |= TX_BD_FLAGS_END;
580     if(pkt_info->flags & LM_TX_FLAG_COAL_NOW)
581     {
582         flags |= TX_BD_FLAGS_COAL_NOW;
583     }
584 
585     last_bd->tx_bd_flags |= (u16_t) flags;
586     start_bd->tx_bd_flags |= TX_BD_FLAGS_START;
587 
588     #if INCLUDE_OFLD_SUPPORT
589     /* We need to do the padding for the catchup path. */
590     if(chain_idx == pdev->tx_info.cu_idx &&
591         packet->u1.tx.size < MIN_ETHERNET_PACKET_SIZE)
592     {
593         last_bd->tx_bd_nbytes +=
594             (u16_t) (MIN_ETHERNET_PACKET_SIZE - packet->u1.tx.size);
595         packet->u1.tx.size = MIN_ETHERNET_PACKET_SIZE;
596     }
597     #endif
598 
599     /* Save the number of BDs used.  Later we need to add this value back
600      * to txq->bd_left when the packet is sent. */
601     packet->u1.tx.bd_used = (u16_t) frags->cnt;
602 
603     packet->u1.tx.next_bd_idx = prod_idx;
604 
605     txq->prod_bd = prod_bd;
606     txq->prod_idx = prod_idx;
607     txq->prod_bseq += packet->u1.tx.size;
608 #if (DBG)
609     if (chain_idx == pdev->tx_info.cu_idx)
610     {
611         DbgBreakIf(packet->u1.tx.size > pdev->params.mtu + 4);
612     }
613     else
614     {
615         DbgBreakIf(packet->u1.tx.size > pdev->params.mtu &&
616             (flags & (TX_BD_FLAGS_SW_LSO | TX_BD_FLAGS_SW_FLAGS)) == 0);
617     }
618 #endif
619     s_list_push_tail(&txq->active_descq, &packet->link);
620 
621     if(!(pkt_info->flags & LM_TX_FLAG_SKIP_MBQ_WRITE))
622     {
623         // hardcode offset in case of L2_ONLY (e.g Solaris)
624         u32_t cmd_offset = 34*sizeof(u32_t); //  == OFFSETOF(l4_context_t, l4ctx_cmd)
625         MBQ_WR16(
626             pdev,
627             GET_CID(txq->cid_addr),
628             cmd_offset +
629                 OFFSETOF(tcp_context_cmd_cell_te_t, ccell_tx_host_bidx),
630             txq->prod_idx);
631         if(pdev->vars.enable_cu_rate_limiter &&
632            txq->idx == TX_CHAIN_IDX1)
633         {
634             REG_WR_IND(
635                 pdev,
636                 OFFSETOF(reg_space_t, com.com_scratch[0])+COM_HSI_OFFSETOFF(com_cu_host_bseq),
637                 txq->prod_bseq);
638         }
639         else
640         {
641         	MBQ_WR32(
642             	pdev,
643             	GET_CID(txq->cid_addr),
644             	cmd_offset +
645                 OFFSETOF(tcp_context_cmd_cell_te_t, ccell_tx_host_bseq),
646             	txq->prod_bseq);
647         }
648     }
649 
650     return LM_STATUS_SUCCESS;
651 } /* lm_send_packet */
652 #endif /* LM_NON_LEGACY_MODE_SUPPORT */
653 
654 
655 /*******************************************************************************
656  * Description:
657  *
658  * Return:
659  ******************************************************************************/
660 STATIC u32_t
661 get_packets_sent(
662     struct _lm_device_t *pdev,
663     lm_tx_chain_t *txq,
664     u16_t hw_con_idx,
665     s_list_t *sent_list)
666 {
667     lm_packet_t *pkt;
668     u32_t pkt_cnt;
669 
670     /* The consumer index may stop at the end of a page boundary.
671      * In this case, we need to advance the next to the next one. */
672     if((hw_con_idx & MAX_BD_PER_PAGE) == MAX_BD_PER_PAGE)
673     {
674         hw_con_idx++;
675     }
676 
677     pkt_cnt = 0;
678 
679     while(txq->con_idx != hw_con_idx)
680     {
681         DbgBreakIf(S16_SUB(hw_con_idx, txq->con_idx) <= 0);
682 
683         pkt = (lm_packet_t *) s_list_peek_head(&txq->active_descq);
684 
685         DbgBreakIf(pkt == NULL);
686 
687         if(!pkt)
688 		{
689 			DbgBreakIf(!s_list_is_empty(&txq->active_descq));
690 			break;
691 		}
692         /* LSO requests may not complete at the request boundary.
693          *
694          * if(pkt->u1.tx.flags & LM_TX_FLAG_TCP_LSO_FRAME) */
695         {
696             if((u16_t) S16_SUB(hw_con_idx, txq->con_idx) <
697                 pkt->u1.tx.bd_used + pkt->u1.tx.span_pages)
698             {
699                 break;
700             }
701         }
702 
703         #if DBG
704         DbgBreakIf(pkt->u1.tx.dbg_start_bd_idx != txq->con_idx);
705 
706         /* Make sure hw_con_idx ends at an l2 packet boundary.  For LSO,
707          * request, hw_con_idx may not end at the request boundary. */
708         while(pkt)
709         {
710             if(S16_SUB(hw_con_idx, pkt->u1.tx.next_bd_idx) <= 0)
711             {
712                 break;
713             }
714 
715             pkt = (lm_packet_t *) s_list_next_entry(&pkt->link);
716         }
717 
718         DbgBreakIf(pkt == NULL);
719 
720         /* catchup workaround.
721          * DbgBreakIf(
722          *    !(pkt->u1.tx.flags & LM_TX_FLAG_TCP_LSO_FRAME) &&
723          *    (hw_con_idx != pkt->u1.tx.next_bd_idx)); */
724         #endif
725 
726         pkt = (lm_packet_t *) s_list_pop_head(&txq->active_descq);
727 
728         /* Advance the txq->con_idx to the start bd_idx of the next packet. */
729         txq->con_idx = pkt->u1.tx.next_bd_idx;
730 
731         pkt->status = LM_STATUS_SUCCESS;
732 
733         txq->bd_left += pkt->u1.tx.bd_used;
734 
735         s_list_push_tail(sent_list, &pkt->link);
736 
737         pkt_cnt++;
738     }
739 
740     return pkt_cnt;
741 } /* get_packets_sent */
742 
743 
744 
745 /*******************************************************************************
746  * Description:
747  *
748  * Return:
749  ******************************************************************************/
750 u32_t
751 lm_get_packets_sent(
752     struct _lm_device_t *pdev,
753     u32_t qidx,
754     u32_t con_idx,
755     s_list_t *sent_list)
756 {
757     lm_tx_chain_t *txq;
758     u16_t hw_con_idx;
759     u32_t pkts_added;
760     u32_t pkt_cnt;
761 
762     txq = &pdev->tx_info.chain[qidx];
763 
764     if(con_idx)
765     {
766         hw_con_idx = con_idx & 0xffff;
767 
768         pkt_cnt = get_packets_sent(pdev, txq, hw_con_idx, sent_list);
769     }
770     else
771     {
772         pkt_cnt = 0;
773 
774         for(; ;)
775         {
776             hw_con_idx = *txq->hw_con_idx_ptr;
777 
778             pkts_added = get_packets_sent(pdev, txq, hw_con_idx, sent_list);
779             if(pkts_added == 0)
780             {
781                 break;
782             }
783 
784             pkt_cnt += pkts_added;
785         }
786     }
787 
788     return pkt_cnt;
789 } /* lm_get_packets_sent */
790 
791 
792 
793 /*******************************************************************************
794  * Description:
795  *
796  * Return:
797  ******************************************************************************/
798 void
799 lm_service_tx_int(
800     lm_device_t *pdev,
801     u32_t chain_idx)
802 {
803     lm_packet_t *pkt_arr[MAX_PACKETS_PER_INDICATION];
804     lm_packet_t **pkt_arr_ptr;
805     s_list_t sent_list;
806     lm_packet_t *pkt;
807     u32_t pkt_cnt;
808 
809     s_list_init(&sent_list, NULL, NULL, 0);
810 
811     (void) lm_get_packets_sent(pdev, chain_idx, 0, &sent_list);
812 
813     while(!s_list_is_empty(&sent_list))
814     {
815         pkt_arr_ptr = pkt_arr;
816 
817         for(pkt_cnt = 0; pkt_cnt < MAX_PACKETS_PER_INDICATION; pkt_cnt++)
818         {
819             pkt = (lm_packet_t *) s_list_pop_head(&sent_list);
820             if(pkt == NULL)
821             {
822                 break;
823             }
824 
825             *pkt_arr_ptr = pkt;
826             pkt_arr_ptr++;
827         }
828 
829         mm_indicate_tx(pdev, chain_idx, pkt_arr, pkt_cnt);
830     }
831 } /* lm_service_tx_int */
832 
833 
834 
835 /*******************************************************************************
836  * Description:
837  *
838  * Return:
839  ******************************************************************************/
840 void
841 lm_send_abort(
842     struct _lm_device_t *pdev,
843     u32_t idx)
844 {
845     lm_tx_chain_t *txq;
846     lm_packet_t *pkt;
847 
848     DbgBreakIf(idx >= pdev->tx_info.num_txq);
849 
850     txq = &pdev->tx_info.chain[idx];
851 
852     for(; ;)
853     {
854         pkt = (lm_packet_t *) s_list_pop_head(&txq->active_descq);
855         if(pkt == NULL)
856         {
857             break;
858         }
859 
860         pkt->status = LM_STATUS_ABORTED;
861         pdev->tx_info.stats.aborted++;
862         txq->bd_left += pkt->u1.tx.bd_used;
863 
864         mm_indicate_tx(pdev, idx, &pkt, 1);
865     }
866 
867     DbgBreakIf(txq->bd_left !=
868         pdev->params.l2_tx_bd_page_cnt[txq->idx] * MAX_BD_PER_PAGE - 1);
869 } /* lm_send_abort */
870