Home | History | Annotate | Line # | Download | only in dmover
swdmover.c revision 1.1
      1 /*	$NetBSD: swdmover.c,v 1.1 2002/08/02 00:30:40 thorpej Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2002 Wasabi Systems, Inc.
      5  * All rights reserved.
      6  *
      7  * Written by Jason R. Thorpe for Wasabi Systems, Inc.
      8  *
      9  * Redistribution and use in source and binary forms, with or without
     10  * modification, are permitted provided that the following conditions
     11  * are met:
     12  * 1. Redistributions of source code must retain the above copyright
     13  *    notice, this list of conditions and the following disclaimer.
     14  * 2. Redistributions in binary form must reproduce the above copyright
     15  *    notice, this list of conditions and the following disclaimer in the
     16  *    documentation and/or other materials provided with the distribution.
     17  * 3. All advertising materials mentioning features or use of this software
     18  *    must display the following acknowledgement:
     19  *	This product includes software developed for the NetBSD Project by
     20  *	Wasabi Systems, Inc.
     21  * 4. The name of Wasabi Systems, Inc. may not be used to endorse
     22  *    or promote products derived from this software without specific prior
     23  *    written permission.
     24  *
     25  * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
     26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     27  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     28  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
     29  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     30  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     31  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     32  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     33  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     34  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     35  * POSSIBILITY OF SUCH DAMAGE.
     36  */
     37 
     38 /*
     39  * swdmover.c: Software back-end providing the dmover functions
     40  * mentioned in dmover(9).
     41  *
     42  * This module provides a fallback for cases where no hardware
     43  * data movers are present in a system, and also serves an an
     44  * example of how to write a dmover back-end.
     45  *
     46  * Note that even through the software dmover doesn't require
     47  * interrupts to be blocked, we block them anyway to demonstrate
     48  * the locking protocol.
     49  */
     50 
     51 #include <sys/cdefs.h>
     52 __KERNEL_RCSID(0, "$NetBSD");
     53 
     54 #include <sys/param.h>
     55 #include <sys/lock.h>
     56 #include <sys/kthread.h>
     57 #include <sys/systm.h>
     58 #include <sys/uio.h>
     59 
     60 #include <dev/dmover/dmovervar.h>
     61 
     62 struct swdmover_function {
     63 	void	(*sdf_process)(struct dmover_request *);
     64 };
     65 
     66 static struct dmover_backend swdmover_backend;
     67 static struct proc *swdmover_proc;
     68 static int swdmover_cv;
     69 
     70 void	swdmoverattach(int);
     71 
     72 /*
     73  * swdmover_process:
     74  *
     75  *	Dmover back-end entry point.
     76  */
     77 static void
     78 swdmover_process(struct dmover_backend *dmb)
     79 {
     80 	int s;
     81 
     82 	/*
     83 	 * Just wake up the processing thread.  This will allow
     84 	 * requests to linger on the middle-end's queue so that
     85 	 * they can be cancelled, if need-be.
     86 	 */
     87 	s = splbio();
     88 	/* XXXLOCK */
     89 	if (TAILQ_EMPTY(&dmb->dmb_pendreqs) == 0)
     90 		wakeup(&swdmover_cv);
     91 	/* XXXUNLOCK */
     92 	splx(s);
     93 }
     94 
     95 /*
     96  * swdmover_thread:
     97  *
     98  *	Request processing thread.
     99  */
    100 static void
    101 swdmover_thread(void *arg)
    102 {
    103 	struct dmover_backend *dmb = arg;
    104 	struct dmover_request *dreq;
    105 	struct swdmover_function *sdf;
    106 	int s;
    107 
    108 	s = splbio();
    109 	/* XXXLOCK */
    110 
    111 	for (;;) {
    112 		dreq = TAILQ_FIRST(&dmb->dmb_pendreqs);
    113 		if (dreq == NULL) {
    114 			/* XXXUNLOCK */
    115 			(void) tsleep(&swdmover_cv, PRIBIO, "swdmvr", 0);
    116 			continue;
    117 		}
    118 
    119 		dmover_backend_remque(dmb, dreq);
    120 		dreq->dreq_flags |= DMOVER_REQ_RUNNING;
    121 
    122 		/* XXXUNLOCK */
    123 		splx(s);
    124 
    125 		sdf = dreq->dreq_assignment->das_algdesc->dad_data;
    126 		(*sdf->sdf_process)(dreq);
    127 
    128 		s = splbio();
    129 		/* XXXLOCK */
    130 	}
    131 }
    132 
    133 /*
    134  * swdmover_func_zero_process:
    135  *
    136  *	Processing routine for the "zero" function.
    137  */
    138 static void
    139 swdmover_func_zero_process(struct dmover_request *dreq)
    140 {
    141 
    142 	switch (dreq->dreq_outbuf_type) {
    143 	case DMOVER_BUF_LINEAR:
    144 		memset(dreq->dreq_outbuf.dmbuf_linear.l_addr, 0,
    145 		    dreq->dreq_outbuf.dmbuf_linear.l_len);
    146 		break;
    147 
    148 	case DMOVER_BUF_UIO:
    149 	    {
    150 		struct uio *uio = dreq->dreq_outbuf.dmbuf_uio;
    151 		char *cp;
    152 		size_t count, buflen;
    153 		int error;
    154 
    155 		if (uio->uio_rw != UIO_READ) {
    156 			/* XXXLOCK */
    157 			dreq->dreq_error = EINVAL;
    158 			dreq->dreq_flags |= DMOVER_REQ_ERROR;
    159 			/* XXXUNLOCK */
    160 			break;
    161 		}
    162 
    163 		buflen = uio->uio_resid;
    164 		if (buflen > 1024)
    165 			buflen = 1024;
    166 		cp = alloca(buflen);
    167 		memset(cp, 0, buflen);
    168 
    169 		while ((count = uio->uio_resid) != 0) {
    170 			if (count > buflen)
    171 				count = buflen;
    172 			error = uiomove(cp, count, uio);
    173 			if (error) {
    174 				/* XXXLOCK */
    175 				dreq->dreq_error = error;
    176 				dreq->dreq_flags |= DMOVER_REQ_ERROR;
    177 				/* XXXUNLOCK */
    178 				break;
    179 			}
    180 		}
    181 		break;
    182 	    }
    183 	}
    184 
    185 	dmover_done(dreq);
    186 }
    187 
    188 /*
    189  * swdmover_func_fill8_process:
    190  *
    191  *	Processing routine for the "fill8" function.
    192  */
    193 static void
    194 swdmover_func_fill8_process(struct dmover_request *dreq)
    195 {
    196 
    197 	switch (dreq->dreq_outbuf_type) {
    198 	case DMOVER_BUF_LINEAR:
    199 		memset(dreq->dreq_outbuf.dmbuf_linear.l_addr,
    200 		    dreq->dreq_immediate[0],
    201 		    dreq->dreq_outbuf.dmbuf_linear.l_len);
    202 		break;
    203 
    204 	case DMOVER_BUF_UIO:
    205 	    {
    206 		struct uio *uio = dreq->dreq_outbuf.dmbuf_uio;
    207 		char *cp;
    208 		size_t count, buflen;
    209 		int error;
    210 
    211 		if (uio->uio_rw != UIO_READ) {
    212 			/* XXXLOCK */
    213 			dreq->dreq_error = EINVAL;
    214 			dreq->dreq_flags |= DMOVER_REQ_ERROR;
    215 			/* XXXUNLOCK */
    216 			break;
    217 		}
    218 
    219 		buflen = uio->uio_resid;
    220 		if (buflen > 1024)
    221 			buflen = 1024;
    222 		cp = alloca(buflen);
    223 		memset(cp, dreq->dreq_immediate[0], buflen);
    224 
    225 		while ((count = uio->uio_resid) != 0) {
    226 			if (count > buflen)
    227 				count = buflen;
    228 			error = uiomove(cp, count, uio);
    229 			if (error) {
    230 				/* XXXLOCK */
    231 				dreq->dreq_error = error;
    232 				dreq->dreq_flags |= DMOVER_REQ_ERROR;
    233 				/* XXXUNLOCK */
    234 				break;
    235 			}
    236 		}
    237 		break;
    238 	    }
    239 	}
    240 
    241 	dmover_done(dreq);
    242 }
    243 
    244 /*
    245  * swdmover_func_copy_process:
    246  *
    247  *	Processing routine for the "copy" function.
    248  */
    249 static void
    250 swdmover_func_copy_process(struct dmover_request *dreq)
    251 {
    252 
    253 	/*
    254 	 * Middle-end makes sure input and output buffers are of
    255 	 * the same type.
    256 	 */
    257 	switch (dreq->dreq_outbuf_type) {
    258 	case DMOVER_BUF_LINEAR:
    259 		if (dreq->dreq_outbuf.dmbuf_linear.l_len !=
    260 		    dreq->dreq_inbuf[0].dmbuf_linear.l_len) {
    261 			/* XXXLOCK */
    262 			dreq->dreq_error = EINVAL;
    263 			dreq->dreq_flags |= DMOVER_REQ_ERROR;
    264 			/* XXXUNLOCK */
    265 			break;
    266 		}
    267 		memcpy(dreq->dreq_outbuf.dmbuf_linear.l_addr,
    268 		    dreq->dreq_inbuf[0].dmbuf_linear.l_addr,
    269 		    dreq->dreq_outbuf.dmbuf_linear.l_len);
    270 		break;
    271 
    272 	case DMOVER_BUF_UIO:
    273 	    {
    274 		struct uio *uio_out = dreq->dreq_outbuf.dmbuf_uio;
    275 		struct uio *uio_in = dreq->dreq_inbuf[0].dmbuf_uio;
    276 		char *cp;
    277 		size_t count, buflen;
    278 		int error;
    279 
    280 		if (uio_in->uio_rw != UIO_WRITE ||
    281 		    uio_out->uio_rw != UIO_READ ||
    282 		    uio_in->uio_resid != uio_out->uio_resid) {
    283 			/* XXXLOCK */
    284 			dreq->dreq_error = EINVAL;
    285 			dreq->dreq_flags |= DMOVER_REQ_ERROR;
    286 			/* XXXUNLOCK */
    287 			break;
    288 		}
    289 
    290 		buflen = uio_in->uio_resid;
    291 		if (buflen > 1024)
    292 			buflen = 1024;
    293 		cp = alloca(buflen);
    294 
    295 		while ((count = uio_in->uio_resid) != 0) {
    296 			if (count > buflen)
    297 				count = buflen;
    298 			error = uiomove(cp, count, uio_in);
    299 			if (error == 0)
    300 				error = uiomove(cp, count, uio_out);
    301 			if (error) {
    302 				/* XXXLOCK */
    303 				dreq->dreq_error = error;
    304 				dreq->dreq_flags |= DMOVER_REQ_ERROR;
    305 				/* XXXUNLOCK */
    306 				break;
    307 			}
    308 		}
    309 		break;
    310 	    }
    311 	}
    312 
    313 	dmover_done(dreq);
    314 }
    315 
    316 static struct swdmover_function swdmover_func_zero = {
    317 	swdmover_func_zero_process
    318 };
    319 
    320 static struct swdmover_function swdmover_func_fill8 = {
    321 	swdmover_func_fill8_process
    322 };
    323 
    324 struct swdmover_function swdmover_func_copy = {
    325 	swdmover_func_copy_process
    326 };
    327 
    328 const struct dmover_algdesc swdmover_algdescs[] = {
    329 	{
    330 	  DMOVER_FUNC_ZERO,
    331 	  &swdmover_func_zero,
    332 	  0
    333 	},
    334 	{
    335 	  DMOVER_FUNC_FILL8,
    336 	  &swdmover_func_fill8,
    337 	  0
    338 	},
    339 	{
    340 	  DMOVER_FUNC_COPY,
    341 	  &swdmover_func_copy,
    342 	  1
    343 	},
    344 };
    345 #define	SWDMOVER_ALGDESC_COUNT \
    346 	(sizeof(swdmover_algdescs) / sizeof(swdmover_algdescs[0]))
    347 
    348 /*
    349  * swdmover_create_thread:
    350  *
    351  *	Actually create the swdmover processing thread.
    352  */
    353 static void
    354 swdmover_create_thread(void *arg)
    355 {
    356 	int error;
    357 
    358 	error = kthread_create1(swdmover_thread, arg, &swdmover_proc,
    359 	    "swdmover");
    360 	if (error)
    361 		printf("WARNING: unable to create swdmover thread, "
    362 		    "error = %d\n", error);
    363 }
    364 
    365 /*
    366  * swdmoverattach:
    367  *
    368  *	Pesudo-device attach routine.
    369  */
    370 void
    371 swdmoverattach(int count)
    372 {
    373 
    374 	swdmover_backend.dmb_name = "swdmover";
    375 	swdmover_backend.dmb_speed = 1;		/* XXX */
    376 	swdmover_backend.dmb_cookie = NULL;
    377 	swdmover_backend.dmb_algdescs = swdmover_algdescs;
    378 	swdmover_backend.dmb_nalgdescs = SWDMOVER_ALGDESC_COUNT;
    379 	swdmover_backend.dmb_process = swdmover_process;
    380 
    381 	kthread_create(swdmover_create_thread, &swdmover_backend);
    382 
    383 	/* XXX Should only register this when kthread creation succeeds. */
    384 	dmover_backend_register(&swdmover_backend);
    385 }
    386