]> git.ozlabs.org Git - ppp.git/blob - ppp_ahdlc.c
b483276bd9f39939b48ece071cdafc1cc5568518
[ppp.git] / ppp_ahdlc.c
1 /*
2  * ppp_ahdlc.c - STREAMS module for doing PPP asynchronous HDLC.
3  *
4  * Copyright (c) 1994 The Australian National University.
5  * All rights reserved.
6  *
7  * Permission to use, copy, modify, and distribute this software and its
8  * documentation is hereby granted, provided that the above copyright
9  * notice appears in all copies.  This software is provided without any
10  * warranty, express or implied. The Australian National University
11  * makes no representations about the suitability of this software for
12  * any purpose.
13  *
14  * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
15  * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
16  * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
17  * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
18  * OF SUCH DAMAGE.
19  *
20  * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
21  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
22  * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
23  * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
24  * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
25  * OR MODIFICATIONS.
26  *
27  * $Id: ppp_ahdlc.c,v 1.2 1995/05/19 02:18:34 paulus Exp $
28  */
29
30 /*
31  * This file is used under Solaris 2.
32  */
33
34 #include <sys/types.h>
35 #include <sys/param.h>
36 #include <sys/stream.h>
37 #include <sys/modctl.h>
38 #include <sys/conf.h>
39 #include <sys/kmem.h>
40 #include <sys/ddi.h>
41 #include <sys/sunddi.h>
42 #include <sys/errno.h>
43 #include <sys/cmn_err.h>
44 #include <net/ppp_defs.h>
45 #include <net/pppio.h>
46
47 #define IFRAME_BSIZE    512     /* Block size to allocate for input */
48 #define OFRAME_BSIZE    4096    /* Don't allocb more than this for output */
49
50 static int ahdlc_open __P((queue_t *, dev_t *, int, int, cred_t *));
51 static int ahdlc_close __P((queue_t *, int, cred_t *));
52 static int ahdlc_wput __P((queue_t *, mblk_t *));
53 static int ahdlc_rput __P((queue_t *, mblk_t *));
54 static void stuff_frame __P((queue_t *, mblk_t *));
55 static void unstuff_chars __P((queue_t *, mblk_t *));
56
57 static struct module_info minfo = {
58     0x7d23, "ppp_ahdl", 0, INFPSZ, 4096, 128
59 };
60
61 static struct qinit rinit = {
62     ahdlc_rput, NULL, ahdlc_open, ahdlc_close, NULL, &minfo, NULL
63 };
64
65 static struct qinit winit = {
66     ahdlc_wput, NULL, NULL, NULL, NULL, &minfo, NULL
67 };
68
69 static struct streamtab ahdlc_info = {
70     &rinit, &winit, NULL, NULL
71 };
72     
73 static struct fmodsw fsw = {
74     "ppp_ahdl",
75     &ahdlc_info,
76     D_NEW | D_MP | D_MTQPAIR
77 };
78
79 extern struct mod_ops mod_strmodops;
80
81 static struct modlstrmod modlstrmod = {
82     &mod_strmodops,
83     "PPP async HDLC module",
84     &fsw
85 };
86
87 static struct modlinkage modlinkage = {
88     MODREV_1,
89     (void *) &modlstrmod,
90     NULL
91 };
92
93 struct ahdlc_state {
94     int flags;
95     mblk_t *cur_frame;
96     mblk_t *cur_blk;
97     int inlen;
98     ushort infcs;
99     u_int32_t xaccm[8];
100     u_int32_t raccm;
101     int mtu;
102     int mru;
103     int unit;
104 };
105
106 /* Values for flags */
107 #define ESCAPED         1       /* last saw escape char on input */
108 #define IFLUSH          2       /* flushing input due to error */
109
110 /*
111  * FCS lookup table as calculated by genfcstab.
112  */
113 static u_short fcstab[256] = {
114         0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
115         0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
116         0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
117         0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
118         0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
119         0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
120         0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
121         0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
122         0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
123         0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
124         0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
125         0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
126         0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
127         0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
128         0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
129         0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
130         0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
131         0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
132         0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
133         0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
134         0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
135         0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
136         0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
137         0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
138         0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
139         0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
140         0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
141         0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
142         0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
143         0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
144         0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
145         0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
146 };
147
148 /*
149  * Entry points for modloading.
150  */
151 int
152 _init(void)
153 {
154     return mod_install(&modlinkage);
155 }
156
157 int
158 _fini(void)
159 {
160     return mod_remove(&modlinkage);
161 }
162
163 int
164 _info(mip)
165     struct modinfo *mip;
166 {
167     return mod_info(&modlinkage, mip);
168 }
169
170 /*
171  * STREAMS module entry points.
172  */
173 static int
174 ahdlc_open(q, devp, flag, sflag, credp)
175     queue_t *q;
176     dev_t *devp;
177     int flag, sflag;
178     cred_t *credp;
179 {
180     struct ahdlc_state *sp;
181
182     if (q->q_ptr == 0) {
183         sp = (struct ahdlc_state *) kmem_zalloc(sizeof(struct ahdlc_state),
184                                                 KM_SLEEP);
185         if (sp == 0)
186             return ENOSR;
187         q->q_ptr = sp;
188         WR(q)->q_ptr = sp;
189         sp->xaccm[0] = ~0;
190         sp->xaccm[3] = 0x60000000;
191         sp->mru = 1500;
192         qprocson(q);
193     }
194     return 0;
195 }
196
197 static int
198 ahdlc_close(q, flag, credp)
199     queue_t *q;
200     int flag;
201     cred_t *credp;
202 {
203     struct ahdlc_state *state;
204
205     qprocsoff(q);
206     if (q->q_ptr != 0) {
207         state = (struct ahdlc_state *) q->q_ptr;
208         if (state->cur_frame != 0) {
209             freemsg(state->cur_frame);
210             state->cur_frame = 0;
211         }
212         kmem_free(q->q_ptr, sizeof(struct ahdlc_state));
213     }
214     return 0;
215 }
216
217 static int
218 ahdlc_wput(q, mp)
219     queue_t *q;
220     mblk_t *mp;
221 {
222     struct ahdlc_state *state;
223     struct iocblk *iop;
224     int error;
225
226     state = (struct ahdlc_state *) q->q_ptr;
227     switch (mp->b_datap->db_type) {
228     case M_DATA:
229         /*
230          * A data packet - do character-stuffing and FCS, and
231          * send it onwards.
232          */
233         stuff_frame(q, mp);
234         freemsg(mp);
235         break;
236
237     case M_IOCTL:
238         iop = (struct iocblk *) mp->b_rptr;
239         error = EINVAL;
240         switch (iop->ioc_cmd) {
241         case PPPIO_XACCM:
242             if (iop->ioc_count < sizeof(u_int32_t)
243                 || iop->ioc_count > sizeof(ext_accm))
244                 break;
245             bcopy(mp->b_cont->b_rptr, (caddr_t)state->xaccm, iop->ioc_count);
246             state->xaccm[2] &= 0x40000000;      /* don't escape 0x5e */
247             state->xaccm[3] |= 0x60000000;      /* do escape 0x7d, 0x7e */
248             iop->ioc_count = 0;
249             error = 0;
250             break;
251
252         case PPPIO_RACCM:
253             if (iop->ioc_count != sizeof(u_int32_t))
254                 break;
255             bcopy(mp->b_cont->b_rptr, (caddr_t)&state->raccm,
256                   sizeof(u_int32_t));
257             iop->ioc_count = 0;
258             error = 0;
259             break;
260
261         default:
262             error = -1;
263             break;
264         }
265
266         if (error < 0)
267             putnext(q, mp);
268         else if (error == 0) {
269             mp->b_datap->db_type = M_IOCACK;
270             qreply(q, mp);
271         } else {
272             mp->b_datap->db_type = M_IOCNAK;
273             iop->ioc_count = 0;
274             iop->ioc_error = error;
275             qreply(q, mp);
276         }
277         break;
278
279     case M_CTL:
280         switch (*mp->b_rptr) {
281         case PPPCTL_MTU:
282             state->mtu = ((unsigned short *)mp->b_rptr)[1];
283             freemsg(mp);
284             break;
285         case PPPCTL_MRU:
286             state->mru = ((unsigned short *)mp->b_rptr)[1];
287             freemsg(mp);
288             break;
289         case PPPCTL_UNIT:
290             state->unit = mp->b_rptr[1];
291             break;
292         default:
293             putnext(q, mp);
294         }
295         break;
296
297     default:
298         putnext(q, mp);
299     }
300     return 0;
301 }
302
303 static int
304 ahdlc_rput(q, mp)
305     queue_t *q;
306     mblk_t *mp;
307 {
308     mblk_t *np;
309     uchar_t *cp;
310     struct ahdlc_state *state;
311
312     switch (mp->b_datap->db_type) {
313     case M_DATA:
314         unstuff_chars(q, mp);
315         freemsg(mp);
316         break;
317
318     case M_HANGUP:
319         state = (struct ahdlc_state *) q->q_ptr;
320         if (state->cur_frame != 0) {
321             /* XXX would like to send this up for debugging */
322             freemsg(state->cur_frame);
323             state->cur_frame = 0;
324             state->cur_blk = 0;
325         }
326         state->inlen = 0;
327         state->flags = IFLUSH;
328         putnext(q, mp);
329         break;
330
331     default:
332         putnext(q, mp);
333     }
334     return 0;
335 }
336
337 /* Extract bit c from map m, to determine if c needs to be escaped. */
338 #define ESCAPE(c, m)    ((m)[(c) >> 5] & (1 << ((c) & 0x1f)))
339
340 static void
341 stuff_frame(q, mp)
342     queue_t *q;
343     mblk_t *mp;
344 {
345     struct ahdlc_state *state;
346     int ilen, olen, c, extra;
347     mblk_t *omsg, *np, *op;
348     uchar_t *sp, *sp0, *dp, *dp0, *spend;
349     ushort_t fcs;
350     u_int32_t *xaccm, lcp_xaccm[8];
351
352     /*
353      * We estimate the length of the output packet as
354      * 1.25 * input length + 16 (for initial flag, FCS, final flag, slop).
355      */
356     state = (struct ahdlc_state *) q->q_ptr;
357     ilen = msgdsize(mp);
358     olen = ilen + (ilen >> 2) + 16;
359     if (olen > OFRAME_BSIZE)
360         olen = OFRAME_BSIZE;
361     omsg = op = allocb(olen, BPRI_MED);
362     if (omsg == 0)
363         return;
364
365     /*
366      * Put in an initial flag for now.  We'll remove it later
367      * if we decide we don't need it.
368      */
369     dp = op->b_wptr;
370     *dp++ = PPP_FLAG;
371     --olen;
372
373     /*
374      * For LCP packets, we must escape all control characters.
375      * LCP packets must not be A/C or protocol compressed.
376      */
377     xaccm = state->xaccm;
378     if (ilen >= PPP_HDRLEN) {
379         if (mp->b_wptr - mp->b_rptr >= PPP_HDRLEN
380             || pullupmsg(mp, PPP_HDRLEN)) {
381             if (PPP_ADDRESS(mp->b_rptr) == PPP_ALLSTATIONS
382                 && PPP_CONTROL(mp->b_rptr) == PPP_UI
383                 && PPP_PROTOCOL(mp->b_rptr) == PPP_LCP) {
384                 bcopy((caddr_t) state->xaccm, (caddr_t) lcp_xaccm,
385                       sizeof(lcp_xaccm));
386                 lcp_xaccm[0] = ~0;
387                 xaccm = lcp_xaccm;
388             }
389         }
390     }
391
392     sp = mp->b_rptr;
393     fcs = PPP_INITFCS;
394     for (;;) {
395         spend = mp->b_wptr;
396         extra = sp + olen - spend;
397         if (extra < 0) {
398             spend = sp + olen;
399             extra = 0;
400         }
401         /*
402          * We can safely process the input up to `spend'
403          * without overrunning the output, provided we don't
404          * hit more than `extra' characters which need to be escaped.
405          */
406         sp0 = sp;
407         dp0 = dp;
408         while (sp < spend) {
409             c = *sp;
410             if (ESCAPE(c, xaccm)) {
411                 if (extra > 0)
412                     --extra;
413                 else if (sp < spend - 1)
414                     --spend;
415                 else
416                     break;
417                 fcs = PPP_FCS(fcs, c);
418                 *dp++ = PPP_ESCAPE;
419                 c ^= PPP_TRANS;
420             } else
421                 fcs = PPP_FCS(fcs, c);
422             *dp++ = c;
423             ++sp;
424         }
425         ilen -= sp - sp0;
426         olen -= dp - dp0;
427
428         /*
429          * At this point, we have emptied an input block
430          * and/or filled an output block.
431          */
432         if (sp >= mp->b_wptr) {
433             /*
434              * We've emptied an input block.  Advance to the next.
435              */
436             mp = mp->b_cont;
437             if (mp == 0)
438                 break;          /* all done */
439             sp = mp->b_rptr;
440         }
441         if (olen < 2) {
442             /*
443              * The output block is full.  Allocate a new one.
444              */
445             op->b_wptr = dp;
446             olen = 2 * ilen + 5;
447             if (olen > OFRAME_BSIZE)
448                 olen = OFRAME_BSIZE;
449             np = allocb(olen, BPRI_MED);
450             if (np == 0) {
451                 freemsg(omsg);
452                 return;
453             }
454             op->b_cont = np;
455             op = np;
456             dp = op->b_wptr;
457         }
458     }
459
460     /*
461      * Append the FCS and closing flag.
462      * This could require up to 5 characters.
463      */
464     if (olen < 5) {
465         /* Sigh.  Need another block. */
466         op->b_wptr = dp;
467         np = allocb(5, BPRI_MED);
468         if (np == 0) {
469             freemsg(omsg);
470             return;
471         }
472         op->b_cont = np;
473         op = np;
474         dp = op->b_wptr;
475     }
476     c = ~fcs & 0xff;
477     if (ESCAPE(c, xaccm)) {
478         *dp++ = PPP_ESCAPE;
479         c ^= PPP_TRANS;
480     }
481     *dp++ = c;
482     c = (~fcs >> 8) & 0xff;
483     if (ESCAPE(c, xaccm)) {
484         *dp++ = PPP_ESCAPE;
485         c ^= PPP_TRANS;
486     }
487     *dp++ = c;
488     *dp++ = PPP_FLAG;
489     op->b_wptr = dp;
490
491     /*
492      * Remove the initial flag, if possible.
493      */
494     if (qsize(q->q_next) > 0)
495         ++omsg->b_rptr;
496
497     putnext(q, omsg);
498 }
499
500 static void
501 unstuff_chars(q, mp)
502     queue_t *q;
503     mblk_t *mp;
504 {
505     struct ahdlc_state *state;
506     mblk_t *om;
507     uchar_t *cp, *cpend, *dp, *dp0;
508     int c, len, extra, offset;
509     ushort_t fcs;
510
511     state = (struct ahdlc_state *) q->q_ptr;
512     cp = mp->b_rptr;
513     for (;;) {
514         /*
515          * Advance to next input block if necessary.
516          */
517         if (cp >= mp->b_wptr) {
518             mp = mp->b_cont;
519             if (mp == 0)
520                 break;
521             cp = mp->b_rptr;
522             continue;
523         }
524
525         if ((state->flags & (IFLUSH|ESCAPED)) == 0
526             && state->inlen >= PPP_HDRLEN
527             && (om = state->cur_blk) != 0) {
528             /*
529              * Process bulk chars as quickly as possible.
530              */
531             dp = om->b_wptr;
532             len = om->b_datap->db_lim - dp; /* max # output bytes */
533             extra = (mp->b_wptr - cp) - len;/* #input chars - #output bytes */
534             if (extra < 0) {
535                 len += extra;               /* we'll run out of input first */
536                 extra = 0;
537             }
538             cpend = cp + len;
539             dp0 = dp;
540             fcs = state->infcs;
541             while (cp < cpend) {
542                 c = *cp;
543                 if (c == PPP_FLAG)
544                     break;
545                 ++cp;
546                 if (c == PPP_ESCAPE) {
547                     if (extra > 0) {
548                         --extra;
549                         ++cpend;
550                     } else if (cp >= cpend) {
551                         state->flags |= ESCAPED;
552                         break;
553                     }
554                     c = *cp;
555                     if (c == PPP_FLAG)
556                         break;
557                     ++cp;
558                     c ^= PPP_TRANS;
559                 }
560                 *dp++ = c;
561                 fcs = PPP_FCS(fcs, c);
562             }
563             state->inlen += dp - dp0;
564             state->infcs = fcs;
565             om->b_wptr = dp;
566             if (cp >= cpend)
567                 continue;       /* go back and check cp again */
568         }
569
570         c = *cp++;
571         if (c == PPP_FLAG) {
572             /*
573              * End of a frame.
574              * If the ESCAPE flag is set, the frame ended with
575              * the frame abort sequence "}~".
576              */
577             om = state->cur_frame;
578             len = state->inlen;
579             state->cur_frame = 0;
580             state->inlen = 0;
581             if (om == 0)
582                 continue;
583             if (!(state->flags & (IFLUSH|ESCAPED) || len < PPP_FCSLEN)) {
584                 if (state->infcs == PPP_GOODFCS) {
585                     adjmsg(om, -PPP_FCSLEN);    /* chop off fcs */
586                     putnext(q, om);             /* bombs away! */
587                     continue;
588                 }
589                 /* incr bad fcs stats */
590 #if DEBUG
591                 cmn_err(CE_CONT, "ppp_ahdl: bad fcs %x\n", state->infcs);
592 #endif
593             }
594             freemsg(om);
595             putctl1(q->q_next, M_CTL, PPPCTL_IERROR);
596             continue;
597         }
598
599         if (state->flags & IFLUSH)
600             continue;
601         if (state->flags & ESCAPED) {
602             c ^= PPP_TRANS;
603             state->flags &= ~ESCAPED;
604         } else if (c == PPP_ESCAPE) {
605             state->flags |= ESCAPED;
606             continue;
607         }
608         if (state->inlen == 0) {
609             /*
610              * First byte of the frame: allocate the first message block.
611              */
612             om = allocb(IFRAME_BSIZE, BPRI_MED);
613             if (om == 0) {
614                 state->flags |= IFLUSH;
615                 continue;
616             }
617             state->cur_frame = om;
618             state->cur_blk = om;
619             state->infcs = PPP_INITFCS;
620         } else {
621             om = state->cur_blk;
622             if (om->b_wptr >= om->b_datap->db_lim) {
623                 /*
624                  * Current message block is full.  Allocate another one,
625                  * unless we have run out of MRU.
626                  */
627                 if (state->inlen >= state->mru + PPP_HDRLEN + PPP_FCSLEN) {
628 #if DEBUG
629                     cmn_err(CE_CONT, "ppp_ahdl: frame too long (%d)\n",
630                             state->inlen);
631 #endif
632                     state->flags |= IFLUSH;
633                     continue;
634                 }
635                 om = allocb(IFRAME_BSIZE, BPRI_MED);
636                 if (om == 0) {
637                     state->flags |= IFLUSH;
638                     continue;
639                 }
640                 state->cur_blk->b_cont = om;
641                 state->cur_blk = om;
642             }
643         }
644         *om->b_wptr++ = c;
645         ++state->inlen;
646         state->infcs = PPP_FCS(state->infcs, c);
647
648         if (state->inlen == PPP_HDRLEN) {
649             /*
650              * We don't do address/control & protocol decompression here,
651              * but we do leave space for the decompressed fields and
652              * arrange for the info field to start on a word boundary.
653              */
654             dp = om->b_rptr;
655             if (PPP_ADDRESS(dp) == PPP_ALLSTATIONS
656                 && PPP_CONTROL(dp) == PPP_UI)
657                 dp += 2;
658             if ((*dp & 1) == 0)
659                 ++dp;
660             /* dp is now pointing at the last byte of the ppp protocol field */
661             offset = 3 - ((unsigned)dp & 3);
662             if (offset > 0) {
663                 dp = om->b_wptr;
664                 do {
665                     --dp;
666                     dp[offset] = dp[0];
667                 } while (dp > om->b_rptr);
668                 om->b_rptr += offset;
669                 om->b_wptr += offset;
670             }
671         }
672     }
673 }
674