2 * Copyright (c) 2003 Ian Dowse. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * Generic message buffer support routines.
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/msgbuf.h>
36 /* Read/write sequence numbers are modulo a multiple of the buffer size. */
37 #define SEQMOD(size) ((size) * 16)
39 static u_int
msgbuf_cksum(struct msgbuf
*mbp
);
42 * Initialize a message buffer of the specified size at the specified
43 * location. This also zeros the buffer area.
46 msgbuf_init(struct msgbuf
*mbp
, void *ptr
, int size
)
51 mbp
->msg_seqmod
= SEQMOD(size
);
53 mbp
->msg_magic
= MSG_MAGIC
;
57 * Reinitialize a message buffer, retaining its previous contents if
58 * the size and checksum are correct. If the old contents cannot be
59 * recovered, the message buffer is cleared.
62 msgbuf_reinit(struct msgbuf
*mbp
, void *ptr
, int size
)
66 if (mbp
->msg_magic
!= MSG_MAGIC
|| mbp
->msg_size
!= size
) {
67 msgbuf_init(mbp
, ptr
, size
);
70 mbp
->msg_seqmod
= SEQMOD(size
);
71 mbp
->msg_wseq
= MSGBUF_SEQNORM(mbp
, mbp
->msg_wseq
);
72 mbp
->msg_rseq
= MSGBUF_SEQNORM(mbp
, mbp
->msg_rseq
);
74 cksum
= msgbuf_cksum(mbp
);
75 if (cksum
!= mbp
->msg_cksum
) {
77 printf("msgbuf cksum mismatch (read %x, calc %x)\n",
78 mbp
->msg_cksum
, cksum
);
79 printf("Old msgbuf not recovered\n");
86 * Clear the message buffer.
89 msgbuf_clear(struct msgbuf
*mbp
)
92 bzero(mbp
->msg_ptr
, mbp
->msg_size
);
99 * Get a count of the number of unread characters in the message buffer.
102 msgbuf_getcount(struct msgbuf
*mbp
)
106 len
= MSGBUF_SEQSUB(mbp
, mbp
->msg_wseq
, mbp
->msg_rseq
);
107 if (len
> mbp
->msg_size
)
113 * Append a character to a message buffer. This function can be
114 * considered fully reentrant so long as the number of concurrent
115 * callers is less than the number of characters in the buffer.
116 * However, the message buffer is only guaranteed to be consistent
117 * for reading when there are no callers in this function.
120 msgbuf_addchar(struct msgbuf
*mbp
, int c
)
122 u_int new_seq
, pos
, seq
;
126 new_seq
= MSGBUF_SEQNORM(mbp
, seq
+ 1);
127 } while (atomic_cmpset_rel_int(&mbp
->msg_wseq
, seq
, new_seq
) == 0);
128 pos
= MSGBUF_SEQ_TO_POS(mbp
, seq
);
129 atomic_add_int(&mbp
->msg_cksum
, (u_int
)(u_char
)c
-
130 (u_int
)(u_char
)mbp
->msg_ptr
[pos
]);
131 mbp
->msg_ptr
[pos
] = c
;
135 * Read and mark as read a character from a message buffer.
136 * Returns the character, or -1 if no characters are available.
139 msgbuf_getchar(struct msgbuf
*mbp
)
144 wseq
= mbp
->msg_wseq
;
145 len
= MSGBUF_SEQSUB(mbp
, wseq
, mbp
->msg_rseq
);
148 if (len
> mbp
->msg_size
)
149 mbp
->msg_rseq
= MSGBUF_SEQNORM(mbp
, wseq
- mbp
->msg_size
);
150 c
= (u_char
)mbp
->msg_ptr
[MSGBUF_SEQ_TO_POS(mbp
, mbp
->msg_rseq
)];
151 mbp
->msg_rseq
= MSGBUF_SEQNORM(mbp
, mbp
->msg_rseq
+ 1);
156 * Read and mark as read a number of characters from a message buffer.
157 * Returns the number of characters that were placed in `buf'.
160 msgbuf_getbytes(struct msgbuf
*mbp
, char *buf
, int buflen
)
162 u_int len
, pos
, wseq
;
164 wseq
= mbp
->msg_wseq
;
165 len
= MSGBUF_SEQSUB(mbp
, wseq
, mbp
->msg_rseq
);
168 if (len
> mbp
->msg_size
) {
169 mbp
->msg_rseq
= MSGBUF_SEQNORM(mbp
, wseq
- mbp
->msg_size
);
172 pos
= MSGBUF_SEQ_TO_POS(mbp
, mbp
->msg_rseq
);
173 len
= min(len
, mbp
->msg_size
- pos
);
174 len
= min(len
, (u_int
)buflen
);
176 bcopy(&mbp
->msg_ptr
[pos
], buf
, len
);
177 mbp
->msg_rseq
= MSGBUF_SEQNORM(mbp
, mbp
->msg_rseq
+ len
);
182 * Peek at the full contents of a message buffer without marking any
183 * data as read. `seqp' should point to an unsigned integer that
184 * msgbuf_peekbytes() can use to retain state between calls so that
185 * the whole message buffer can be read in multiple short reads.
186 * To initialise this variable to the start of the message buffer,
187 * call msgbuf_peekbytes() with a NULL `buf' parameter.
189 * Returns the number of characters that were placed in `buf'.
192 msgbuf_peekbytes(struct msgbuf
*mbp
, char *buf
, int buflen
, u_int
*seqp
)
194 u_int len
, pos
, wseq
;
197 /* Just initialise *seqp. */
198 *seqp
= MSGBUF_SEQNORM(mbp
, mbp
->msg_wseq
- mbp
->msg_size
);
202 wseq
= mbp
->msg_wseq
;
203 len
= MSGBUF_SEQSUB(mbp
, wseq
, *seqp
);
206 if (len
> mbp
->msg_size
) {
207 *seqp
= MSGBUF_SEQNORM(mbp
, wseq
- mbp
->msg_size
);
210 pos
= MSGBUF_SEQ_TO_POS(mbp
, *seqp
);
211 len
= min(len
, mbp
->msg_size
- pos
);
212 len
= min(len
, (u_int
)buflen
);
213 bcopy(&mbp
->msg_ptr
[MSGBUF_SEQ_TO_POS(mbp
, *seqp
)], buf
, len
);
214 *seqp
= MSGBUF_SEQNORM(mbp
, *seqp
+ len
);
219 * Compute the checksum for the complete message buffer contents.
222 msgbuf_cksum(struct msgbuf
*mbp
)
227 for (i
= 0; i
< mbp
->msg_size
; i
++)
228 sum
+= (u_char
)mbp
->msg_ptr
[i
];
233 * Copy from one message buffer to another.
236 msgbuf_copy(struct msgbuf
*src
, struct msgbuf
*dst
)
240 while ((c
= msgbuf_getchar(src
)) >= 0)
241 msgbuf_addchar(dst
, c
);