1 /* $NetBSD: isadma_machdep.c,v 1.1 2002/02/10 01:57:55 thorpej Exp $ */ 2 3 #define ISA_DMA_STATS 4 5 /*- 6 * Copyright (c) 1996, 1997, 1998 The NetBSD Foundation, Inc. 7 * All rights reserved. 8 * 9 * This code is derived from software contributed to The NetBSD Foundation 10 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, 11 * NASA Ames Research Center. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution. 21 * 3. All advertising materials mentioning features or use of this software 22 * must display the following acknowledgement: 23 * This product includes software developed by the NetBSD 24 * Foundation, Inc. and its contributors. 25 * 4. Neither the name of The NetBSD Foundation nor the names of its 26 * contributors may be used to endorse or promote products derived 27 * from this software without specific prior written permission. 28 * 29 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 30 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 31 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 32 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 33 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 34 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 35 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 36 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 37 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 38 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 39 * POSSIBILITY OF SUCH DAMAGE. 40 */ 41 42 #include <sys/param.h> 43 #include <sys/systm.h> 44 #include <sys/syslog.h> 45 #include <sys/device.h> 46 #include <sys/malloc.h> 47 #include <sys/proc.h> 48 #include <sys/mbuf.h> 49 50 #define _ARM32_BUS_DMA_PRIVATE 51 #include <machine/bus.h> 52 53 #include <dev/isa/isareg.h> 54 #include <dev/isa/isavar.h> 55 56 #include <uvm/uvm_extern.h> 57 58 /* 59 * ISA has a 24-bit address limitation, so at most it has a 16M 60 * DMA range. However, some platforms have a more limited range, 61 * e.g. the Shark NC. On these systems, we are provided with 62 * a set of DMA ranges. The pmap module is aware of these ranges 63 * and places DMA-safe memory for them onto an alternate free list 64 * so that they are protected from being used to service page faults, 65 * etc. (unless we've run out of memory elsewhere). 66 */ 67 #define ISA_DMA_BOUNCE_THRESHOLD (16 * 1024 * 1024) 68 extern bus_dma_segment_t *pmap_isa_dma_ranges; 69 extern int pmap_isa_dma_nranges; 70 71 int _isa_bus_dmamap_create __P((bus_dma_tag_t, bus_size_t, int, 72 bus_size_t, bus_size_t, int, bus_dmamap_t *)); 73 void _isa_bus_dmamap_destroy __P((bus_dma_tag_t, bus_dmamap_t)); 74 int _isa_bus_dmamap_load __P((bus_dma_tag_t, bus_dmamap_t, void *, 75 bus_size_t, struct proc *, int)); 76 int _isa_bus_dmamap_load_mbuf __P((bus_dma_tag_t, bus_dmamap_t, 77 struct mbuf *, int)); 78 int _isa_bus_dmamap_load_uio __P((bus_dma_tag_t, bus_dmamap_t, 79 struct uio *, int)); 80 int _isa_bus_dmamap_load_raw __P((bus_dma_tag_t, bus_dmamap_t, 81 bus_dma_segment_t *, int, bus_size_t, int)); 82 void _isa_bus_dmamap_unload __P((bus_dma_tag_t, bus_dmamap_t)); 83 void _isa_bus_dmamap_sync __P((bus_dma_tag_t, bus_dmamap_t, 84 bus_addr_t, bus_size_t, int)); 85 86 int _isa_bus_dmamem_alloc __P((bus_dma_tag_t, bus_size_t, bus_size_t, 87 bus_size_t, bus_dma_segment_t *, int, int *, int)); 88 89 int _isa_dma_alloc_bouncebuf __P((bus_dma_tag_t, bus_dmamap_t, 90 bus_size_t, int)); 91 void _isa_dma_free_bouncebuf __P((bus_dma_tag_t, bus_dmamap_t)); 92 93 /* 94 * Entry points for ISA DMA. These are mostly wrappers around 95 * the generic functions that understand how to deal with bounce 96 * buffers, if necessary. 97 */ 98 struct arm32_bus_dma_tag isa_bus_dma_tag = { 99 0, /* _ranges */ 100 0, /* _nranges */ 101 _isa_bus_dmamap_create, 102 _isa_bus_dmamap_destroy, 103 _isa_bus_dmamap_load, 104 _isa_bus_dmamap_load_mbuf, 105 _isa_bus_dmamap_load_uio, 106 _isa_bus_dmamap_load_raw, 107 _isa_bus_dmamap_unload, 108 _isa_bus_dmamap_sync, 109 _isa_bus_dmamem_alloc, 110 _bus_dmamem_free, 111 _bus_dmamem_map, 112 _bus_dmamem_unmap, 113 _bus_dmamem_mmap, 114 }; 115 116 /* 117 * Initialize ISA DMA. 118 */ 119 void 120 isa_dma_init() 121 { 122 123 isa_bus_dma_tag._ranges = pmap_isa_dma_ranges; 124 isa_bus_dma_tag._nranges = pmap_isa_dma_nranges; 125 } 126 127 /********************************************************************** 128 * bus.h dma interface entry points 129 **********************************************************************/ 130 131 #ifdef ISA_DMA_STATS 132 #define STAT_INCR(v) (v)++ 133 #define STAT_DECR(v) do { \ 134 if ((v) == 0) \ 135 printf("%s:%d -- Already 0!\n", __FILE__, __LINE__); \ 136 else \ 137 (v)--; \ 138 } while (0) 139 u_long isa_dma_stats_loads; 140 u_long isa_dma_stats_bounces; 141 u_long isa_dma_stats_nbouncebufs; 142 #else 143 #define STAT_INCR(v) 144 #define STAT_DECR(v) 145 #endif 146 147 /* 148 * Create an ISA DMA map. 149 */ 150 int 151 _isa_bus_dmamap_create(t, size, nsegments, maxsegsz, boundary, flags, dmamp) 152 bus_dma_tag_t t; 153 bus_size_t size; 154 int nsegments; 155 bus_size_t maxsegsz; 156 bus_size_t boundary; 157 int flags; 158 bus_dmamap_t *dmamp; 159 { 160 struct arm32_isa_dma_cookie *cookie; 161 bus_dmamap_t map; 162 int error, cookieflags; 163 void *cookiestore; 164 size_t cookiesize; 165 166 /* Call common function to create the basic map. */ 167 error = _bus_dmamap_create(t, size, nsegments, maxsegsz, boundary, 168 flags, dmamp); 169 if (error) 170 return (error); 171 172 map = *dmamp; 173 map->_dm_cookie = NULL; 174 175 cookiesize = sizeof(struct arm32_isa_dma_cookie); 176 177 /* 178 * ISA only has 24-bits of address space. This means 179 * we can't DMA to pages over 16M. In order to DMA to 180 * arbitrary buffers, we use "bounce buffers" - pages 181 * in memory below the 16M boundary. On DMA reads, 182 * DMA happens to the bounce buffers, and is copied into 183 * the caller's buffer. On writes, data is copied into 184 * but bounce buffer, and the DMA happens from those 185 * pages. To software using the DMA mapping interface, 186 * this looks simply like a data cache. 187 * 188 * If we have more than 16M of RAM in the system, we may 189 * need bounce buffers. We check and remember that here. 190 * 191 * There are exceptions, however. VLB devices can do 192 * 32-bit DMA, and indicate that here. 193 * 194 * ...or, there is an opposite case. The most segments 195 * a transfer will require is (maxxfer / NBPG) + 1. If 196 * the caller can't handle that many segments (e.g. the 197 * ISA DMA controller), we may have to bounce it as well. 198 * 199 * Well, not really... see note above regarding DMA ranges. 200 * Because of the range issue on this platform, we just 201 * always "might bounce". 202 */ 203 cookieflags = ID_MIGHT_NEED_BOUNCE; 204 cookiesize += (sizeof(bus_dma_segment_t) * map->_dm_segcnt); 205 206 /* 207 * Allocate our cookie. 208 */ 209 if ((cookiestore = malloc(cookiesize, M_DMAMAP, 210 (flags & BUS_DMA_NOWAIT) ? M_NOWAIT : M_WAITOK)) == NULL) { 211 error = ENOMEM; 212 goto out; 213 } 214 memset(cookiestore, 0, cookiesize); 215 cookie = (struct arm32_isa_dma_cookie *)cookiestore; 216 cookie->id_flags = cookieflags; 217 map->_dm_cookie = cookie; 218 219 if (cookieflags & ID_MIGHT_NEED_BOUNCE) { 220 /* 221 * Allocate the bounce pages now if the caller 222 * wishes us to do so. 223 */ 224 if ((flags & BUS_DMA_ALLOCNOW) == 0) 225 goto out; 226 227 error = _isa_dma_alloc_bouncebuf(t, map, size, flags); 228 } 229 230 out: 231 if (error) { 232 if (map->_dm_cookie != NULL) 233 free(map->_dm_cookie, M_DMAMAP); 234 _bus_dmamap_destroy(t, map); 235 } 236 return (error); 237 } 238 239 /* 240 * Destroy an ISA DMA map. 241 */ 242 void 243 _isa_bus_dmamap_destroy(t, map) 244 bus_dma_tag_t t; 245 bus_dmamap_t map; 246 { 247 struct arm32_isa_dma_cookie *cookie = map->_dm_cookie; 248 249 /* 250 * Free any bounce pages this map might hold. 251 */ 252 if (cookie->id_flags & ID_HAS_BOUNCE) 253 _isa_dma_free_bouncebuf(t, map); 254 255 free(cookie, M_DMAMAP); 256 _bus_dmamap_destroy(t, map); 257 } 258 259 /* 260 * Load an ISA DMA map with a linear buffer. 261 */ 262 int 263 _isa_bus_dmamap_load(t, map, buf, buflen, p, flags) 264 bus_dma_tag_t t; 265 bus_dmamap_t map; 266 void *buf; 267 bus_size_t buflen; 268 struct proc *p; 269 int flags; 270 { 271 struct arm32_isa_dma_cookie *cookie = map->_dm_cookie; 272 int error; 273 274 STAT_INCR(isa_dma_stats_loads); 275 276 /* 277 * Make sure that on error condition we return "no valid mappings." 278 */ 279 map->dm_mapsize = 0; 280 map->dm_nsegs = 0; 281 282 /* 283 * Try to load the map the normal way. If this errors out, 284 * and we can bounce, we will. 285 */ 286 error = _bus_dmamap_load(t, map, buf, buflen, p, flags); 287 if (error == 0 || 288 (error != 0 && (cookie->id_flags & ID_MIGHT_NEED_BOUNCE) == 0)) 289 return (error); 290 291 /* 292 * First attempt failed; bounce it. 293 */ 294 295 STAT_INCR(isa_dma_stats_bounces); 296 297 /* 298 * Allocate bounce pages, if necessary. 299 */ 300 if ((cookie->id_flags & ID_HAS_BOUNCE) == 0) { 301 error = _isa_dma_alloc_bouncebuf(t, map, buflen, flags); 302 if (error) 303 return (error); 304 } 305 306 /* 307 * Cache a pointer to the caller's buffer and load the DMA map 308 * with the bounce buffer. 309 */ 310 cookie->id_origbuf = buf; 311 cookie->id_origbuflen = buflen; 312 cookie->id_buftype = ID_BUFTYPE_LINEAR; 313 error = _bus_dmamap_load(t, map, cookie->id_bouncebuf, buflen, 314 NULL, flags); 315 if (error) { 316 /* 317 * Free the bounce pages, unless our resources 318 * are reserved for our exclusive use. 319 */ 320 if ((map->_dm_flags & BUS_DMA_ALLOCNOW) == 0) 321 _isa_dma_free_bouncebuf(t, map); 322 return (error); 323 } 324 325 /* ...so _isa_bus_dmamap_sync() knows we're bouncing */ 326 cookie->id_flags |= ID_IS_BOUNCING; 327 return (0); 328 } 329 330 /* 331 * Like _isa_bus_dmamap_load(), but for mbufs. 332 */ 333 int 334 _isa_bus_dmamap_load_mbuf(t, map, m0, flags) 335 bus_dma_tag_t t; 336 bus_dmamap_t map; 337 struct mbuf *m0; 338 int flags; 339 { 340 struct arm32_isa_dma_cookie *cookie = map->_dm_cookie; 341 int error; 342 343 /* 344 * Make sure that on error condition we return "no valid mappings." 345 */ 346 map->dm_mapsize = 0; 347 map->dm_nsegs = 0; 348 349 #ifdef DIAGNOSTIC 350 if ((m0->m_flags & M_PKTHDR) == 0) 351 panic("_isa_bus_dmamap_load_mbuf: no packet header"); 352 #endif 353 354 if (m0->m_pkthdr.len > map->_dm_size) 355 return (EINVAL); 356 357 /* 358 * Try to load the map the normal way. If this errors out, 359 * and we can bounce, we will. 360 */ 361 error = _bus_dmamap_load_mbuf(t, map, m0, flags); 362 if (error == 0 || 363 (error != 0 && (cookie->id_flags & ID_MIGHT_NEED_BOUNCE) == 0)) 364 return (error); 365 366 /* 367 * First attempt failed; bounce it. 368 */ 369 370 STAT_INCR(isa_dma_stats_bounces); 371 372 /* 373 * Allocate bounce pages, if necessary. 374 */ 375 if ((cookie->id_flags & ID_HAS_BOUNCE) == 0) { 376 error = _isa_dma_alloc_bouncebuf(t, map, m0->m_pkthdr.len, 377 flags); 378 if (error) 379 return (error); 380 } 381 382 /* 383 * Cache a pointer to the caller's buffer and load the DMA map 384 * with the bounce buffer. 385 */ 386 cookie->id_origbuf = m0; 387 cookie->id_origbuflen = m0->m_pkthdr.len; /* not really used */ 388 cookie->id_buftype = ID_BUFTYPE_MBUF; 389 error = _bus_dmamap_load(t, map, cookie->id_bouncebuf, 390 m0->m_pkthdr.len, NULL, flags); 391 if (error) { 392 /* 393 * Free the bounce pages, unless our resources 394 * are reserved for our exclusive use. 395 */ 396 if ((map->_dm_flags & BUS_DMA_ALLOCNOW) == 0) 397 _isa_dma_free_bouncebuf(t, map); 398 return (error); 399 } 400 401 /* ...so _isa_bus_dmamap_sync() knows we're bouncing */ 402 cookie->id_flags |= ID_IS_BOUNCING; 403 return (0); 404 } 405 406 /* 407 * Like _isa_bus_dmamap_load(), but for uios. 408 */ 409 int 410 _isa_bus_dmamap_load_uio(t, map, uio, flags) 411 bus_dma_tag_t t; 412 bus_dmamap_t map; 413 struct uio *uio; 414 int flags; 415 { 416 417 panic("_isa_bus_dmamap_load_uio: not implemented"); 418 } 419 420 /* 421 * Like _isa_bus_dmamap_load(), but for raw memory allocated with 422 * bus_dmamem_alloc(). 423 */ 424 int 425 _isa_bus_dmamap_load_raw(t, map, segs, nsegs, size, flags) 426 bus_dma_tag_t t; 427 bus_dmamap_t map; 428 bus_dma_segment_t *segs; 429 int nsegs; 430 bus_size_t size; 431 int flags; 432 { 433 434 panic("_isa_bus_dmamap_load_raw: not implemented"); 435 } 436 437 /* 438 * Unload an ISA DMA map. 439 */ 440 void 441 _isa_bus_dmamap_unload(t, map) 442 bus_dma_tag_t t; 443 bus_dmamap_t map; 444 { 445 struct arm32_isa_dma_cookie *cookie = map->_dm_cookie; 446 447 /* 448 * If we have bounce pages, free them, unless they're 449 * reserved for our exclusive use. 450 */ 451 if ((cookie->id_flags & ID_HAS_BOUNCE) && 452 (map->_dm_flags & BUS_DMA_ALLOCNOW) == 0) 453 _isa_dma_free_bouncebuf(t, map); 454 455 cookie->id_flags &= ~ID_IS_BOUNCING; 456 cookie->id_buftype = ID_BUFTYPE_INVALID; 457 458 /* 459 * Do the generic bits of the unload. 460 */ 461 _bus_dmamap_unload(t, map); 462 } 463 464 /* 465 * Synchronize an ISA DMA map. 466 */ 467 void 468 _isa_bus_dmamap_sync(t, map, offset, len, ops) 469 bus_dma_tag_t t; 470 bus_dmamap_t map; 471 bus_addr_t offset; 472 bus_size_t len; 473 int ops; 474 { 475 struct arm32_isa_dma_cookie *cookie = map->_dm_cookie; 476 477 /* 478 * Mixing PRE and POST operations is not allowed. 479 */ 480 if ((ops & (BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE)) != 0 && 481 (ops & (BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE)) != 0) 482 panic("_isa_bus_dmamap_sync: mix PRE and POST"); 483 484 #ifdef DIAGNOSTIC 485 if ((ops & (BUS_DMASYNC_PREWRITE|BUS_DMASYNC_POSTREAD)) != 0) { 486 if (offset >= map->dm_mapsize) 487 panic("_isa_bus_dmamap_sync: bad offset"); 488 if (len == 0 || (offset + len) > map->dm_mapsize) 489 panic("_isa_bus_dmamap_sync: bad length"); 490 } 491 #endif 492 493 /* 494 * If we're not bouncing, just return; nothing to do. 495 */ 496 if ((cookie->id_flags & ID_IS_BOUNCING) == 0) 497 return; 498 499 switch (cookie->id_buftype) { 500 case ID_BUFTYPE_LINEAR: 501 /* 502 * Nothing to do for pre-read. 503 */ 504 505 if (ops & BUS_DMASYNC_PREWRITE) { 506 /* 507 * Copy the caller's buffer to the bounce buffer. 508 */ 509 memcpy((char *)cookie->id_bouncebuf + offset, 510 (char *)cookie->id_origbuf + offset, len); 511 } 512 513 if (ops & BUS_DMASYNC_POSTREAD) { 514 /* 515 * Copy the bounce buffer to the caller's buffer. 516 */ 517 memcpy((char *)cookie->id_origbuf + offset, 518 (char *)cookie->id_bouncebuf + offset, len); 519 } 520 521 /* 522 * Nothing to do for post-write. 523 */ 524 break; 525 526 case ID_BUFTYPE_MBUF: 527 { 528 struct mbuf *m, *m0 = cookie->id_origbuf; 529 bus_size_t minlen, moff; 530 531 /* 532 * Nothing to do for pre-read. 533 */ 534 535 if (ops & BUS_DMASYNC_PREWRITE) { 536 /* 537 * Copy the caller's buffer to the bounce buffer. 538 */ 539 m_copydata(m0, offset, len, 540 (char *)cookie->id_bouncebuf + offset); 541 } 542 543 if (ops & BUS_DMASYNC_POSTREAD) { 544 /* 545 * Copy the bounce buffer to the caller's buffer. 546 */ 547 for (moff = offset, m = m0; m != NULL && len != 0; 548 m = m->m_next) { 549 /* Find the beginning mbuf. */ 550 if (moff >= m->m_len) { 551 moff -= m->m_len; 552 continue; 553 } 554 555 /* 556 * Now at the first mbuf to sync; nail 557 * each one until we have exhausted the 558 * length. 559 */ 560 minlen = len < m->m_len - moff ? 561 len : m->m_len - moff; 562 563 memcpy(mtod(m, caddr_t) + moff, 564 (char *)cookie->id_bouncebuf + offset, 565 minlen); 566 567 moff = 0; 568 len -= minlen; 569 offset += minlen; 570 } 571 } 572 573 /* 574 * Nothing to do for post-write. 575 */ 576 break; 577 } 578 579 case ID_BUFTYPE_UIO: 580 panic("_isa_bus_dmamap_sync: ID_BUFTYPE_UIO"); 581 break; 582 583 case ID_BUFTYPE_RAW: 584 panic("_isa_bus_dmamap_sync: ID_BUFTYPE_RAW"); 585 break; 586 587 case ID_BUFTYPE_INVALID: 588 panic("_isa_bus_dmamap_sync: ID_BUFTYPE_INVALID"); 589 break; 590 591 default: 592 printf("unknown buffer type %d\n", cookie->id_buftype); 593 panic("_isa_bus_dmamap_sync"); 594 } 595 } 596 597 /* 598 * Allocate memory safe for ISA DMA. 599 */ 600 int 601 _isa_bus_dmamem_alloc(t, size, alignment, boundary, segs, nsegs, rsegs, flags) 602 bus_dma_tag_t t; 603 bus_size_t size, alignment, boundary; 604 bus_dma_segment_t *segs; 605 int nsegs; 606 int *rsegs; 607 int flags; 608 { 609 bus_dma_segment_t *ds; 610 int i, error; 611 612 if (t->_ranges == NULL) 613 return (ENOMEM); 614 615 for (i = 0, error = ENOMEM, ds = t->_ranges; 616 i < t->_nranges; i++, ds++) { 617 error = _bus_dmamem_alloc_range(t, size, alignment, boundary, 618 segs, nsegs, rsegs, flags, ds->ds_addr, 619 ds->ds_addr + ds->ds_len); 620 if (error == 0) 621 break; 622 } 623 624 return (error); 625 } 626 627 /********************************************************************** 628 * ISA DMA utility functions 629 **********************************************************************/ 630 631 int 632 _isa_dma_alloc_bouncebuf(t, map, size, flags) 633 bus_dma_tag_t t; 634 bus_dmamap_t map; 635 bus_size_t size; 636 int flags; 637 { 638 struct arm32_isa_dma_cookie *cookie = map->_dm_cookie; 639 int error = 0; 640 641 cookie->id_bouncebuflen = round_page(size); 642 error = _isa_bus_dmamem_alloc(t, cookie->id_bouncebuflen, 643 NBPG, map->_dm_boundary, cookie->id_bouncesegs, 644 map->_dm_segcnt, &cookie->id_nbouncesegs, flags); 645 if (error) 646 goto out; 647 error = _bus_dmamem_map(t, cookie->id_bouncesegs, 648 cookie->id_nbouncesegs, cookie->id_bouncebuflen, 649 (caddr_t *)&cookie->id_bouncebuf, flags); 650 651 out: 652 if (error) { 653 _bus_dmamem_free(t, cookie->id_bouncesegs, 654 cookie->id_nbouncesegs); 655 cookie->id_bouncebuflen = 0; 656 cookie->id_nbouncesegs = 0; 657 } else { 658 cookie->id_flags |= ID_HAS_BOUNCE; 659 STAT_INCR(isa_dma_stats_nbouncebufs); 660 } 661 662 return (error); 663 } 664 665 void 666 _isa_dma_free_bouncebuf(t, map) 667 bus_dma_tag_t t; 668 bus_dmamap_t map; 669 { 670 struct arm32_isa_dma_cookie *cookie = map->_dm_cookie; 671 672 STAT_DECR(isa_dma_stats_nbouncebufs); 673 674 _bus_dmamem_unmap(t, cookie->id_bouncebuf, 675 cookie->id_bouncebuflen); 676 _bus_dmamem_free(t, cookie->id_bouncesegs, 677 cookie->id_nbouncesegs); 678 cookie->id_bouncebuflen = 0; 679 cookie->id_nbouncesegs = 0; 680 cookie->id_flags &= ~ID_HAS_BOUNCE; 681 } 682