| 1 | /* |
| 2 | * |
| 3 | * =================================== |
| 4 | * HARP | Host ATM Research Platform |
| 5 | * =================================== |
| 6 | * |
| 7 | * |
| 8 | * This Host ATM Research Platform ("HARP") file (the "Software") is |
| 9 | * made available by Network Computing Services, Inc. ("NetworkCS") |
| 10 | * "AS IS". NetworkCS does not provide maintenance, improvements or |
| 11 | * support of any kind. |
| 12 | * |
| 13 | * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED, |
| 14 | * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY |
| 15 | * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE |
| 16 | * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE. |
| 17 | * In no event shall NetworkCS be responsible for any damages, including |
| 18 | * but not limited to consequential damages, arising from or relating to |
| 19 | * any use of the Software or related support. |
| 20 | * |
| 21 | * Copyright 1994-1998 Network Computing Services, Inc. |
| 22 | * |
| 23 | * Copies of this Software may be made, however, the above copyright |
| 24 | * notice must be reproduced on all copies. |
| 25 | * |
| 26 | * @(#) $FreeBSD: src/sys/dev/hea/eni_receive.c,v 1.5 1999/08/28 00:41:45 peter Exp $ |
| 27 | * @(#) $DragonFly: src/sys/dev/atm/hea/eni_receive.c,v 1.5 2003/08/27 10:35:15 rob Exp $ |
| 28 | */ |
| 29 | |
| 30 | /* |
| 31 | * Efficient ENI Adapter Support |
| 32 | * ----------------------------- |
| 33 | * |
| 34 | * Receive management |
| 35 | * |
| 36 | */ |
| 37 | |
| 38 | #include <netproto/atm/kern_include.h> |
| 39 | |
| 40 | #include "eni_stats.h" |
| 41 | #include "eni.h" |
| 42 | #include "eni_var.h" |
| 43 | |
| 44 | static void eni_recv_stack (void *, KBuffer *); |
| 45 | |
| 46 | #ifdef DIAGNOSTIC |
| 47 | extern int eni_pdu_print; |
| 48 | #endif |
| 49 | |
| 50 | /* |
| 51 | * Procedure to remove VCs from the Service List and generate DMA |
| 52 | * requests to move the associated PDUs into host memory. As PDUs |
| 53 | * are completed in adapter memory, the adapter examines the IN_SERVICE |
| 54 | * bit for the VC in the VC table. If this bit is not set, the adapter |
| 55 | * will place the VC number at the end of the service list queue, set |
| 56 | * the IN_SERVICE bit in the VC table, and interrupt the host. The host |
| 57 | * will remove VCs from the service list, clear the IN_SERVICE bit in |
| 58 | * the VC table, and create a DMA list to move the PDU into host buffers. |
| 59 | * |
| 60 | * Arguments: |
| 61 | * eup pointer to per unit structure |
| 62 | * |
| 63 | * Returns: |
| 64 | * none |
| 65 | * |
| 66 | */ |
| 67 | void |
| 68 | eni_do_service ( eup ) |
| 69 | Eni_unit *eup; |
| 70 | { |
| 71 | int vcc; |
| 72 | Eni_vcc *evp; |
| 73 | u_long servwrite; |
| 74 | VCI_Table *vct; |
| 75 | u_long rdptr; |
| 76 | u_long *rxp; |
| 77 | KBuffer *m; |
| 78 | u_long dma[TEMP_DMA_SIZE]; |
| 79 | u_long i, j; |
| 80 | u_long dma_rd, dma_wr; |
| 81 | u_long dma_avail; |
| 82 | int pdulen; |
| 83 | int mask; |
| 84 | u_long *upp; |
| 85 | |
| 86 | /* |
| 87 | * Where is the adapter currently inserting entries? |
| 88 | */ |
| 89 | servwrite = eup->eu_midway[MIDWAY_SVCWR] & SVC_SIZE_MASK; |
| 90 | /* |
| 91 | * As long as we're not caught up with the adapter, keep |
| 92 | * removing VCs from the service list. |
| 93 | */ |
| 94 | while ( servwrite != eup->eu_servread ) { |
| 95 | int vci_hdr; |
| 96 | u_long descr; |
| 97 | |
| 98 | /* |
| 99 | * Get VC number and find VC table entry. |
| 100 | */ |
| 101 | vcc = eup->eu_svclist[eup->eu_servread]; |
| 102 | vct = &eup->eu_vcitbl[vcc]; |
| 103 | vci_hdr = vct->vci_control; /* Current status */ |
| 104 | |
| 105 | /* |
| 106 | * Check that this VCC still needs servicing. We |
| 107 | * might have closed this VCC down in between |
| 108 | * the adapter setting the flag and our checking |
| 109 | * the flag. Also check that we haven't placed the |
| 110 | * VCC into TRASH mode. |
| 111 | */ |
| 112 | if ( ( vci_hdr & VCI_IN_SERVICE ) == 0 || |
| 113 | ( (vci_hdr & ~VCI_MODE_MASK) == |
| 114 | (VCI_MODE_TRASH << VCI_MODE_SHIFT) ) ) |
| 115 | goto next_vcc; |
| 116 | |
| 117 | /* |
| 118 | * Find the size of this VCs buffer |
| 119 | */ |
| 120 | mask = (vci_hdr >> VCI_SIZE_SHIFT) & VCI_SIZE_MASK; |
| 121 | mask = 1 << (ENI_LOC_PREDIV + mask); |
| 122 | /* Turn byte count into word count */ |
| 123 | mask >>= 2; |
| 124 | /* |
| 125 | * Find the start of the adapter buffer for this VC. |
| 126 | */ |
| 127 | rxp = (u_long *) |
| 128 | ((int)(((vci_hdr >> VCI_LOC_SHIFT ) & VCI_LOC_MASK) |
| 129 | << ENI_LOC_PREDIV) + (int)eup->eu_ram); |
| 130 | /* |
| 131 | * Locate incoming VCC for this PDU and find where we |
| 132 | * should next read from. |
| 133 | */ |
| 134 | evp = (Eni_vcc *) atm_dev_vcc_find ( (Cmn_unit *)eup, |
| 135 | 0, vcc, VCC_IN ); |
| 136 | if ( evp == (Eni_vcc *)NULL ) |
| 137 | goto next_vcc; /* VCI no longer active */ |
| 138 | rdptr = evp->ev_rxpos; |
| 139 | /* |
| 140 | * Find out where the adapter is currently reassembling. |
| 141 | * The PDU which starts at descr is not yet complete so we |
| 142 | * must stop there. |
| 143 | */ |
| 144 | descr = ( vct->vci_descr >> 16 ) & 0x7FFF; |
| 145 | /* |
| 146 | * As long as we haven't processed all the completed PDUs on |
| 147 | * this VC, keep going... |
| 148 | */ |
| 149 | while ( rdptr != descr ) |
| 150 | { |
| 151 | int n_cells; |
| 152 | int pdu_descr; |
| 153 | int aal5; |
| 154 | |
| 155 | /* |
| 156 | * Ensure that the following are reset for every new |
| 157 | * PDU. |
| 158 | */ |
| 159 | upp = NULL; |
| 160 | m = NULL; |
| 161 | |
| 162 | /* |
| 163 | * Fisrt build a DMA with JK to skip the descriptor word. |
| 164 | * We must always skip the descriptor even if it turns out |
| 165 | * that there isn't any PDU here. |
| 166 | */ |
| 167 | j = 0; |
| 168 | dma[j++] = (((rdptr + 1) & (mask-1)) << DMA_COUNT_SHIFT ) | |
| 169 | ( vcc << DMA_VCC_SHIFT ) | DMA_JK; |
| 170 | dma[j++] = 0; |
| 171 | |
| 172 | /* |
| 173 | * We'll use some of the values below for skipping |
| 174 | * bad PDUs or counting statistics so compute them |
| 175 | * now. |
| 176 | */ |
| 177 | |
| 178 | /* |
| 179 | * Grab a copy of the descriptor word |
| 180 | */ |
| 181 | pdu_descr = rxp[rdptr]; |
| 182 | |
| 183 | /* |
| 184 | * Strip out cell count from descriptor word. |
| 185 | * At this point, we still don't know if there |
| 186 | * is any real data until after we check for |
| 187 | * TRASH mode. |
| 188 | */ |
| 189 | n_cells = pdu_descr & DESCR_CELL_COUNT; |
| 190 | |
| 191 | /* |
| 192 | * Is this an AAL5 PDU? Check MODE in vci_hdr. |
| 193 | */ |
| 194 | aal5 = ( ( vci_hdr & ~VCI_MODE_MASK ) == |
| 195 | VCI_MODE_AAL5 << VCI_MODE_SHIFT ); |
| 196 | |
| 197 | /* |
| 198 | * Now check to see if we're trashing on this vcc. |
| 199 | * If so, there is no data with this VC and the |
| 200 | * next word after the current descriptor is the |
| 201 | * descriptor for the next PDU. |
| 202 | */ |
| 203 | if ( ( pdu_descr & DESCR_TRASH_BIT ) != 0 ) { |
| 204 | if ( aal5 ) |
| 205 | /* |
| 206 | * Count as number of AAL5 cells dropped |
| 207 | */ |
| 208 | eup->eu_stats.eni_st_aal5.aal5_drops += n_cells; |
| 209 | else |
| 210 | /* |
| 211 | * Count as number of AAL0 cells dropped |
| 212 | */ |
| 213 | eup->eu_stats.eni_st_aal0.aal0_drops += n_cells; |
| 214 | eup->eu_pif.pif_ierrors++; |
| 215 | /* |
| 216 | * When cells have been trashed, all we have in the |
| 217 | * buffer is a descriptor word. There are no data |
| 218 | * words. Set the number of cells to zero so that |
| 219 | * we correctly skip to the next word which will |
| 220 | * be the descriptor for the next PDU. |
| 221 | */ |
| 222 | n_cells = 0; |
| 223 | /* |
| 224 | * Go issue the DMA to skip this descriptor word. |
| 225 | */ |
| 226 | goto send_dma; |
| 227 | } |
| 228 | |
| 229 | /* |
| 230 | * Data length: number of cells * cell size |
| 231 | */ |
| 232 | pdulen = n_cells * BYTES_PER_CELL; |
| 233 | |
| 234 | /* |
| 235 | * If this is an AAL5 PDU, then we need to check |
| 236 | * for the presence of any CRC errors. If there |
| 237 | * is one or more CRC errors, then we are going to |
| 238 | * drop this PDU. |
| 239 | */ |
| 240 | if ( aal5 && ( pdu_descr & DESCR_CRC_ERR ) ) { |
| 241 | /* |
| 242 | * Count the stat |
| 243 | */ |
| 244 | eup->eu_pif.pif_ierrors++; |
| 245 | eup->eu_stats.eni_st_aal5.aal5_pdu_crc++; |
| 246 | if ( evp->ev_connvc->cvc_vcc ) |
| 247 | evp->ev_connvc->cvc_vcc->vc_ierrors++; |
| 248 | /* |
| 249 | * Build a DMA entry to skip the rest of this |
| 250 | * PDU. |
| 251 | */ |
| 252 | dma[j++] = |
| 253 | (((rdptr + n_cells*WORDS_PER_CELL + 1) |
| 254 | & (mask-1)) << DMA_COUNT_SHIFT ) | |
| 255 | (vcc << DMA_VCC_SHIFT ) | DMA_JK; |
| 256 | dma[j++] = 0; |
| 257 | /* |
| 258 | * All done with this PDU. Get a buffer to save some |
| 259 | * data for reclamation services. |
| 260 | */ |
| 261 | KB_ALLOCPKT ( m, ENI_SMALL_BSIZE, KB_F_NOWAIT, |
| 262 | KB_T_DATA ); |
| 263 | if ( m ) { |
| 264 | u_long *up; |
| 265 | |
| 266 | KB_DATASTART ( m, up, u_long * ); |
| 267 | /* |
| 268 | * Indicate no PDU |
| 269 | */ |
| 270 | KB_PLENSET ( m, 0 ); |
| 271 | /* |
| 272 | * Set buffer length - only driver overhead |
| 273 | */ |
| 274 | KB_LEN ( m ) = 3 * sizeof ( u_long ); |
| 275 | /* |
| 276 | * Insert vcc, space for DMA pointers, |
| 277 | * and pdulen |
| 278 | */ |
| 279 | *up++ = vcc; |
| 280 | upp = up; /* Remember location */ |
| 281 | up++; /* And skip it */ |
| 282 | /* - to be filled later */ |
| 283 | *up = pdulen; /* Actual PDU length if it */ |
| 284 | /* were valid */ |
| 285 | } else { |
| 286 | /* |
| 287 | * We've a real problem here as now we can't |
| 288 | * reclaim/advance resources/safety pointers. |
| 289 | */ |
| 290 | eup->eu_stats.eni_st_drv.drv_rv_norsc++; |
| 291 | #ifdef DO_LOG |
| 292 | log ( LOG_ERR, |
| 293 | "eni_do_service: No drain buffers available. Receiver about to lock.\n" ); |
| 294 | #endif |
| 295 | } |
| 296 | goto send_dma; |
| 297 | } |
| 298 | |
| 299 | /* |
| 300 | * Do we need to strip the AAL layer? Yes if this |
| 301 | * is an AAL5 PDU. |
| 302 | */ |
| 303 | if ( aal5 ) { |
| 304 | /* |
| 305 | * Grab the CS-PDU length. Find the address of the |
| 306 | * last word, back up one word to skip CRC, and |
| 307 | * then mask the whole thing to handle circular wraps. |
| 308 | */ |
| 309 | pdulen = rxp[(rdptr + n_cells*WORDS_PER_CELL - 1) |
| 310 | & (mask-1)] |
| 311 | & 0xFFFF; |
| 312 | } |
| 313 | |
| 314 | /* |
| 315 | * We now have a valid PDU of some length. Build |
| 316 | * the necessary DMA list to move it into host |
| 317 | * memory. |
| 318 | */ |
| 319 | |
| 320 | /* |
| 321 | * Get an initial buffer. |
| 322 | */ |
| 323 | KB_ALLOCPKT ( m, ENI_SMALL_BSIZE, KB_F_NOWAIT, KB_T_DATA ); |
| 324 | /* |
| 325 | * Do we have a valid buffer? |
| 326 | */ |
| 327 | if ( m != (KBuffer *)NULL ) |
| 328 | { |
| 329 | int len; |
| 330 | u_long *up; |
| 331 | KBuffer *m0; |
| 332 | |
| 333 | KB_DATASTART ( m, up, u_long * ); |
| 334 | /* |
| 335 | * Fill in pdulen in PKTHDR structure (for IP). |
| 336 | */ |
| 337 | KB_PLENSET ( m, pdulen ); |
| 338 | /* |
| 339 | * We're going to save the VCI nuber, the start |
| 340 | * and stop DMA pointers, and the PDU length at |
| 341 | * the head of the buffer. We'll pull this out |
| 342 | * later after the DMA has completed. |
| 343 | * |
| 344 | * Insert VCI number as first word in first buffer, |
| 345 | * remeber where we want to store the start/stop |
| 346 | * pointers, and store the PDU length. |
| 347 | */ |
| 348 | *up++ = vcc; /* PDU's VCC */ |
| 349 | upp = up; /* Remember where we are */ |
| 350 | up++; /* To stuff start/stop pointers in */ |
| 351 | *up++ = pdulen; /* PDU's length */ |
| 352 | /* |
| 353 | * Leave some extra room in case a higher protocol |
| 354 | * (IP) wants to do a pullup. Maybe we can keep |
| 355 | * someone from having to allocate another buffer |
| 356 | * a do a larger memory copy. |
| 357 | */ |
| 358 | len = MIN ( ENI_SMALL_BSIZE, pdulen ); |
| 359 | (void) eni_set_dma ( eup, 1, dma, TEMP_DMA_SIZE, &j, |
| 360 | vcc, (u_long)up, len ); |
| 361 | /* |
| 362 | * Adjust length of remaining data in PDU |
| 363 | */ |
| 364 | pdulen -= len; |
| 365 | /* |
| 366 | * Set buffer length, including our overhead |
| 367 | */ |
| 368 | KB_LEN ( m ) = len + 3 * sizeof ( u_long ); |
| 369 | /* |
| 370 | * Finish by moving anything which won't fit in |
| 371 | * first buffer |
| 372 | */ |
| 373 | m0 = m; |
| 374 | while ( pdulen ) { |
| 375 | KBuffer *m1; |
| 376 | u_long data_addr; |
| 377 | |
| 378 | /* |
| 379 | * Get another buffer |
| 380 | */ |
| 381 | KB_ALLOCEXT ( m1, ENI_LARGE_BSIZE, KB_F_NOWAIT, |
| 382 | KB_T_DATA ); |
| 383 | |
| 384 | /* |
| 385 | * If we succeeded... |
| 386 | */ |
| 387 | if ( m1 ) { |
| 388 | /* |
| 389 | * Figure out how much we can move into |
| 390 | * this buffer. |
| 391 | */ |
| 392 | len = MIN ( ENI_LARGE_BSIZE, pdulen ); |
| 393 | /* |
| 394 | * Setup DMA list for this buffer |
| 395 | */ |
| 396 | KB_DATASTART ( m1, data_addr, u_long ); |
| 397 | (void) eni_set_dma |
| 398 | ( eup, 1, dma, TEMP_DMA_SIZE, &j, vcc, |
| 399 | data_addr, len ); |
| 400 | /* |
| 401 | * Adjust remaining length |
| 402 | */ |
| 403 | pdulen -= len; |
| 404 | /* |
| 405 | * Set buffer length |
| 406 | */ |
| 407 | KB_LEN ( m1 ) = len; |
| 408 | /* |
| 409 | * Link new buffer onto end and advance |
| 410 | * pointer |
| 411 | */ |
| 412 | KB_NEXT ( m0 ) = m1; |
| 413 | m0 = m1; |
| 414 | } else { |
| 415 | /* |
| 416 | * Either we were unable to grab another |
| 417 | * buffer or there are no large buffers |
| 418 | * available. We know that the first |
| 419 | * buffer is valid, so drop everything |
| 420 | * else, build a JK DMA to skip/drop this |
| 421 | * PDU, set the pointers to reclaim |
| 422 | * resources/advance pointers, and |
| 423 | * finish this PDU now. |
| 424 | */ |
| 425 | if ( KB_NEXT ( m ) ) |
| 426 | KB_FREEALL ( KB_NEXT ( m ) ); |
| 427 | eup->eu_pif.pif_ierrors++; |
| 428 | j = 2; |
| 429 | dma[j++] = |
| 430 | (((rdptr + n_cells*WORDS_PER_CELL + 1) |
| 431 | & (mask-1)) << DMA_COUNT_SHIFT ) | |
| 432 | (vcc << DMA_VCC_SHIFT ) | |
| 433 | DMA_JK; |
| 434 | dma[j++] = 0; |
| 435 | /* |
| 436 | * Reset PDU length to zero |
| 437 | */ |
| 438 | KB_PLENSET ( m, 0 ); |
| 439 | /* |
| 440 | * Count some statistics |
| 441 | */ |
| 442 | /* |
| 443 | * Count this as dropped cells |
| 444 | */ |
| 445 | if ( aal5 ) { |
| 446 | eup->eu_stats.eni_st_aal5.aal5_drops += |
| 447 | n_cells; |
| 448 | eup->eu_stats.eni_st_aal5.aal5_pdu_drops++; |
| 449 | } else |
| 450 | eup->eu_stats.eni_st_aal0.aal0_drops += |
| 451 | n_cells; |
| 452 | /* |
| 453 | * Drop it |
| 454 | */ |
| 455 | goto send_dma; |
| 456 | } |
| 457 | } |
| 458 | /* |
| 459 | * If necessary, skip AAL layer |
| 460 | */ |
| 461 | if ( aal5 ) { |
| 462 | dma[j++] = |
| 463 | (((rdptr + n_cells*WORDS_PER_CELL + 1) |
| 464 | & (mask-1)) << DMA_COUNT_SHIFT) |
| 465 | | (vcc << DMA_VCC_SHIFT) | DMA_JK; |
| 466 | dma[j++] = 0; |
| 467 | } |
| 468 | } else { |
| 469 | /* |
| 470 | * We failed to get an initial buffer. Since we |
| 471 | * haven't changed anything for this PDU yet and the |
| 472 | * PDU is still valid, exit now and try to service it |
| 473 | * next time around. We're not very likely to get |
| 474 | * another buffer right now anyways. |
| 475 | */ |
| 476 | eup->eu_stats.eni_st_drv.drv_rv_nobufs++; |
| 477 | #ifdef DO_LOG |
| 478 | log ( LOG_ERR, |
| 479 | "eni_do_service: No buffers available. Exiting without servicing service list.\n" ); |
| 480 | #endif |
| 481 | /* |
| 482 | * Clear the IN_SERVICE indicator for this VCC |
| 483 | */ |
| 484 | vct->vci_control &= ~VCI_IN_SERVICE; |
| 485 | return; |
| 486 | } |
| 487 | |
| 488 | send_dma: |
| 489 | /* |
| 490 | * Set the end bit on the last DMA for this PDU |
| 491 | */ |
| 492 | dma[j-2] |= DMA_END_BIT; |
| 493 | |
| 494 | /* |
| 495 | * Where are the current DMA pointers |
| 496 | */ |
| 497 | dma_rd = eup->eu_midway[MIDWAY_RX_RD]; |
| 498 | dma_wr = eup->eu_midway[MIDWAY_RX_WR]; |
| 499 | |
| 500 | /* |
| 501 | * Check how much space is available |
| 502 | */ |
| 503 | if ( dma_rd == dma_wr ) |
| 504 | dma_avail = DMA_LIST_SIZE; |
| 505 | else |
| 506 | dma_avail = ( dma_rd + DMA_LIST_SIZE - dma_wr ) |
| 507 | & (DMA_LIST_SIZE-1); |
| 508 | |
| 509 | /* |
| 510 | * Check for queue full or wrap past write okay pointer |
| 511 | */ |
| 512 | if ( dma_avail < j || |
| 513 | ( dma_wr + j > eup->eu_rxdmawr + DMA_LIST_SIZE ) ) { |
| 514 | /* |
| 515 | * There's no room in the DMA list to insert |
| 516 | * this request. Since we haven't changed anything |
| 517 | * yet and the PDU is good, exit now and service |
| 518 | * it next time around. What we really need to do |
| 519 | * is wait for the RX list to drain and that won't |
| 520 | * happen if we keep trying to process PDUs here. |
| 521 | */ |
| 522 | eup->eu_stats.eni_st_drv.drv_rv_nodma++; |
| 523 | #ifdef DO_LOG |
| 524 | log ( LOG_ERR, |
| 525 | "eni_do_service: No room in receive DMA list. Postponing service request.\n" ); |
| 526 | #endif |
| 527 | /* |
| 528 | * Free the local buffer chain |
| 529 | */ |
| 530 | KB_FREEALL ( m ); |
| 531 | /* |
| 532 | * Clear the IN_SERVICE indicator for this VCC. |
| 533 | */ |
| 534 | vct->vci_control &= ~VCI_IN_SERVICE; |
| 535 | return; |
| 536 | } |
| 537 | |
| 538 | /* |
| 539 | * If we have a buffer chain, save the starting |
| 540 | * dma_list location. |
| 541 | */ |
| 542 | if ( upp ) { |
| 543 | *upp = dma_wr << 16; |
| 544 | } |
| 545 | |
| 546 | /* |
| 547 | * Stuff the DMA list |
| 548 | */ |
| 549 | j >>= 1; |
| 550 | for ( i = 0; i < j; i++ ) { |
| 551 | eup->eu_rxdma[dma_wr*2] = dma[i*2]; |
| 552 | eup->eu_rxdma[dma_wr*2+1] = dma[i*2+1]; |
| 553 | dma_wr = (dma_wr+1) & (DMA_LIST_SIZE-1); |
| 554 | } |
| 555 | /* |
| 556 | * If we have a buffer chain, save the location of |
| 557 | * the ending dma_list location and queue the chain |
| 558 | * so that we can recover the resources later. |
| 559 | */ |
| 560 | if ( upp ) { |
| 561 | *upp |= dma_wr; |
| 562 | /* |
| 563 | * Place buffer on receive queue waiting for RX_DMA |
| 564 | */ |
| 565 | if ( IF_QFULL ( &eup->eu_rxqueue ) ) { |
| 566 | /* |
| 567 | * We haven't done anything we can't back out |
| 568 | * of. Drop request and service it next time. |
| 569 | * We've inserted the DMA list but it's not |
| 570 | * valid until we advance the RX_WR pointer, |
| 571 | * thus it's okay to bail here... |
| 572 | */ |
| 573 | eup->eu_stats.eni_st_drv.drv_rv_rxq++; |
| 574 | #ifdef DO_LOG |
| 575 | log ( LOG_ERR, |
| 576 | "eni_do_service: RX drain queue full. Postponing servicing.\n" ); |
| 577 | #endif |
| 578 | KB_FREEALL ( m ); |
| 579 | /* |
| 580 | * Clear the IN_SERVICE indicator for this VCC. |
| 581 | */ |
| 582 | vct->vci_control &= ~VCI_IN_SERVICE; |
| 583 | return; |
| 584 | } else { |
| 585 | IF_ENQUEUE ( &eup->eu_rxqueue, m ); |
| 586 | /* |
| 587 | * Advance the RX_WR pointer to cause |
| 588 | * the adapter to work on this DMA list. |
| 589 | */ |
| 590 | eup->eu_midway[MIDWAY_RX_WR] = dma_wr; |
| 591 | } |
| 592 | } |
| 593 | /* |
| 594 | * Advance our notion of where the next PDU |
| 595 | * should start. |
| 596 | */ |
| 597 | rdptr = (rdptr + n_cells*WORDS_PER_CELL + 1) |
| 598 | & (mask-1); |
| 599 | evp->ev_rxpos = rdptr; |
| 600 | |
| 601 | /* |
| 602 | * Increment cells/pdu received stats. |
| 603 | */ |
| 604 | eup->eu_stats.eni_st_atm.atm_rcvd += n_cells; |
| 605 | if ( aal5 ) { |
| 606 | eup->eu_stats.eni_st_aal5.aal5_rcvd += n_cells; |
| 607 | eup->eu_stats.eni_st_aal5.aal5_pdu_rcvd++; |
| 608 | } else { |
| 609 | eup->eu_stats.eni_st_aal0.aal0_rcvd += n_cells; |
| 610 | } |
| 611 | |
| 612 | /* |
| 613 | * Continue processing PDUs on this same VCI |
| 614 | */ |
| 615 | } |
| 616 | |
| 617 | next_vcc: |
| 618 | /* |
| 619 | * Advance to next entry in the service_list. |
| 620 | */ |
| 621 | eup->eu_servread = (eup->eu_servread + 1) & SVC_SIZE_MASK; |
| 622 | |
| 623 | /* |
| 624 | * And clear the IN_SERVICE indicator for this VCC. |
| 625 | */ |
| 626 | vct->vci_control &= ~VCI_IN_SERVICE; |
| 627 | } |
| 628 | return; |
| 629 | } |
| 630 | |
| 631 | /* |
| 632 | * Drain Receive queue |
| 633 | * |
| 634 | * As we build DMA lists to move PDUs from adapter buffers into host |
| 635 | * buffers, we place the request on a private ifqueue so that we can |
| 636 | * free any resources AFTER we know they've been successfully DMAed. |
| 637 | * As part of the service processing, we record the PDUs start and stop |
| 638 | * entries in the DMA list, and prevent wrapping. When we pull the top |
| 639 | * entry off, we simply check that the current DMA location is outside |
| 640 | * this PDU and if so, it's okay to free things. |
| 641 | * |
| 642 | * Arguments: |
| 643 | * eup pointer to device unit structure |
| 644 | * |
| 645 | * Returns: |
| 646 | * none |
| 647 | * |
| 648 | */ |
| 649 | void |
| 650 | eni_recv_drain ( eup ) |
| 651 | Eni_unit *eup; |
| 652 | { |
| 653 | KBuffer *m; |
| 654 | Eni_vcc *evp; |
| 655 | struct vccb *vcp; |
| 656 | u_long vcc; |
| 657 | u_long DMA_Rdptr; |
| 658 | u_long dma_wrp; |
| 659 | u_long start, stop; |
| 660 | int que = 0; |
| 661 | int s; |
| 662 | |
| 663 | s = splimp(); |
| 664 | /* Pop first buffer */ |
| 665 | IF_DEQUEUE ( &eup->eu_rxqueue, m ); |
| 666 | while ( m ) { |
| 667 | u_long *up; |
| 668 | u_long pdulen; |
| 669 | |
| 670 | KB_DATASTART ( m, up, u_long * ); |
| 671 | |
| 672 | /* |
| 673 | * Grab the VCI number |
| 674 | */ |
| 675 | vcc = *up++; |
| 676 | |
| 677 | /* |
| 678 | * Check to see if we can process this buffer yet. |
| 679 | */ |
| 680 | /* Get current DMA_Rdptr */ |
| 681 | DMA_Rdptr = eup->eu_midway[MIDWAY_RX_RD]; |
| 682 | /* Boundaries for first buffer */ |
| 683 | dma_wrp = *up++; |
| 684 | start = dma_wrp >> 16; |
| 685 | stop = dma_wrp & 0xffff; |
| 686 | /* |
| 687 | * Start should not equal stop because that would |
| 688 | * mean we tried inserting a NULL DMA list. |
| 689 | */ |
| 690 | if ( start > stop ) { /* We wrapped */ |
| 691 | if ( !(DMA_Rdptr >= stop && DMA_Rdptr < start) ) { |
| 692 | IF_PREPEND ( &eup->eu_rxqueue, m ); |
| 693 | goto finish; |
| 694 | } |
| 695 | } else { |
| 696 | if ( DMA_Rdptr < stop && DMA_Rdptr >= start ) { |
| 697 | IF_PREPEND ( &eup->eu_rxqueue, m ); |
| 698 | goto finish; |
| 699 | } |
| 700 | } |
| 701 | /* |
| 702 | * Adapter is finished with this buffer, we can |
| 703 | * continue processing it now. |
| 704 | */ |
| 705 | |
| 706 | /* |
| 707 | * Locate incoming VCC for this PDU |
| 708 | */ |
| 709 | evp = (Eni_vcc *) atm_dev_vcc_find ( (Cmn_unit *)eup, |
| 710 | 0, vcc, VCC_IN ); |
| 711 | |
| 712 | if ( evp == NULL ) { |
| 713 | eup->eu_stats.eni_st_drv.drv_rv_novcc++; |
| 714 | KB_FREEALL ( m ); |
| 715 | goto next_buffer; |
| 716 | } |
| 717 | |
| 718 | #ifdef DIAGNOSTIC |
| 719 | if ( eni_pdu_print ) |
| 720 | atm_dev_pdu_print ( (Cmn_unit *)eup, (Cmn_vcc *)evp, m, |
| 721 | "eni_stack_drain" ); |
| 722 | #endif |
| 723 | |
| 724 | /* |
| 725 | * Grab theoretical PDU length |
| 726 | */ |
| 727 | pdulen = *up++; |
| 728 | |
| 729 | /* |
| 730 | * Quick, count the PDU |
| 731 | */ |
| 732 | eup->eu_pif.pif_ipdus++; |
| 733 | eup->eu_pif.pif_ibytes += pdulen; |
| 734 | if ( evp ) { |
| 735 | vcp = evp->ev_connvc->cvc_vcc; |
| 736 | if ( vcp ) { |
| 737 | vcp->vc_ipdus++; |
| 738 | vcp->vc_ibytes += pdulen; |
| 739 | if ( vcp->vc_nif ) { |
| 740 | vcp->vc_nif->nif_ibytes += pdulen; |
| 741 | vcp->vc_nif->nif_if.if_ipackets++; |
| 742 | #if (defined(BSD) && (BSD >= 199103)) |
| 743 | vcp->vc_nif->nif_if.if_ibytes += pdulen; |
| 744 | #endif |
| 745 | } |
| 746 | } |
| 747 | } |
| 748 | |
| 749 | /* |
| 750 | * Advance DMA write allowable pointer |
| 751 | */ |
| 752 | eup->eu_rxdmawr = stop; |
| 753 | |
| 754 | /* |
| 755 | * Get packet PDU length |
| 756 | */ |
| 757 | KB_PLENGET ( m, pdulen ); |
| 758 | |
| 759 | /* |
| 760 | * Only try queueing this if there is data |
| 761 | * to be handed up to the next layer. Errors |
| 762 | * such as CRC and VC trashing will get us this |
| 763 | * far to advance pointers, etc., but the PDU |
| 764 | * length will be zero. |
| 765 | */ |
| 766 | if ( pdulen ) { |
| 767 | /* |
| 768 | * We saved three words back in eni_do_service() |
| 769 | * to use for callback. Since the core only |
| 770 | * expects two words, skip over the first one. |
| 771 | * Then, reset up pointer to start of buffer data |
| 772 | * area and write the callback info. |
| 773 | */ |
| 774 | KB_HEADADJ ( m, -sizeof(u_long) ); |
| 775 | KB_DATASTART ( m, up, u_long * ); |
| 776 | *((int *)up) = (int)eni_recv_stack; |
| 777 | up++; |
| 778 | *((int *)up) = (int)evp; |
| 779 | /* |
| 780 | * Schedule callback |
| 781 | */ |
| 782 | if ( !IF_QFULL ( &atm_intrq ) ) { |
| 783 | que++; |
| 784 | IF_ENQUEUE ( &atm_intrq, m ); |
| 785 | } else { |
| 786 | eup->eu_stats.eni_st_drv.drv_rv_intrq++; |
| 787 | eup->eu_pif.pif_ierrors++; |
| 788 | #ifdef DO_LOG |
| 789 | log ( LOG_ERR, |
| 790 | "eni_receive_drain: ATM_INTRQ is full. Unable to pass up stack.\n" ); |
| 791 | #endif |
| 792 | KB_FREEALL ( m ); |
| 793 | } |
| 794 | } else { |
| 795 | /* |
| 796 | * Free zero-length buffer |
| 797 | */ |
| 798 | KB_FREEALL(m); |
| 799 | } |
| 800 | |
| 801 | next_buffer: |
| 802 | /* |
| 803 | * Look for next buffer |
| 804 | */ |
| 805 | IF_DEQUEUE ( &eup->eu_rxqueue, m ); |
| 806 | } |
| 807 | finish: |
| 808 | (void) splx(s); |
| 809 | |
| 810 | /* |
| 811 | * If we found any completed buffers, schedule a call into |
| 812 | * the kernel to process the atm_intrq. |
| 813 | */ |
| 814 | if ( que ) |
| 815 | SCHED_ATM; |
| 816 | |
| 817 | return; |
| 818 | |
| 819 | } |
| 820 | |
| 821 | /* |
| 822 | * Pass incoming PDU up Stack |
| 823 | * |
| 824 | * This function is called via the core ATM interrupt queue callback |
| 825 | * set in eni_recv_drain(). It will pass the supplied incoming |
| 826 | * PDU up the incoming VCC's stack. |
| 827 | * |
| 828 | * Arguments: |
| 829 | * tok token to identify stack instantiation |
| 830 | * m pointer to incoming PDU buffer chain |
| 831 | * |
| 832 | * Returns: |
| 833 | * none |
| 834 | */ |
| 835 | static void |
| 836 | eni_recv_stack ( tok, m ) |
| 837 | void *tok; |
| 838 | KBuffer *m; |
| 839 | { |
| 840 | Eni_vcc *evp = (Eni_vcc *)tok; |
| 841 | int err; |
| 842 | |
| 843 | /* |
| 844 | * This should never happen now but if it does and we don't stop it, |
| 845 | * we end up panic'ing in netatm when trying to pull a function |
| 846 | * pointer and token value out of a buffer with address zero. |
| 847 | */ |
| 848 | if ( !m ) { |
| 849 | #ifdef DO_LOG |
| 850 | log ( LOG_ERR, |
| 851 | "eni_recv_stack: NULL buffer, tok = %p\n", tok ); |
| 852 | #endif |
| 853 | return; |
| 854 | } |
| 855 | |
| 856 | /* |
| 857 | * Send the data up the stack |
| 858 | */ |
| 859 | STACK_CALL ( CPCS_UNITDATA_SIG, evp->ev_upper, |
| 860 | (void *)evp->ev_toku, evp->ev_connvc, (int)m, 0, err ); |
| 861 | if ( err ) { |
| 862 | KB_FREEALL ( m ); |
| 863 | } |
| 864 | |
| 865 | return; |
| 866 | } |
| 867 | |