blob: db6e4aa8dd4b6f16b426212fee457dfb0965ab28 [file] [log] [blame]
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
Erik Nordmarkde8c4a12009-02-12 08:42:06 -08005 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07007 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
Erik Nordmarkde8c4a12009-02-12 08:42:06 -080022 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -070023 * Use is subject to license terms.
24 */
25
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -070026/*
27 * Solaris DDI STREAMS utility routines (PSARC/2003/648).
28 *
29 * Please see the appropriate section 9F manpage for documentation.
30 */
31
32#include <sys/types.h>
33#include <sys/systm.h>
34#include <sys/errno.h>
35#include <sys/stream.h>
36#include <sys/stropts.h>
masputraff550d02005-10-22 22:50:14 -070037#include <sys/strsubr.h>
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -070038#include <sys/strsun.h>
masputraff550d02005-10-22 22:50:14 -070039#include <sys/sysmacros.h>
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -070040#include <sys/cmn_err.h>
41
42void
43merror(queue_t *wq, mblk_t *mp, int error)
44{
45 if ((mp = mexchange(wq, mp, 1, M_ERROR, -1)) == NULL)
46 return;
47
48 *mp->b_rptr = (uchar_t)error;
49 qreply(wq, mp);
50}
51
52void
53mioc2ack(mblk_t *mp, mblk_t *dp, size_t count, int rval)
54{
55 struct iocblk *iocp = (struct iocblk *)mp->b_rptr;
56 mblk_t *odp = mp->b_cont; /* allows freemsg() to be a tail call */
57
58 DB_TYPE(mp) = M_IOCACK;
59 iocp->ioc_count = count;
60 iocp->ioc_error = 0;
61 iocp->ioc_rval = rval;
62
63 mp->b_cont = dp;
64 if (dp != NULL)
65 dp->b_wptr = dp->b_rptr + count;
66 freemsg(odp);
67}
68
69void
70miocack(queue_t *wq, mblk_t *mp, int count, int rval)
71{
72 struct iocblk *iocp = (struct iocblk *)mp->b_rptr;
73
74 DB_TYPE(mp) = M_IOCACK;
75 iocp->ioc_count = count;
76 iocp->ioc_error = 0;
77 iocp->ioc_rval = rval;
78 qreply(wq, mp);
79}
80
81void
82miocnak(queue_t *wq, mblk_t *mp, int count, int error)
83{
84 struct iocblk *iocp = (struct iocblk *)mp->b_rptr;
85
86 DB_TYPE(mp) = M_IOCNAK;
87 iocp->ioc_count = count;
88 iocp->ioc_error = error;
89 qreply(wq, mp);
90}
91
92mblk_t *
93mexchange(queue_t *wq, mblk_t *mp, size_t size, uchar_t type, int32_t primtype)
94{
95 if (mp == NULL || MBLKSIZE(mp) < size || DB_REF(mp) > 1) {
96 freemsg(mp);
97 if ((mp = allocb(size, BPRI_LO)) == NULL) {
98 if (wq != NULL) {
99 if ((mp = allocb(1, BPRI_HI)) != NULL)
100 merror(wq, mp, ENOSR);
101 }
102 return (NULL);
103 }
104 }
105
106 DB_TYPE(mp) = type;
107 mp->b_rptr = DB_BASE(mp);
108 mp->b_wptr = mp->b_rptr + size;
109 if (primtype >= 0)
110 *(int32_t *)mp->b_rptr = primtype;
111
112 return (mp);
113}
114
115size_t
116msgsize(mblk_t *mp)
117{
118 size_t n = 0;
119
120 for (; mp != NULL; mp = mp->b_cont)
121 n += MBLKL(mp);
122
123 return (n);
124}
125
126void
127mcopymsg(mblk_t *mp, void *bufp)
128{
129 caddr_t dest = bufp;
130 mblk_t *bp;
131 size_t n;
132
133 for (bp = mp; bp != NULL; bp = bp->b_cont) {
134 n = MBLKL(bp);
135 bcopy(bp->b_rptr, dest, n);
136 dest += n;
137 }
138
139 freemsg(mp);
140}
141
142void
143mcopyin(mblk_t *mp, void *private, size_t size, void *useraddr)
144{
145 struct copyreq *cp = (struct copyreq *)mp->b_rptr;
146
147 if (useraddr != NULL) {
148 cp->cq_addr = (caddr_t)useraddr;
149 } else {
150 ASSERT(DB_TYPE(mp) == M_IOCTL);
151 ASSERT(mp->b_cont != NULL);
152 ASSERT(((struct iocblk *)mp->b_rptr)->ioc_count == TRANSPARENT);
153 cp->cq_addr = (caddr_t)*(uintptr_t *)mp->b_cont->b_rptr;
154 }
155
156 cp->cq_flag = 0;
157 cp->cq_size = size;
158 cp->cq_private = (mblk_t *)private;
159
160 DB_TYPE(mp) = M_COPYIN;
161 mp->b_wptr = mp->b_rptr + sizeof (struct copyreq);
162
163 if (mp->b_cont != NULL) {
164 freemsg(mp->b_cont);
165 mp->b_cont = NULL;
166 }
167}
168
169void
170mcopyout(mblk_t *mp, void *private, size_t size, void *useraddr, mblk_t *dp)
171{
172 struct copyreq *cp = (struct copyreq *)mp->b_rptr;
173
174 if (useraddr != NULL)
175 cp->cq_addr = (caddr_t)useraddr;
176 else {
177 ASSERT(DB_TYPE(mp) == M_IOCTL);
178 ASSERT(mp->b_cont != NULL);
179 ASSERT(((struct iocblk *)mp->b_rptr)->ioc_count == TRANSPARENT);
180 cp->cq_addr = (caddr_t)*(uintptr_t *)mp->b_cont->b_rptr;
181 }
182
183 cp->cq_flag = 0;
184 cp->cq_size = size;
185 cp->cq_private = (mblk_t *)private;
186
187 DB_TYPE(mp) = M_COPYOUT;
188 mp->b_wptr = mp->b_rptr + sizeof (struct copyreq);
189
190 if (dp != NULL) {
191 if (mp->b_cont != NULL)
192 freemsg(mp->b_cont);
193 mp->b_cont = dp;
194 mp->b_cont->b_wptr = mp->b_cont->b_rptr + size;
195 }
196}
197
198int
199miocpullup(mblk_t *iocmp, size_t size)
200{
201 struct iocblk *iocp = (struct iocblk *)iocmp->b_rptr;
202 mblk_t *datamp = iocmp->b_cont;
203 mblk_t *newdatamp;
204
205 /*
206 * We'd like to be sure that DB_TYPE(iocmp) == M_IOCTL, but some
207 * nitwit routines like ttycommon_ioctl() always reset the type of
208 * legitimate M_IOCTL messages to M_IOCACK as a "courtesy" to the
209 * caller, even when the routine does not understand the M_IOCTL.
210 * The ttycommon_ioctl() routine does us the additional favor of
211 * clearing ioc_count, so we cannot rely on it having a correct
212 * size either (blissfully, ttycommon_ioctl() does not screw with
213 * TRANSPARENT messages, so we can still sanity check for that).
214 */
215 ASSERT(MBLKL(iocmp) == sizeof (struct iocblk));
216 if (MBLKL(iocmp) != sizeof (struct iocblk)) {
217 cmn_err(CE_WARN, "miocpullup: passed mblk_t %p is not an ioctl"
218 " mblk_t", (void *)iocmp);
219 return (EINVAL);
220 }
221
222 if (iocp->ioc_count == TRANSPARENT)
223 return (EINVAL);
224
225 if (size == 0)
226 return (0);
227
228 if (datamp == NULL)
229 return (EINVAL);
230
231 if (MBLKL(datamp) >= size)
232 return (0);
233
234 newdatamp = msgpullup(datamp, size);
235 if (newdatamp == NULL) {
236 if (msgdsize(datamp) < size)
237 return (EINVAL);
238 return (ENOMEM);
239 }
240
241 iocmp->b_cont = newdatamp;
242 freemsg(datamp);
243 return (0);
244}
masputraff550d02005-10-22 22:50:14 -0700245
246/* Copy userdata into a new mblk_t */
247mblk_t *
248mcopyinuio(struct stdata *stp, uio_t *uiop, ssize_t iosize,
249 ssize_t maxblk, int *errorp)
250{
251 mblk_t *head = NULL, **tail = &head;
252 size_t offset = stp->sd_wroff;
kaisc28749e2005-11-12 18:58:05 -0800253 size_t tail_len = stp->sd_tail;
masputraff550d02005-10-22 22:50:14 -0700254
255 if (iosize == INFPSZ || iosize > uiop->uio_resid)
256 iosize = uiop->uio_resid;
257
258 if (maxblk == INFPSZ)
259 maxblk = iosize;
260
261 /* Nothing to do in these cases, so we're done */
262 if (iosize < 0 || maxblk < 0 || (maxblk == 0 && iosize > 0))
263 goto done;
264
265 if (stp->sd_flag & STRCOPYCACHED)
266 uiop->uio_extflg |= UIO_COPY_CACHED;
267
268 /*
269 * We will enter the loop below if iosize is 0; it will allocate an
270 * empty message block and call uiomove(9F) which will just return.
271 * We could avoid that with an extra check but would only slow
272 * down the much more likely case where iosize is larger than 0.
273 */
274 do {
275 ssize_t blocksize;
276 mblk_t *mp;
277
278 blocksize = MIN(iosize, maxblk);
279 ASSERT(blocksize >= 0);
kaisc28749e2005-11-12 18:58:05 -0800280 if ((mp = allocb_cred(offset + blocksize + tail_len,
Erik Nordmarkde8c4a12009-02-12 08:42:06 -0800281 CRED(), curproc->p_pid)) == NULL) {
masputraff550d02005-10-22 22:50:14 -0700282 *errorp = ENOMEM;
283 return (head);
284 }
285 mp->b_rptr += offset;
286 mp->b_wptr = mp->b_rptr + blocksize;
masputraff550d02005-10-22 22:50:14 -0700287
288 *tail = mp;
289 tail = &mp->b_cont;
290
291 /* uiomove(9F) either returns 0 or EFAULT */
292 if ((*errorp = uiomove(mp->b_rptr, (size_t)blocksize,
293 UIO_WRITE, uiop)) != 0) {
294 ASSERT(*errorp != ENOMEM);
295 freemsg(head);
296 return (NULL);
297 }
298
299 iosize -= blocksize;
300 } while (iosize > 0);
301
302done:
303 *errorp = 0;
304 return (head);
305}