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);