xref: /freebsd/sys/dev/sound/midi/midi.c (revision 77a0943ded95b9e6438f7db70c4a28e4d93946d4)
1 /*
2  * Main midi driver for FreeBSD. This file provides the main
3  * entry points for probe/attach and all i/o demultiplexing, including
4  * default routines for generic devices.
5  *
6  * (C) 1999 Seigo Tanimura
7  *
8  * Redistribution and use in source and binary forms, with or
9  * without modification, are permitted provided that the following
10  * conditions are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above
14  *    copyright notice, this list of conditions and the following
15  *    disclaimer in the documentation and/or other materials provided
16  *    with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS
19  * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
22  * AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  *
31  *
32  * For each card type a template "mididev_info" structure contains
33  * all the relevant parameters, both for configuration and runtime.
34  *
35  * In this file we build tables of pointers to the descriptors for
36  * the various supported cards. The generic probe routine scans
37  * the table(s) looking for a matching entry, then invokes the
38  * board-specific probe routine. If successful, a pointer to the
39  * correct mididev_info is stored in mididev_last_probed, for subsequent
40  * use in the attach routine. The generic attach routine copies
41  * the template to a permanent descriptor (midi_info[unit] and
42  * friends), initializes all generic parameters, and calls the
43  * board-specific attach routine.
44  *
45  * On device calls, the generic routines do the checks on unit and
46  * device parameters, then call the board-specific routines if
47  * available, or try to perform the task using the default code.
48  *
49  * $FreeBSD$
50  *
51  */
52 
53 #include <dev/sound/midi/midi.h>
54 
55 static devclass_t midi_devclass;
56 
57 static d_open_t midiopen;
58 static d_close_t midiclose;
59 static d_ioctl_t midiioctl;
60 static d_read_t midiread;
61 static d_write_t midiwrite;
62 static d_poll_t midipoll;
63 
64 /* These functions are local. */
65 static d_open_t midistat_open;
66 static d_close_t midistat_close;
67 static d_read_t midistat_read;
68 static int midi_initstatus(char *buf, int size);
69 static int midi_readstatus(char *buf, int *ptr, struct uio *uio);
70 
71 #define CDEV_MAJOR MIDI_CDEV_MAJOR
72 static struct cdevsw midi_cdevsw = {
73 	/* open */	midiopen,
74 	/* close */	midiclose,
75 	/* read */	midiread,
76 	/* write */	midiwrite,
77 	/* ioctl */	midiioctl,
78 	/* poll */	midipoll,
79 	/* mmap */	nommap,
80 	/* strategy */	nostrategy,
81 	/* name */	"midi",
82 	/* maj */	CDEV_MAJOR,
83 	/* dump */	nodump,
84 	/* psize */	nopsize,
85 	/* flags */	0,
86 	/* bmaj */	-1
87 };
88 
89 /*
90  * descriptors for active devices. also used as the public softc
91  * of a device.
92  */
93 mididev_info midi_info[NMIDI_MAX];
94 
95 u_long nmidi;	/* total number of midi devices, filled in by the driver */
96 u_long nsynth;	/* total number of synthesizers, filled in by the driver */
97 
98 /* These make the buffer for /dev/midistat */
99 static int midistatbusy;
100 static char midistatbuf[4096];
101 static int midistatptr;
102 
103 /*
104  * This is the generic init routine
105  */
106 int
107 midiinit(mididev_info *d, device_t dev)
108 {
109 	int unit;
110 
111 	if (midi_devclass == NULL) {
112 		midi_devclass = device_get_devclass(dev);
113 		make_dev(&midi_cdevsw, MIDIMKMINOR(0, MIDI_DEV_STATUS),
114 			 UID_ROOT, GID_WHEEL, 0444, "midistat");
115 	}
116 
117 	unit = device_get_unit(dev);
118 	make_dev(&midi_cdevsw, MIDIMKMINOR(unit, MIDI_DEV_MIDIN),
119 		 UID_ROOT, GID_WHEEL, 0666, "midi%d", unit);
120 
121 	/*
122 	 * initialize standard parameters for the device. This can be
123 	 * overridden by device-specific configurations but better do
124 	 * here the generic things.
125 	 */
126 
127 	d->unit = device_get_unit(dev);
128 	d->softc = device_get_softc(dev);
129 	d->dev = dev;
130 	d->magic = MAGIC(d->unit); /* debugging... */
131 
132 	return 0 ;
133 }
134 
135 /*
136  * a small utility function which, given a device number, returns
137  * a pointer to the associated mididev_info struct, and sets the unit
138  * number.
139  */
140 mididev_info *
141 get_mididev_info(dev_t i_dev, int *unit)
142 {
143 	int u;
144 	mididev_info *d = NULL;
145 
146 	if (MIDIDEV(i_dev) != MIDI_DEV_MIDIN)
147 		return NULL;
148 	u = MIDIUNIT(i_dev);
149 	if (unit)
150 		*unit = u;
151 
152 	if (u >= nmidi + nsynth) {
153 		DEB(printf("get_mididev_info: unit %d is not configured.\n", u));
154 		return NULL;
155 	}
156 	d = &midi_info[u];
157 
158 	return d;
159 }
160 
161 /*
162  * here are the switches for the main functions. The switches do
163  * all necessary checks on the device number to make sure
164  * that the device is configured. They also provide some default
165  * functionalities so that device-specific drivers have to deal
166  * only with special cases.
167  */
168 
169 static int
170 midiopen(dev_t i_dev, int flags, int mode, struct proc * p)
171 {
172 	switch (MIDIDEV(i_dev)) {
173 	case MIDI_DEV_MIDIN:
174 		return midi_open(i_dev, flags, mode, p);
175 	case MIDI_DEV_STATUS:
176 		return midistat_open(i_dev, flags, mode, p);
177 	}
178 
179 	return (ENXIO);
180 }
181 
182 static int
183 midiclose(dev_t i_dev, int flags, int mode, struct proc * p)
184 {
185 	switch (MIDIDEV(i_dev)) {
186 	case MIDI_DEV_MIDIN:
187 		return midi_close(i_dev, flags, mode, p);
188 	case MIDI_DEV_STATUS:
189 		return midistat_close(i_dev, flags, mode, p);
190 	}
191 
192 	return (ENXIO);
193 }
194 
195 static int
196 midiread(dev_t i_dev, struct uio * buf, int flag)
197 {
198 	switch (MIDIDEV(i_dev)) {
199 	case MIDI_DEV_MIDIN:
200 		return midi_read(i_dev, buf, flag);
201 	case MIDI_DEV_STATUS:
202 		return midistat_read(i_dev, buf, flag);
203 	}
204 
205 	return (ENXIO);
206 }
207 
208 static int
209 midiwrite(dev_t i_dev, struct uio * buf, int flag)
210 {
211 	switch (MIDIDEV(i_dev)) {
212 	case MIDI_DEV_MIDIN:
213 		return midi_write(i_dev, buf, flag);
214 	}
215 
216 	return (ENXIO);
217 }
218 
219 static int
220 midiioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p)
221 {
222 	switch (MIDIDEV(i_dev)) {
223 	case MIDI_DEV_MIDIN:
224 		return midi_ioctl(i_dev, cmd, arg, mode, p);
225 	}
226 
227 	return (ENXIO);
228 }
229 
230 static int
231 midipoll(dev_t i_dev, int events, struct proc * p)
232 {
233 	switch (MIDIDEV(i_dev)) {
234 	case MIDI_DEV_MIDIN:
235 		return midi_poll(i_dev, events, p);
236 	}
237 
238 	return (ENXIO);
239 }
240 
241 /*
242  * Followings are the generic methods in midi drivers.
243  */
244 
245 int
246 midi_open(dev_t i_dev, int flags, int mode, struct proc * p)
247 {
248 	int dev, unit, s, ret;
249 	mididev_info *d;
250 
251 	dev = minor(i_dev);
252 	d = get_mididev_info(i_dev, &unit);
253 
254 	DEB(printf("open midi%d subdev %d flags 0x%08x mode 0x%08x\n",
255 		   unit, dev & 0xf, flags, mode));
256 
257 	if (d == NULL)
258 		return (ENXIO);
259 
260 	s = splmidi();
261 
262 	/* Mark this device busy. */
263 	device_busy(d->dev);
264 	if ((d->flags & MIDI_F_BUSY) != 0) {
265 		splx(s);
266 		DEB(printf("opl_open: unit %d is busy.\n", unit));
267 		return (EBUSY);
268 	}
269 	d->flags |= MIDI_F_BUSY;
270 	d->flags &= ~(MIDI_F_READING | MIDI_F_WRITING);
271 	d->fflags = flags;
272 
273 	/* Init the queue. */
274 	if ((d->fflags & FREAD) != 0)
275 		midibuf_init(&d->midi_dbuf_in);
276 	if ((d->fflags & FWRITE) != 0) {
277 		midibuf_init(&d->midi_dbuf_out);
278 		midibuf_init(&d->midi_dbuf_passthru);
279 	}
280 
281 	if (d->open == NULL)
282 		ret = 0;
283 	else
284 		ret = d->open(i_dev, flags, mode, p);
285 
286 	splx(s);
287 
288 	return (ret);
289 }
290 
291 int
292 midi_close(dev_t i_dev, int flags, int mode, struct proc * p)
293 {
294 	int dev, unit, s, ret;
295 	mididev_info *d;
296 
297 	dev = minor(i_dev);
298 	d = get_mididev_info(i_dev, &unit);
299 
300 	DEB(printf("close midi%d subdev %d\n", unit, dev & 0xf));
301 
302 	if (d == NULL)
303 		return (ENXIO);
304 
305 	s = splmidi();
306 
307 	/* Clear the queues. */
308 	if ((d->fflags & FREAD) != 0)
309 		midibuf_init(&d->midi_dbuf_in);
310 	if ((d->fflags & FWRITE) != 0) {
311 		midibuf_init(&d->midi_dbuf_out);
312 		midibuf_init(&d->midi_dbuf_passthru);
313 	}
314 
315 	/* Stop playing and unmark this device busy. */
316 	d->flags &= ~MIDI_F_BUSY;
317 	d->fflags = 0;
318 
319 	device_unbusy(d->dev);
320 
321 	if (d->close == NULL)
322 		ret = 0;
323 	else
324 		ret = d->close(i_dev, flags, mode, p);
325 
326 	splx(s);
327 
328 	return (ret);
329 }
330 
331 int
332 midi_read(dev_t i_dev, struct uio * buf, int flag)
333 {
334 	int dev, unit, s, len, ret;
335 	mididev_info *d ;
336 
337 	dev = minor(i_dev);
338 
339 	d = get_mididev_info(i_dev, &unit);
340 	DEB(printf("read midi%d subdev %d flag 0x%08x\n", unit, dev & 0xf, flag));
341 
342 	if (d == NULL)
343 		return (ENXIO);
344 
345 	ret = 0;
346 	s = splmidi();
347 
348 	/* Begin recording. */
349 	d->callback(d, MIDI_CB_START | MIDI_CB_RD);
350 
351 	/* Have we got the data to read? */
352 	if ((d->flags & MIDI_F_NBIO) != 0 && d->midi_dbuf_in.rl == 0)
353 		ret = EAGAIN;
354 	else {
355 		len = buf->uio_resid;
356 		ret = midibuf_uioread(&d->midi_dbuf_in, buf, len);
357 		if (ret < 0)
358 			ret = -ret;
359 		else
360 			ret = 0;
361 	}
362 
363 	if (ret == 0 && d->read != NULL)
364 		ret = d->read(i_dev, buf, flag);
365 
366 	splx(s);
367 
368 	return (ret);
369 }
370 
371 int
372 midi_write(dev_t i_dev, struct uio * buf, int flag)
373 {
374 	int dev, unit, s, len, ret;
375 	mididev_info *d;
376 
377 	dev = minor(i_dev);
378 	d = get_mididev_info(i_dev, &unit);
379 
380 	DEB(printf("write midi%d subdev %d flag 0x%08x\n", unit, dev & 0xf, flag));
381 
382 	if (d == NULL)
383 		return (ENXIO);
384 
385 	ret = 0;
386 	s = splmidi();
387 
388 	/* Begin playing. */
389 	d->callback(d, MIDI_CB_START | MIDI_CB_WR);
390 
391 	/* Have we got the data to write? */
392 	if ((d->flags & MIDI_F_NBIO) != 0 && d->midi_dbuf_out.fl == 0)
393 		ret = EAGAIN;
394 	else {
395 		len = buf->uio_resid;
396 		if (len > d->midi_dbuf_out.fl &&
397 		    (d->flags & MIDI_F_NBIO))
398 			len = d->midi_dbuf_out.fl;
399 		ret = midibuf_uiowrite(&d->midi_dbuf_out, buf, len);
400 		if (ret < 0)
401 			ret = -ret;
402 		else
403 			ret = 0;
404 	}
405 
406 	/* Begin playing. */
407 	d->callback(d, MIDI_CB_START | MIDI_CB_WR);
408 
409 	if (ret == 0 && d->write != NULL)
410 		ret = d->write(i_dev, buf, flag);
411 
412 	splx(s);
413 
414 	return (ret);
415 }
416 
417 /*
418  * generic midi ioctl. Functions of the default driver can be
419  * overridden by the device-specific ioctl call.
420  * If a device-specific call returns ENOSYS (Function not implemented),
421  * the default driver is called. Otherwise, the returned value
422  * is passed up.
423  *
424  * The default handler, for many parameters, sets the value in the
425  * descriptor, sets MIDI_F_INIT, and calls the callback function with
426  * reason INIT. If successful, the callback returns 1 and the caller
427  * can update the parameter.
428  */
429 
430 int
431 midi_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p)
432 {
433 	int ret = ENOSYS, dev, unit;
434 	mididev_info *d;
435 	struct snd_size *sndsize;
436 	u_long s;
437 
438 	dev = minor(i_dev);
439 	d = get_mididev_info(i_dev, &unit);
440 
441 	if (d == NULL)
442 		return (ENXIO);
443 
444 	if (d->ioctl)
445 		ret = d->ioctl(i_dev, cmd, arg, mode, p);
446 	if (ret != ENOSYS)
447 		return ret;
448 
449 	/*
450 	 * pass control to the default ioctl handler. Set ret to 0 now.
451 	 */
452 	ret = 0;
453 
454 	/*
455 	 * all routines are called with int. blocked. Make sure that
456 	 * ints are re-enabled when calling slow or blocking functions!
457 	 */
458 	s = splmidi();
459 	switch(cmd) {
460 
461 		/*
462 		 * we start with the new ioctl interface.
463 		 */
464 	case AIONWRITE:	/* how many bytes can write ? */
465 		*(int *)arg = d->midi_dbuf_out.fl;
466 		break;
467 
468 	case AIOSSIZE:     /* set the current blocksize */
469 		sndsize = (struct snd_size *)arg;
470 		if (sndsize->play_size <= d->midi_dbuf_out.unit_size && sndsize->rec_size <= d->midi_dbuf_in.unit_size) {
471 			d->flags &= ~MIDI_F_HAS_SIZE;
472 			d->midi_dbuf_out.blocksize = d->midi_dbuf_out.unit_size;
473 			d->midi_dbuf_in.blocksize = d->midi_dbuf_in.unit_size;
474 		}
475 		else {
476 			if (sndsize->play_size > d->midi_dbuf_out.bufsize / 4)
477 				sndsize->play_size = d->midi_dbuf_out.bufsize / 4;
478 			if (sndsize->rec_size > d->midi_dbuf_in.bufsize / 4)
479 				sndsize->rec_size = d->midi_dbuf_in.bufsize / 4;
480 			/* Round up the size to the multiple of EV_SZ. */
481 			d->midi_dbuf_out.blocksize =
482 			    ((sndsize->play_size + d->midi_dbuf_out.unit_size - 1)
483 			     / d->midi_dbuf_out.unit_size) * d->midi_dbuf_out.unit_size;
484 			d->midi_dbuf_in.blocksize =
485 			    ((sndsize->rec_size + d->midi_dbuf_in.unit_size - 1)
486 			     / d->midi_dbuf_in.unit_size) * d->midi_dbuf_in.unit_size;
487 			d->flags |= MIDI_F_HAS_SIZE;
488 		}
489 		/* FALLTHROUGH */
490 	case AIOGSIZE:	/* get the current blocksize */
491 		sndsize = (struct snd_size *)arg;
492 		sndsize->play_size = d->midi_dbuf_out.blocksize;
493 		sndsize->rec_size = d->midi_dbuf_in.blocksize;
494 
495 		ret = 0;
496 		break;
497 
498 	case AIOSTOP:
499 		if (*(int *)arg == AIOSYNC_PLAY) /* play */
500 			*(int *)arg = d->callback(d, MIDI_CB_STOP | MIDI_CB_WR);
501 		else if (*(int *)arg == AIOSYNC_CAPTURE)
502 			*(int *)arg = d->callback(d, MIDI_CB_STOP | MIDI_CB_RD);
503 		else {
504 			splx(s);
505 			DEB(printf("AIOSTOP: bad channel 0x%x\n", *(int *)arg));
506 			*(int *)arg = 0 ;
507 		}
508 		break ;
509 
510 	case AIOSYNC:
511 		DEB(printf("AIOSYNC chan 0x%03lx pos %lu unimplemented\n",
512 			   ((snd_sync_parm *)arg)->chan,
513 			   ((snd_sync_parm *)arg)->pos));
514 		break;
515 		/*
516 		 * here follow the standard ioctls (filio.h etc.)
517 		 */
518 	case FIONREAD: /* get # bytes to read */
519 		*(int *)arg = d->midi_dbuf_in.rl;
520 		break;
521 
522 	case FIOASYNC: /*set/clear async i/o */
523 		DEB( printf("FIOASYNC\n") ; )
524 		    break;
525 
526 	case FIONBIO: /* set/clear non-blocking i/o */
527 		if ( *(int *)arg == 0 )
528 			d->flags &= ~MIDI_F_NBIO ;
529 		else
530 			d->flags |= MIDI_F_NBIO ;
531 		break ;
532 
533 	case MIOSPASSTHRU: /* set/clear passthru */
534 		if ( *(int *)arg == 0 )
535 			d->flags &= ~MIDI_F_PASSTHRU ;
536 		else
537 			d->flags |= MIDI_F_PASSTHRU ;
538 
539 		/* Init the queue. */
540 		midibuf_init(&d->midi_dbuf_passthru);
541 
542 		/* FALLTHROUGH */
543 	case MIOGPASSTHRU: /* get passthru */
544 		if ((d->flags & MIDI_F_PASSTHRU) != 0)
545 			(int *)arg = 1;
546 		else
547 			(int *)arg = 0;
548 		break ;
549 
550 	default:
551 		DEB(printf("default ioctl midi%d subdev %d fn 0x%08x fail\n",
552 			   unit, dev & 0xf, cmd));
553 		ret = EINVAL;
554 		break ;
555 	}
556 	splx(s);
557 	return ret ;
558 }
559 
560 int
561 midi_poll(dev_t i_dev, int events, struct proc * p)
562 {
563 	int unit, dev, ret, s, lim;
564 	mididev_info *d;
565 
566 	dev = minor(i_dev);
567 	d = get_mididev_info(i_dev, &unit);
568 
569 	if (d == NULL)
570 		return (ENXIO);
571 
572 	if (d->poll)
573 		ret = d->poll(i_dev, events, p);
574 
575 	ret = 0;
576 	s = splmidi();
577 
578 	/* Look up the apropriate queue and select it. */
579 	if ((events & (POLLOUT | POLLWRNORM)) != 0) {
580 		/* Start playing. */
581 		d->callback(d, MIDI_CB_START | MIDI_CB_WR);
582 
583 		/* Find out the boundary. */
584 		if ((d->flags & MIDI_F_HAS_SIZE) != 0)
585 			lim = d->midi_dbuf_out.blocksize;
586 		else
587 			lim = d->midi_dbuf_out.unit_size;
588 		if (d->midi_dbuf_out.fl < lim)
589 			/* No enough space, record select. */
590 			selrecord(p, &d->midi_dbuf_out.sel);
591 		else
592 			/* We can write now. */
593 			ret |= events & (POLLOUT | POLLWRNORM);
594 	}
595 	if ((events & (POLLIN | POLLRDNORM)) != 0) {
596 		/* Start recording. */
597 		d->callback(d, MIDI_CB_START | MIDI_CB_RD);
598 
599 		/* Find out the boundary. */
600 		if ((d->flags & MIDI_F_HAS_SIZE) != 0)
601 			lim = d->midi_dbuf_in.blocksize;
602 		else
603 			lim = d->midi_dbuf_in.unit_size;
604 		if (d->midi_dbuf_in.rl < lim)
605 			/* No data ready, record select. */
606 			selrecord(p, &d->midi_dbuf_in.sel);
607 		else
608 			/* We can write now. */
609 			ret |= events & (POLLIN | POLLRDNORM);
610 	}
611 	splx(s);
612 
613 	return (ret);
614 }
615 
616 void
617 midi_intr(mididev_info *d)
618 {
619 	if (d->intr != NULL)
620 		d->intr(d->intrarg, d);
621 }
622 
623 /*
624  * These handle the status message of the midi drivers.
625  */
626 
627 int
628 midistat_open(dev_t i_dev, int flags, int mode, struct proc * p)
629 {
630 	if (midistatbusy)
631 		return (EBUSY);
632 
633 	bzero(midistatbuf, sizeof(midistatbuf));
634 	midistatptr = 0;
635 	if (midi_initstatus(midistatbuf, sizeof(midistatbuf) - 1))
636 		return (ENOMEM);
637 
638 	midistatbusy = 1;
639 
640 	return (0);
641 }
642 
643 int
644 midistat_close(dev_t i_dev, int flags, int mode, struct proc * p)
645 {
646 	midistatbusy = 0;
647 
648 	return (0);
649 }
650 
651 int
652 midistat_read(dev_t i_dev, struct uio * buf, int flag)
653 {
654 	return midi_readstatus(midistatbuf, &midistatptr, buf);
655 }
656 
657 /*
658  * finally, some "libraries"
659  */
660 
661 /* Inits the buffer for /dev/midistat. */
662 static int
663 midi_initstatus(char *buf, int size)
664 {
665 	int i, p;
666 	device_t dev;
667 	mididev_info *md;
668 
669 	p = 0;
670 	p += snprintf(buf, size, "FreeBSD Midi Driver (newmidi) %s %s\nInstalled devices:\n", __DATE__, __TIME__);
671 	for (i = 0 ; i < NMIDI_MAX ; i++) {
672 		md = &midi_info[i];
673 		if (!MIDICONFED(md))
674 			continue;
675 		dev = devclass_get_device(midi_devclass, i);
676 		if (p < size)
677 			p += snprintf(&buf[p], size - p, "midi%d: <%s> %s\n", i, device_get_desc(dev), md->midistat);
678 		else
679 			return (1);
680 	}
681 
682 	return (0);
683 }
684 
685 /* Reads the status message. */
686 static int
687 midi_readstatus(char *buf, int *ptr, struct uio *uio)
688 {
689 	int s, len;
690 
691 	s = splmidi();
692 	len = min(uio->uio_resid, strlen(&buf[*ptr]));
693 	if (len > 0) {
694 		uiomove(&buf[*ptr], len, uio);
695 		*ptr += len;
696 	}
697 	splx(s);
698 
699 	return (0);
700 }
701