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