isadma_bounce.c revision 1.7 1 /* $NetBSD: isadma_bounce.c,v 1.7 2005/01/22 07:35:34 tsutsui 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.7 2005/01/22 07:35:34 tsutsui 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(bus_dma_tag_t, bus_dmamap_t,
117 bus_size_t, int);
118 static void isadma_bounce_free_bouncebuf(bus_dma_tag_t, bus_dmamap_t);
119
120 void
121 isadma_bounce_tag_init(bus_dma_tag_t t)
122 {
123 /*
124 * Initialize the DMA tag used for ISA DMA.
125 */
126
127 _bus_dma_tag_init(t);
128
129 t->_dmamap_create = isadma_bounce_dmamap_create;
130 t->_dmamap_destroy = isadma_bounce_dmamap_destroy;
131 t->_dmamap_load = isadma_bounce_dmamap_load;
132 t->_dmamap_load_mbuf = isadma_bounce_dmamap_load_mbuf;
133 t->_dmamap_load_uio = isadma_bounce_dmamap_load_uio;
134 t->_dmamap_load_raw = isadma_bounce_dmamap_load_raw;
135 t->_dmamap_unload = isadma_bounce_dmamap_unload;
136 t->_dmamap_sync = isadma_bounce_dmamap_sync;
137 t->_dmamem_alloc = isadma_bounce_dmamem_alloc;
138 }
139
140 /*
141 * Create an ISA DMA map.
142 */
143 int
144 isadma_bounce_dmamap_create(bus_dma_tag_t t, bus_size_t size, int nsegments,
145 bus_size_t maxsegsz, bus_size_t boundary, int flags, bus_dmamap_t *dmamp)
146 {
147 struct isadma_bounce_cookie *cookie;
148 bus_dmamap_t map;
149 int error, cookieflags;
150 void *cookiestore;
151 size_t cookiesize;
152
153 /* Call common function to create the basic map. */
154 error = _bus_dmamap_create(t, size, nsegments, maxsegsz, boundary,
155 flags, dmamp);
156 if (error)
157 return error;
158
159 map = *dmamp;
160 map->_dm_cookie = NULL;
161
162 cookiesize = sizeof(*cookie);
163
164 /*
165 * ISA only has 24-bits of address space. This means
166 * we can't DMA to pages over 16M. In order to DMA to
167 * arbitrary buffers, we use "bounce buffers" - pages
168 * in memory below the 16M boundary. On DMA reads,
169 * DMA happens to the bounce buffers, and is copied into
170 * the caller's buffer. On writes, data is copied into
171 * but bounce buffer, and the DMA happens from those
172 * pages. To software using the DMA mapping interface,
173 * this looks simply like a data cache.
174 *
175 * If we have more than 16M of RAM in the system, we may
176 * need bounce buffers. We check and remember that here.
177 *
178 * ...or, there is an opposite case. The most segments
179 * a transfer will require is (maxxfer / PAGE_SIZE) + 1. If
180 * the caller can't handle that many segments (e.g. the
181 * ISA DMA controller), we may have to bounce it as well.
182 */
183 cookieflags = 0;
184 if (avail_end > ISA_DMA_BOUNCE_THRESHOLD ||
185 ((map->_dm_size / PAGE_SIZE) + 1) > map->_dm_segcnt) {
186 cookieflags |= ID_MIGHT_NEED_BOUNCE;
187 cookiesize += (sizeof(bus_dma_segment_t) *
188 (map->_dm_segcnt - 1));
189 }
190
191 /*
192 * Allocate our cookie.
193 */
194 if ((cookiestore = malloc(cookiesize, M_DMAMAP,
195 (flags & BUS_DMA_NOWAIT) ? M_NOWAIT : M_WAITOK)) == NULL) {
196 error = ENOMEM;
197 goto out;
198 }
199 memset(cookiestore, 0, cookiesize);
200 cookie = (struct isadma_bounce_cookie *)cookiestore;
201 cookie->id_flags = cookieflags;
202 map->_dm_cookie = cookie;
203
204 if (cookieflags & ID_MIGHT_NEED_BOUNCE) {
205 /*
206 * Allocate the bounce pages now if the caller
207 * wishes us to do so.
208 */
209 if ((flags & BUS_DMA_ALLOCNOW) == 0)
210 goto out;
211
212 error = isadma_bounce_alloc_bouncebuf(t, map, size, flags);
213 }
214
215 out:
216 if (error) {
217 if (map->_dm_cookie != NULL)
218 free(map->_dm_cookie, M_DMAMAP);
219 _bus_dmamap_destroy(t, map);
220 }
221 return error;
222 }
223
224 /*
225 * Destroy an ISA DMA map.
226 */
227 void
228 isadma_bounce_dmamap_destroy(bus_dma_tag_t t, bus_dmamap_t map)
229 {
230 struct isadma_bounce_cookie *cookie = map->_dm_cookie;
231
232 /*
233 * Free any bounce pages this map might hold.
234 */
235 if (cookie->id_flags & ID_HAS_BOUNCE)
236 isadma_bounce_free_bouncebuf(t, map);
237
238 free(cookie, M_DMAMAP);
239 _bus_dmamap_destroy(t, map);
240 }
241
242 /*
243 * Load an ISA DMA map with a linear buffer.
244 */
245 int
246 isadma_bounce_dmamap_load(bus_dma_tag_t t, bus_dmamap_t map, void *buf,
247 bus_size_t buflen, struct proc *p, int flags)
248 {
249 struct isadma_bounce_cookie *cookie = map->_dm_cookie;
250 int error;
251
252 /*
253 * Make sure that on error condition we return "no valid mappings."
254 */
255 map->dm_mapsize = 0;
256 map->dm_nsegs = 0;
257
258 /*
259 * Try to load the map the normal way. If this errors out,
260 * and we can bounce, we will.
261 */
262 error = _bus_dmamap_load(t, map, buf, buflen, p, flags);
263 if (error == 0 ||
264 (error != 0 && (cookie->id_flags & ID_MIGHT_NEED_BOUNCE) == 0))
265 return error;
266
267 /*
268 * First attempt failed; bounce it.
269 */
270
271 /*
272 * Allocate bounce pages, if necessary.
273 */
274 if ((cookie->id_flags & ID_HAS_BOUNCE) == 0) {
275 error = isadma_bounce_alloc_bouncebuf(t, map, buflen, flags);
276 if (error)
277 return error;
278 }
279
280 /*
281 * Cache a pointer to the caller's buffer and load the DMA map
282 * with the bounce buffer.
283 */
284 cookie->id_origbuf = buf;
285 cookie->id_origbuflen = buflen;
286 cookie->id_buftype = ID_BUFTYPE_LINEAR;
287 error = _bus_dmamap_load(t, map, cookie->id_bouncebuf, buflen,
288 p, flags);
289 if (error) {
290 /*
291 * Free the bounce pages, unless our resources
292 * are reserved for our exclusive use.
293 */
294 if ((map->_dm_flags & BUS_DMA_ALLOCNOW) == 0)
295 isadma_bounce_free_bouncebuf(t, map);
296 return error;
297 }
298
299 /* ...so isadma_bounce_dmamap_sync() knows we're bouncing */
300 cookie->id_flags |= ID_IS_BOUNCING;
301 return 0;
302 }
303
304 /*
305 * Like isadma_bounce_dmamap_load(), but for mbufs.
306 */
307 int
308 isadma_bounce_dmamap_load_mbuf(bus_dma_tag_t t, bus_dmamap_t map,
309 struct mbuf *m0, int flags)
310 {
311 struct isadma_bounce_cookie *cookie = map->_dm_cookie;
312 int error;
313
314 /*
315 * Make sure on error condition we return "no valid mappings."
316 */
317 map->dm_mapsize = 0;
318 map->dm_nsegs = 0;
319
320 #ifdef DIAGNOSTIC
321 if ((m0->m_flags & M_PKTHDR) == 0)
322 panic("isadma_bounce_dmamap_load_mbuf: no packet header");
323 #endif
324
325 if (m0->m_pkthdr.len > map->_dm_size)
326 return EINVAL;
327
328 /*
329 * Try to load the map the normal way. If this errors out,
330 * and we can bounce, we will.
331 */
332 error = _bus_dmamap_load_mbuf(t, map, m0, flags);
333 if (error == 0 ||
334 (error != 0 && (cookie->id_flags & ID_MIGHT_NEED_BOUNCE) == 0))
335 return error;
336
337 /*
338 * First attempt failed; bounce it.
339 */
340
341 /*
342 * Allocate bounce pages, if necessary.
343 */
344 if ((cookie->id_flags & ID_HAS_BOUNCE) == 0) {
345 error = isadma_bounce_alloc_bouncebuf(t, map, m0->m_pkthdr.len,
346 flags);
347 if (error)
348 return error;
349 }
350
351 /*
352 * Cache a pointer to the caller's buffer and load the DMA map
353 * with the bounce buffer.
354 */
355 cookie->id_origbuf = m0;
356 cookie->id_origbuflen = m0->m_pkthdr.len; /* not really used */
357 cookie->id_buftype = ID_BUFTYPE_MBUF;
358 error = _bus_dmamap_load(t, map, cookie->id_bouncebuf,
359 m0->m_pkthdr.len, NULL, flags);
360 if (error) {
361 /*
362 * Free the bounce pages, unless our resources
363 * are reserved for our exclusive use.
364 */
365 if ((map->_dm_flags & BUS_DMA_ALLOCNOW) == 0)
366 isadma_bounce_free_bouncebuf(t, map);
367 return error;
368 }
369
370 /* ...so isadma_bounce_dmamap_sync() knows we're bouncing */
371 cookie->id_flags |= ID_IS_BOUNCING;
372 return 0;
373 }
374
375 /*
376 * Like isadma_bounce_dmamap_load(), but for uios.
377 */
378 int
379 isadma_bounce_dmamap_load_uio(bus_dma_tag_t t, bus_dmamap_t map,
380 struct uio *uio, int flags)
381 {
382
383 panic("isadma_bounce_dmamap_load_uio: not implemented");
384 }
385
386 /*
387 * Like isadma_bounce_dmamap_load(), but for raw memory allocated with
388 * bus_dmamem_alloc().
389 */
390 int
391 isadma_bounce_dmamap_load_raw(bus_dma_tag_t t, bus_dmamap_t map,
392 bus_dma_segment_t *segs, int nsegs, bus_size_t size, int flags)
393 {
394
395 panic("isadma_bounce_dmamap_load_raw: not implemented");
396 }
397
398 /*
399 * Unload an ISA DMA map.
400 */
401 void
402 isadma_bounce_dmamap_unload(bus_dma_tag_t t, bus_dmamap_t map)
403 {
404 struct isadma_bounce_cookie *cookie = map->_dm_cookie;
405
406 /*
407 * If we have bounce pages, free them, unless they're
408 * reserved for our exclusive use.
409 */
410 if ((cookie->id_flags & ID_HAS_BOUNCE) &&
411 (map->_dm_flags & BUS_DMA_ALLOCNOW) == 0)
412 isadma_bounce_free_bouncebuf(t, map);
413
414 cookie->id_flags &= ~ID_IS_BOUNCING;
415 cookie->id_buftype = ID_BUFTYPE_INVALID;
416
417 /*
418 * Do the generic bits of the unload.
419 */
420 _bus_dmamap_unload(t, map);
421 }
422
423 /*
424 * Synchronize an ISA DMA map.
425 */
426 void
427 isadma_bounce_dmamap_sync(bus_dma_tag_t t, bus_dmamap_t map, bus_addr_t offset,
428 bus_size_t len, int ops)
429 {
430 struct isadma_bounce_cookie *cookie = map->_dm_cookie;
431 void (*sync)(bus_dma_tag_t, bus_dmamap_t, bus_addr_t, bus_size_t, int);
432
433 sync = _bus_dmamap_sync;
434
435 /*
436 * Mixing PRE and POST operations is not allowed.
437 */
438 if ((ops & (BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE)) != 0 &&
439 (ops & (BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE)) != 0)
440 panic("isadma_bounce_dmamap_sync: mix PRE and POST");
441
442 #ifdef DIAGNOSTIC
443 if ((ops & (BUS_DMASYNC_PREWRITE|BUS_DMASYNC_POSTREAD)) != 0) {
444 if (offset >= map->dm_mapsize)
445 panic("isadma_bounce_dmamap_sync: bad offset");
446 if (len == 0 || (offset + len) > map->dm_mapsize)
447 panic("isadma_bounce_dmamap_sync: bad length");
448 }
449 #endif
450
451 /*
452 * If we're not bouncing, just drain the write buffer
453 * and flush cache.
454 */
455 if ((cookie->id_flags & ID_IS_BOUNCING) == 0) {
456 ((*sync)(t, map, offset, len, ops));
457 return;
458 }
459
460 /*
461 * XXX
462 * This should be needed in BUS_DMASYNC_POSTREAD case only,
463 * if _mips3_bus_dmamap_sync() used "Hit_Invalidate on POSTREAD",
464 * rather than "Hit_Write_Back_Invalidate on PREREAD".
465 */
466 if (ops & (BUS_DMASYNC_PREREAD|BUS_DMASYNC_POSTREAD))
467 ((*sync)(t, map, offset, len, ops));
468
469 switch (cookie->id_buftype) {
470 case ID_BUFTYPE_LINEAR:
471 /*
472 * Nothing to do for pre-read.
473 */
474
475 if (ops & BUS_DMASYNC_PREWRITE) {
476 /*
477 * Copy the caller's buffer to the bounce buffer.
478 */
479 memcpy((char *)cookie->id_bouncebuf + offset,
480 (char *)cookie->id_origbuf + offset, len);
481 }
482
483 if (ops & BUS_DMASYNC_POSTREAD) {
484 /*
485 * Copy the bounce buffer to the caller's buffer.
486 */
487 memcpy((char *)cookie->id_origbuf + offset,
488 (char *)cookie->id_bouncebuf + offset, len);
489 }
490
491 /*
492 * Nothing to do for post-write.
493 */
494 break;
495
496 case ID_BUFTYPE_MBUF:
497 {
498 struct mbuf *m, *m0 = cookie->id_origbuf;
499 bus_size_t minlen, moff;
500
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 m_copydata(m0, offset, len,
510 (char *)cookie->id_bouncebuf + offset);
511 }
512
513 if (ops & BUS_DMASYNC_POSTREAD) {
514 /*
515 * Copy the bounce buffer to the caller's buffer.
516 */
517 for (moff = offset, m = m0; m != NULL && len != 0;
518 m = m->m_next) {
519 /* Find the beginning mbuf. */
520 if (moff >= m->m_len) {
521 moff -= m->m_len;
522 continue;
523 }
524
525 /*
526 * Now at the first mbuf to sync; nail
527 * each one until we have exhausted the
528 * length.
529 */
530 minlen = len < m->m_len - moff ?
531 len : m->m_len - moff;
532
533 memcpy(mtod(m, caddr_t) + moff,
534 (char *)cookie->id_bouncebuf + offset,
535 minlen);
536
537 moff = 0;
538 len -= minlen;
539 offset += minlen;
540 }
541 }
542
543 /*
544 * Nothing to do for post-write.
545 */
546 break;
547 }
548
549 case ID_BUFTYPE_UIO:
550 panic("isadma_bounce_dmamap_sync: ID_BUFTYPE_UIO");
551 break;
552
553 case ID_BUFTYPE_RAW:
554 panic("isadma_bounce_dmamap_sync: ID_BUFTYPE_RAW");
555 break;
556
557 case ID_BUFTYPE_INVALID:
558 panic("isadma_bounce_dmamap_sync: ID_BUFTYPE_INVALID");
559 break;
560
561 default:
562 printf("unknown buffer type %d\n", cookie->id_buftype);
563 panic("isadma_bounce_dmamap_sync");
564 }
565
566 if (ops & BUS_DMASYNC_PREWRITE)
567 ((*sync)(t, map, offset, len, ops));
568 }
569
570 /*
571 * Allocate memory safe for ISA DMA.
572 */
573 int
574 isadma_bounce_dmamem_alloc(bus_dma_tag_t t, bus_size_t size,
575 bus_size_t alignment, bus_size_t boundary, bus_dma_segment_t *segs,
576 int nsegs, int *rsegs, int flags)
577 {
578 paddr_t high;
579
580 if (avail_end > ISA_DMA_BOUNCE_THRESHOLD)
581 high = trunc_page(ISA_DMA_BOUNCE_THRESHOLD);
582 else
583 high = trunc_page(avail_end);
584
585 return _bus_dmamem_alloc_range(t, size, alignment, boundary,
586 segs, nsegs, rsegs, flags, 0, high);
587 }
588
589 /**********************************************************************
590 * ISA DMA utility functions
591 **********************************************************************/
592
593 static int
594 isadma_bounce_alloc_bouncebuf(bus_dma_tag_t t, bus_dmamap_t map,
595 bus_size_t size, int flags)
596 {
597 struct isadma_bounce_cookie *cookie = map->_dm_cookie;
598 int error = 0;
599
600 cookie->id_bouncebuflen = round_page(size);
601 error = isadma_bounce_dmamem_alloc(t, cookie->id_bouncebuflen,
602 PAGE_SIZE, map->_dm_boundary, cookie->id_bouncesegs,
603 map->_dm_segcnt, &cookie->id_nbouncesegs, flags);
604 if (error)
605 goto out;
606 error = _bus_dmamem_map(t, cookie->id_bouncesegs,
607 cookie->id_nbouncesegs, cookie->id_bouncebuflen,
608 (caddr_t *)&cookie->id_bouncebuf, flags);
609
610 out:
611 if (error) {
612 _bus_dmamem_free(t, cookie->id_bouncesegs,
613 cookie->id_nbouncesegs);
614 cookie->id_bouncebuflen = 0;
615 cookie->id_nbouncesegs = 0;
616 } else
617 cookie->id_flags |= ID_HAS_BOUNCE;
618
619 return error;
620 }
621
622 static void
623 isadma_bounce_free_bouncebuf(bus_dma_tag_t t, bus_dmamap_t map)
624 {
625 struct isadma_bounce_cookie *cookie = map->_dm_cookie;
626
627 _bus_dmamem_unmap(t, cookie->id_bouncebuf,
628 cookie->id_bouncebuflen);
629 _bus_dmamem_free(t, cookie->id_bouncesegs,
630 cookie->id_nbouncesegs);
631 cookie->id_bouncebuflen = 0;
632 cookie->id_nbouncesegs = 0;
633 cookie->id_flags &= ~ID_HAS_BOUNCE;
634 }
635