1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(C) 2021 Marvell. 3 */ 4 #include "roc_api.h" 5 #include "roc_priv.h" 6 7 const struct roc_npc_item_info * 8 npc_parse_skip_void_and_any_items(const struct roc_npc_item_info *pattern) 9 { 10 while ((pattern->type == ROC_NPC_ITEM_TYPE_VOID) || 11 (pattern->type == ROC_NPC_ITEM_TYPE_ANY)) 12 pattern++; 13 14 return pattern; 15 } 16 17 int 18 npc_parse_meta_items(struct npc_parse_state *pst) 19 { 20 PLT_SET_USED(pst); 21 return 0; 22 } 23 24 int 25 npc_parse_cpt_hdr(struct npc_parse_state *pst) 26 { 27 uint8_t hw_mask[NPC_MAX_EXTRACT_HW_LEN]; 28 struct npc_parse_item_info info; 29 int lid, lt; 30 int rc; 31 32 /* Identify the pattern type into lid, lt */ 33 if (pst->pattern->type != ROC_NPC_ITEM_TYPE_CPT_HDR) 34 return 0; 35 36 lid = NPC_LID_LA; 37 lt = NPC_LT_LA_CPT_HDR; 38 info.hw_hdr_len = 0; 39 40 /* Prepare for parsing the item */ 41 info.hw_mask = &hw_mask; 42 info.len = pst->pattern->size; 43 npc_get_hw_supp_mask(pst, &info, lid, lt); 44 info.spec = NULL; 45 info.mask = NULL; 46 47 /* Basic validation of item parameters */ 48 rc = npc_parse_item_basic(pst->pattern, &info); 49 if (rc) 50 return rc; 51 52 /* Update pst if not validate only? clash check? */ 53 return npc_update_parse_state(pst, &info, lid, lt, 0); 54 } 55 56 int 57 npc_parse_higig2_hdr(struct npc_parse_state *pst) 58 { 59 uint8_t hw_mask[NPC_MAX_EXTRACT_HW_LEN]; 60 struct npc_parse_item_info info; 61 int lid, lt; 62 int rc; 63 64 /* Identify the pattern type into lid, lt */ 65 if (pst->pattern->type != ROC_NPC_ITEM_TYPE_HIGIG2) 66 return 0; 67 68 lid = NPC_LID_LA; 69 lt = NPC_LT_LA_HIGIG2_ETHER; 70 info.hw_hdr_len = 0; 71 72 if (pst->flow->nix_intf == NIX_INTF_TX) { 73 lt = NPC_LT_LA_IH_NIX_HIGIG2_ETHER; 74 info.hw_hdr_len = NPC_IH_LENGTH; 75 } 76 77 /* Prepare for parsing the item */ 78 info.hw_mask = &hw_mask; 79 info.len = pst->pattern->size; 80 npc_get_hw_supp_mask(pst, &info, lid, lt); 81 info.spec = NULL; 82 info.mask = NULL; 83 84 /* Basic validation of item parameters */ 85 rc = npc_parse_item_basic(pst->pattern, &info); 86 if (rc) 87 return rc; 88 89 /* Update pst if not validate only? clash check? */ 90 return npc_update_parse_state(pst, &info, lid, lt, 0); 91 } 92 93 int 94 npc_parse_la(struct npc_parse_state *pst) 95 { 96 uint8_t hw_mask[NPC_MAX_EXTRACT_HW_LEN]; 97 struct npc_parse_item_info info; 98 int lid, lt; 99 int rc; 100 101 /* Identify the pattern type into lid, lt */ 102 if (pst->pattern->type != ROC_NPC_ITEM_TYPE_ETH) 103 return 0; 104 105 lid = NPC_LID_LA; 106 lt = NPC_LT_LA_ETHER; 107 info.hw_hdr_len = 0; 108 109 if (pst->flow->nix_intf == NIX_INTF_TX) { 110 lt = NPC_LT_LA_IH_NIX_ETHER; 111 info.hw_hdr_len = NPC_IH_LENGTH; 112 if (pst->npc->switch_header_type == ROC_PRIV_FLAGS_HIGIG) { 113 lt = NPC_LT_LA_IH_NIX_HIGIG2_ETHER; 114 info.hw_hdr_len += NPC_HIGIG2_LENGTH; 115 } 116 } else { 117 if (pst->npc->switch_header_type == ROC_PRIV_FLAGS_HIGIG) { 118 lt = NPC_LT_LA_HIGIG2_ETHER; 119 info.hw_hdr_len = NPC_HIGIG2_LENGTH; 120 } 121 } 122 123 /* Prepare for parsing the item */ 124 info.hw_mask = &hw_mask; 125 info.len = pst->pattern->size; 126 npc_get_hw_supp_mask(pst, &info, lid, lt); 127 info.spec = NULL; 128 info.mask = NULL; 129 130 /* Basic validation of item parameters */ 131 rc = npc_parse_item_basic(pst->pattern, &info); 132 if (rc) 133 return rc; 134 135 /* Update pst if not validate only? clash check? */ 136 return npc_update_parse_state(pst, &info, lid, lt, 0); 137 } 138 139 int 140 npc_parse_lb(struct npc_parse_state *pst) 141 { 142 const struct roc_npc_item_info *pattern = pst->pattern; 143 const struct roc_npc_item_info *last_pattern; 144 char hw_mask[NPC_MAX_EXTRACT_HW_LEN]; 145 struct npc_parse_item_info info; 146 int lid, lt, lflags; 147 int nr_vlans = 0; 148 int rc; 149 150 info.spec = NULL; 151 info.mask = NULL; 152 info.def_mask = NULL; 153 info.hw_hdr_len = NPC_TPID_LENGTH; 154 155 lid = NPC_LID_LB; 156 lflags = 0; 157 last_pattern = pattern; 158 159 if (pst->pattern->type == ROC_NPC_ITEM_TYPE_VLAN) { 160 /* RTE vlan is either 802.1q or 802.1ad, 161 * this maps to either CTAG/STAG. We need to decide 162 * based on number of VLANS present. Matching is 163 * supported on first tag only. 164 */ 165 info.hw_mask = NULL; 166 info.len = pst->pattern->size; 167 168 pattern = pst->pattern; 169 while (pattern->type == ROC_NPC_ITEM_TYPE_VLAN) { 170 nr_vlans++; 171 172 /* Basic validation of Second/Third vlan item */ 173 if (nr_vlans > 1) { 174 rc = npc_parse_item_basic(pattern, &info); 175 if (rc != 0) 176 return rc; 177 } 178 last_pattern = pattern; 179 pattern++; 180 pattern = npc_parse_skip_void_and_any_items(pattern); 181 } 182 183 switch (nr_vlans) { 184 case 1: 185 lt = NPC_LT_LB_CTAG; 186 break; 187 case 2: 188 lt = NPC_LT_LB_STAG_QINQ; 189 lflags = NPC_F_STAG_CTAG; 190 break; 191 case 3: 192 lt = NPC_LT_LB_STAG_QINQ; 193 lflags = NPC_F_STAG_STAG_CTAG; 194 break; 195 default: 196 return NPC_ERR_PATTERN_NOTSUP; 197 } 198 } else if (pst->pattern->type == ROC_NPC_ITEM_TYPE_E_TAG) { 199 /* we can support ETAG and match a subsequent CTAG 200 * without any matching support. 201 */ 202 lt = NPC_LT_LB_ETAG; 203 lflags = 0; 204 205 last_pattern = pst->pattern; 206 pattern = npc_parse_skip_void_and_any_items(pst->pattern + 1); 207 if (pattern->type == ROC_NPC_ITEM_TYPE_VLAN) { 208 /* set supported mask to NULL for vlan tag */ 209 info.hw_mask = NULL; 210 info.len = pattern->size; 211 rc = npc_parse_item_basic(pattern, &info); 212 if (rc != 0) 213 return rc; 214 215 lflags = NPC_F_ETAG_CTAG; 216 last_pattern = pattern; 217 } 218 info.len = pattern->size; 219 } else if (pst->pattern->type == ROC_NPC_ITEM_TYPE_QINQ) { 220 info.hw_mask = NULL; 221 info.len = pst->pattern->size; 222 lt = NPC_LT_LB_STAG_QINQ; 223 lflags = NPC_F_STAG_CTAG; 224 } else { 225 return 0; 226 } 227 228 info.hw_mask = &hw_mask; 229 info.spec = NULL; 230 info.mask = NULL; 231 npc_get_hw_supp_mask(pst, &info, lid, lt); 232 233 rc = npc_parse_item_basic(pst->pattern, &info); 234 if (rc != 0) 235 return rc; 236 237 /* Point pattern to last item consumed */ 238 pst->pattern = last_pattern; 239 return npc_update_parse_state(pst, &info, lid, lt, lflags); 240 } 241 242 static int 243 npc_parse_mpls_label_stack(struct npc_parse_state *pst, int *flag) 244 { 245 uint8_t flag_list[] = {0, NPC_F_MPLS_2_LABELS, NPC_F_MPLS_3_LABELS, 246 NPC_F_MPLS_4_LABELS}; 247 const struct roc_npc_item_info *pattern = pst->pattern; 248 struct npc_parse_item_info info; 249 int nr_labels = 0; 250 int rc; 251 252 /* 253 * pst->pattern points to first MPLS label. We only check 254 * that subsequent labels do not have anything to match. 255 */ 256 info.hw_mask = NULL; 257 info.len = pattern->size; 258 info.spec = NULL; 259 info.mask = NULL; 260 info.hw_hdr_len = 0; 261 info.def_mask = NULL; 262 263 while (pattern->type == ROC_NPC_ITEM_TYPE_MPLS) { 264 nr_labels++; 265 266 /* Basic validation of Second/Third/Fourth mpls item */ 267 if (nr_labels > 1) { 268 rc = npc_parse_item_basic(pattern, &info); 269 if (rc != 0) 270 return rc; 271 } 272 pst->last_pattern = pattern; 273 pattern++; 274 pattern = npc_parse_skip_void_and_any_items(pattern); 275 } 276 277 if (nr_labels < 1 || nr_labels > 4) 278 return NPC_ERR_PATTERN_NOTSUP; 279 280 *flag = flag_list[nr_labels - 1]; 281 return 0; 282 } 283 284 static int 285 npc_parse_mpls(struct npc_parse_state *pst, int lid) 286 { 287 /* Find number of MPLS labels */ 288 uint8_t hw_mask[NPC_MAX_EXTRACT_HW_LEN]; 289 struct npc_parse_item_info info; 290 int lt, lflags; 291 int rc; 292 293 lflags = 0; 294 295 if (lid == NPC_LID_LC) 296 lt = NPC_LT_LC_MPLS; 297 else if (lid == NPC_LID_LD) 298 lt = NPC_LT_LD_TU_MPLS_IN_IP; 299 else 300 lt = NPC_LT_LE_TU_MPLS_IN_UDP; 301 302 /* Prepare for parsing the first item */ 303 info.hw_mask = &hw_mask; 304 info.len = pst->pattern->size; 305 info.spec = NULL; 306 info.mask = NULL; 307 info.hw_hdr_len = 0; 308 309 npc_get_hw_supp_mask(pst, &info, lid, lt); 310 rc = npc_parse_item_basic(pst->pattern, &info); 311 if (rc != 0) 312 return rc; 313 314 /* 315 * Parse for more labels. 316 * This sets lflags and pst->last_pattern correctly. 317 */ 318 rc = npc_parse_mpls_label_stack(pst, &lflags); 319 if (rc != 0) 320 return rc; 321 322 pst->tunnel = 1; 323 pst->pattern = pst->last_pattern; 324 325 return npc_update_parse_state(pst, &info, lid, lt, lflags); 326 } 327 328 static inline void 329 npc_check_lc_ip_tunnel(struct npc_parse_state *pst) 330 { 331 const struct roc_npc_item_info *pattern = pst->pattern + 1; 332 333 pattern = npc_parse_skip_void_and_any_items(pattern); 334 if (pattern->type == ROC_NPC_ITEM_TYPE_MPLS || 335 pattern->type == ROC_NPC_ITEM_TYPE_IPV4 || 336 pattern->type == ROC_NPC_ITEM_TYPE_IPV6) 337 pst->tunnel = 1; 338 } 339 340 int 341 npc_parse_lc(struct npc_parse_state *pst) 342 { 343 uint8_t hw_mask[NPC_MAX_EXTRACT_HW_LEN]; 344 struct npc_parse_item_info info; 345 int lid, lt; 346 int rc; 347 348 if (pst->pattern->type == ROC_NPC_ITEM_TYPE_MPLS) 349 return npc_parse_mpls(pst, NPC_LID_LC); 350 351 info.hw_mask = &hw_mask; 352 info.spec = NULL; 353 info.mask = NULL; 354 info.hw_hdr_len = 0; 355 lid = NPC_LID_LC; 356 357 switch (pst->pattern->type) { 358 case ROC_NPC_ITEM_TYPE_IPV4: 359 lt = NPC_LT_LC_IP; 360 info.len = pst->pattern->size; 361 break; 362 case ROC_NPC_ITEM_TYPE_IPV6: 363 lid = NPC_LID_LC; 364 lt = NPC_LT_LC_IP6; 365 info.len = pst->pattern->size; 366 break; 367 case ROC_NPC_ITEM_TYPE_ARP_ETH_IPV4: 368 lt = NPC_LT_LC_ARP; 369 info.len = pst->pattern->size; 370 break; 371 case ROC_NPC_ITEM_TYPE_IPV6_EXT: 372 lid = NPC_LID_LC; 373 lt = NPC_LT_LC_IP6_EXT; 374 info.len = pst->pattern->size; 375 info.hw_hdr_len = 40; 376 break; 377 case ROC_NPC_ITEM_TYPE_L3_CUSTOM: 378 lt = NPC_LT_LC_CUSTOM0; 379 info.len = pst->pattern->size; 380 break; 381 default: 382 /* No match at this layer */ 383 return 0; 384 } 385 386 /* Identify if IP tunnels MPLS or IPv4/v6 */ 387 npc_check_lc_ip_tunnel(pst); 388 389 npc_get_hw_supp_mask(pst, &info, lid, lt); 390 rc = npc_parse_item_basic(pst->pattern, &info); 391 if (rc != 0) 392 return rc; 393 394 return npc_update_parse_state(pst, &info, lid, lt, 0); 395 } 396 397 int 398 npc_parse_ld(struct npc_parse_state *pst) 399 { 400 char hw_mask[NPC_MAX_EXTRACT_HW_LEN]; 401 struct npc_parse_item_info info; 402 int lid, lt, lflags; 403 int rc; 404 405 if (pst->tunnel) { 406 /* We have already parsed MPLS or IPv4/v6 followed 407 * by MPLS or IPv4/v6. Subsequent TCP/UDP etc 408 * would be parsed as tunneled versions. Skip 409 * this layer, except for tunneled MPLS. If LC is 410 * MPLS, we have anyway skipped all stacked MPLS 411 * labels. 412 */ 413 if (pst->pattern->type == ROC_NPC_ITEM_TYPE_MPLS) 414 return npc_parse_mpls(pst, NPC_LID_LD); 415 return 0; 416 } 417 info.hw_mask = &hw_mask; 418 info.spec = NULL; 419 info.mask = NULL; 420 info.def_mask = NULL; 421 info.len = 0; 422 info.hw_hdr_len = 0; 423 424 lid = NPC_LID_LD; 425 lflags = 0; 426 427 switch (pst->pattern->type) { 428 case ROC_NPC_ITEM_TYPE_ICMP: 429 if (pst->lt[NPC_LID_LC] == NPC_LT_LC_IP6) 430 lt = NPC_LT_LD_ICMP6; 431 else 432 lt = NPC_LT_LD_ICMP; 433 info.len = pst->pattern->size; 434 break; 435 case ROC_NPC_ITEM_TYPE_UDP: 436 lt = NPC_LT_LD_UDP; 437 info.len = pst->pattern->size; 438 break; 439 case ROC_NPC_ITEM_TYPE_IGMP: 440 lt = NPC_LT_LD_IGMP; 441 info.len = pst->pattern->size; 442 break; 443 case ROC_NPC_ITEM_TYPE_TCP: 444 lt = NPC_LT_LD_TCP; 445 info.len = pst->pattern->size; 446 break; 447 case ROC_NPC_ITEM_TYPE_SCTP: 448 lt = NPC_LT_LD_SCTP; 449 info.len = pst->pattern->size; 450 break; 451 case ROC_NPC_ITEM_TYPE_GRE: 452 lt = NPC_LT_LD_GRE; 453 info.len = pst->pattern->size; 454 break; 455 case ROC_NPC_ITEM_TYPE_GRE_KEY: 456 lt = NPC_LT_LD_GRE; 457 info.len = pst->pattern->size; 458 info.hw_hdr_len = 4; 459 break; 460 case ROC_NPC_ITEM_TYPE_NVGRE: 461 lt = NPC_LT_LD_NVGRE; 462 lflags = NPC_F_GRE_NVGRE; 463 info.len = pst->pattern->size; 464 /* Further IP/Ethernet are parsed as tunneled */ 465 pst->tunnel = 1; 466 break; 467 default: 468 return 0; 469 } 470 471 npc_get_hw_supp_mask(pst, &info, lid, lt); 472 rc = npc_parse_item_basic(pst->pattern, &info); 473 if (rc != 0) 474 return rc; 475 476 return npc_update_parse_state(pst, &info, lid, lt, lflags); 477 } 478 479 int 480 npc_parse_le(struct npc_parse_state *pst) 481 { 482 const struct roc_npc_item_info *pattern = pst->pattern; 483 char hw_mask[NPC_MAX_EXTRACT_HW_LEN]; 484 struct npc_parse_item_info info; 485 int lid, lt, lflags; 486 int rc; 487 488 if (pst->tunnel) 489 return 0; 490 491 if (pst->pattern->type == ROC_NPC_ITEM_TYPE_MPLS) 492 return npc_parse_mpls(pst, NPC_LID_LE); 493 494 info.spec = NULL; 495 info.mask = NULL; 496 info.hw_mask = NULL; 497 info.def_mask = NULL; 498 info.len = 0; 499 info.hw_hdr_len = 0; 500 lid = NPC_LID_LE; 501 lflags = 0; 502 503 /* Ensure we are not matching anything in UDP */ 504 rc = npc_parse_item_basic(pattern, &info); 505 if (rc) 506 return rc; 507 508 info.hw_mask = &hw_mask; 509 pattern = npc_parse_skip_void_and_any_items(pattern); 510 switch (pattern->type) { 511 case ROC_NPC_ITEM_TYPE_VXLAN: 512 lflags = NPC_F_UDP_VXLAN; 513 info.len = pattern->size; 514 lt = NPC_LT_LE_VXLAN; 515 break; 516 case ROC_NPC_ITEM_TYPE_GTPC: 517 lflags = NPC_F_UDP_GTP_GTPC; 518 info.len = pattern->size; 519 lt = NPC_LT_LE_GTPC; 520 break; 521 case ROC_NPC_ITEM_TYPE_GTPU: 522 lflags = NPC_F_UDP_GTP_GTPU_G_PDU; 523 info.len = pattern->size; 524 lt = NPC_LT_LE_GTPU; 525 break; 526 case ROC_NPC_ITEM_TYPE_GENEVE: 527 lflags = NPC_F_UDP_GENEVE; 528 info.len = pattern->size; 529 lt = NPC_LT_LE_GENEVE; 530 break; 531 case ROC_NPC_ITEM_TYPE_VXLAN_GPE: 532 lflags = NPC_F_UDP_VXLANGPE; 533 info.len = pattern->size; 534 lt = NPC_LT_LE_VXLANGPE; 535 break; 536 case ROC_NPC_ITEM_TYPE_ESP: 537 lt = NPC_LT_LE_ESP; 538 info.len = pst->pattern->size; 539 break; 540 default: 541 return 0; 542 } 543 544 pst->tunnel = 1; 545 546 npc_get_hw_supp_mask(pst, &info, lid, lt); 547 rc = npc_parse_item_basic(pattern, &info); 548 if (rc != 0) 549 return rc; 550 551 return npc_update_parse_state(pst, &info, lid, lt, lflags); 552 } 553 554 int 555 npc_parse_lf(struct npc_parse_state *pst) 556 { 557 const struct roc_npc_item_info *pattern, *last_pattern; 558 char hw_mask[NPC_MAX_EXTRACT_HW_LEN]; 559 struct npc_parse_item_info info; 560 int lid, lt, lflags; 561 int nr_vlans = 0; 562 int rc; 563 564 /* We hit this layer if there is a tunneling protocol */ 565 if (!pst->tunnel) 566 return 0; 567 568 if (pst->pattern->type != ROC_NPC_ITEM_TYPE_ETH) 569 return 0; 570 571 lid = NPC_LID_LF; 572 lt = NPC_LT_LF_TU_ETHER; 573 lflags = 0; 574 575 /* No match support for vlan tags */ 576 info.hw_mask = NULL; 577 info.len = pst->pattern->size; 578 info.spec = NULL; 579 info.mask = NULL; 580 info.hw_hdr_len = 0; 581 582 /* Look ahead and find out any VLAN tags. These can be 583 * detected but no data matching is available. 584 */ 585 last_pattern = pst->pattern; 586 pattern = pst->pattern + 1; 587 pattern = npc_parse_skip_void_and_any_items(pattern); 588 while (pattern->type == ROC_NPC_ITEM_TYPE_VLAN) { 589 nr_vlans++; 590 last_pattern = pattern; 591 pattern++; 592 pattern = npc_parse_skip_void_and_any_items(pattern); 593 } 594 switch (nr_vlans) { 595 case 0: 596 break; 597 case 1: 598 lflags = NPC_F_TU_ETHER_CTAG; 599 break; 600 case 2: 601 lflags = NPC_F_TU_ETHER_STAG_CTAG; 602 break; 603 default: 604 return NPC_ERR_PATTERN_NOTSUP; 605 } 606 607 info.hw_mask = &hw_mask; 608 info.len = pst->pattern->size; 609 info.hw_hdr_len = 0; 610 npc_get_hw_supp_mask(pst, &info, lid, lt); 611 info.spec = NULL; 612 info.mask = NULL; 613 614 rc = npc_parse_item_basic(pst->pattern, &info); 615 if (rc != 0) 616 return rc; 617 618 pst->pattern = last_pattern; 619 620 return npc_update_parse_state(pst, &info, lid, lt, lflags); 621 } 622 623 int 624 npc_parse_lg(struct npc_parse_state *pst) 625 { 626 char hw_mask[NPC_MAX_EXTRACT_HW_LEN]; 627 struct npc_parse_item_info info; 628 int lid, lt; 629 int rc; 630 631 if (!pst->tunnel) 632 return 0; 633 634 info.hw_mask = &hw_mask; 635 info.spec = NULL; 636 info.mask = NULL; 637 info.hw_hdr_len = 0; 638 lid = NPC_LID_LG; 639 640 if (pst->pattern->type == ROC_NPC_ITEM_TYPE_IPV4) { 641 lt = NPC_LT_LG_TU_IP; 642 info.len = pst->pattern->size; 643 } else if (pst->pattern->type == ROC_NPC_ITEM_TYPE_IPV6) { 644 lt = NPC_LT_LG_TU_IP6; 645 info.len = pst->pattern->size; 646 } else { 647 /* There is no tunneled IP header */ 648 return 0; 649 } 650 651 npc_get_hw_supp_mask(pst, &info, lid, lt); 652 rc = npc_parse_item_basic(pst->pattern, &info); 653 if (rc != 0) 654 return rc; 655 656 return npc_update_parse_state(pst, &info, lid, lt, 0); 657 } 658 659 int 660 npc_parse_lh(struct npc_parse_state *pst) 661 { 662 char hw_mask[NPC_MAX_EXTRACT_HW_LEN]; 663 struct npc_parse_item_info info; 664 int lid, lt; 665 int rc; 666 667 if (!pst->tunnel) 668 return 0; 669 670 info.hw_mask = &hw_mask; 671 info.spec = NULL; 672 info.mask = NULL; 673 info.hw_hdr_len = 0; 674 lid = NPC_LID_LH; 675 676 switch (pst->pattern->type) { 677 case ROC_NPC_ITEM_TYPE_UDP: 678 lt = NPC_LT_LH_TU_UDP; 679 info.len = pst->pattern->size; 680 break; 681 case ROC_NPC_ITEM_TYPE_TCP: 682 lt = NPC_LT_LH_TU_TCP; 683 info.len = pst->pattern->size; 684 break; 685 case ROC_NPC_ITEM_TYPE_SCTP: 686 lt = NPC_LT_LH_TU_SCTP; 687 info.len = pst->pattern->size; 688 break; 689 case ROC_NPC_ITEM_TYPE_ESP: 690 lt = NPC_LT_LH_TU_ESP; 691 info.len = pst->pattern->size; 692 break; 693 default: 694 return 0; 695 } 696 697 npc_get_hw_supp_mask(pst, &info, lid, lt); 698 rc = npc_parse_item_basic(pst->pattern, &info); 699 if (rc != 0) 700 return rc; 701 702 return npc_update_parse_state(pst, &info, lid, lt, 0); 703 } 704