Home | History | Annotate | Line # | Download | only in dmover
swdmover.c revision 1.3
      1 /*	$NetBSD: swdmover.c,v 1.3 2002/12/10 01:09:09 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: swdmover.c,v 1.3 2002/12/10 01:09:09 thorpej Exp $");
     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 	default:
    185 		/* XXXLOCK */
    186 		dreq->dreq_error = EINVAL;
    187 		dreq->dreq_flags |= DMOVER_REQ_ERROR;
    188 		/* XXXUNLOCK */
    189 	}
    190 
    191 	dmover_done(dreq);
    192 }
    193 
    194 /*
    195  * swdmover_func_fill8_process:
    196  *
    197  *	Processing routine for the "fill8" function.
    198  */
    199 static void
    200 swdmover_func_fill8_process(struct dmover_request *dreq)
    201 {
    202 
    203 	switch (dreq->dreq_outbuf_type) {
    204 	case DMOVER_BUF_LINEAR:
    205 		memset(dreq->dreq_outbuf.dmbuf_linear.l_addr,
    206 		    dreq->dreq_immediate[0],
    207 		    dreq->dreq_outbuf.dmbuf_linear.l_len);
    208 		break;
    209 
    210 	case DMOVER_BUF_UIO:
    211 	    {
    212 		struct uio *uio = dreq->dreq_outbuf.dmbuf_uio;
    213 		char *cp;
    214 		size_t count, buflen;
    215 		int error;
    216 
    217 		if (uio->uio_rw != UIO_READ) {
    218 			/* XXXLOCK */
    219 			dreq->dreq_error = EINVAL;
    220 			dreq->dreq_flags |= DMOVER_REQ_ERROR;
    221 			/* XXXUNLOCK */
    222 			break;
    223 		}
    224 
    225 		buflen = uio->uio_resid;
    226 		if (buflen > 1024)
    227 			buflen = 1024;
    228 		cp = alloca(buflen);
    229 		memset(cp, dreq->dreq_immediate[0], buflen);
    230 
    231 		while ((count = uio->uio_resid) != 0) {
    232 			if (count > buflen)
    233 				count = buflen;
    234 			error = uiomove(cp, count, uio);
    235 			if (error) {
    236 				/* XXXLOCK */
    237 				dreq->dreq_error = error;
    238 				dreq->dreq_flags |= DMOVER_REQ_ERROR;
    239 				/* XXXUNLOCK */
    240 				break;
    241 			}
    242 		}
    243 		break;
    244 	    }
    245 
    246 	default:
    247 		/* XXXLOCK */
    248 		dreq->dreq_error = EINVAL;
    249 		dreq->dreq_flags |= DMOVER_REQ_ERROR;
    250 		/* XXXUNLOCK */
    251 	}
    252 
    253 	dmover_done(dreq);
    254 }
    255 
    256 /*
    257  * swdmover_func_copy_process:
    258  *
    259  *	Processing routine for the "copy" function.
    260  */
    261 static void
    262 swdmover_func_copy_process(struct dmover_request *dreq)
    263 {
    264 
    265 	/*
    266 	 * Middle-end makes sure input and output buffers are of
    267 	 * the same type.
    268 	 */
    269 	switch (dreq->dreq_outbuf_type) {
    270 	case DMOVER_BUF_LINEAR:
    271 		if (dreq->dreq_outbuf.dmbuf_linear.l_len !=
    272 		    dreq->dreq_inbuf[0].dmbuf_linear.l_len) {
    273 			/* XXXLOCK */
    274 			dreq->dreq_error = EINVAL;
    275 			dreq->dreq_flags |= DMOVER_REQ_ERROR;
    276 			/* XXXUNLOCK */
    277 			break;
    278 		}
    279 		memcpy(dreq->dreq_outbuf.dmbuf_linear.l_addr,
    280 		    dreq->dreq_inbuf[0].dmbuf_linear.l_addr,
    281 		    dreq->dreq_outbuf.dmbuf_linear.l_len);
    282 		break;
    283 
    284 	case DMOVER_BUF_UIO:
    285 	    {
    286 		struct uio *uio_out = dreq->dreq_outbuf.dmbuf_uio;
    287 		struct uio *uio_in = dreq->dreq_inbuf[0].dmbuf_uio;
    288 		char *cp;
    289 		size_t count, buflen;
    290 		int error;
    291 
    292 		if (uio_in->uio_rw != UIO_WRITE ||
    293 		    uio_out->uio_rw != UIO_READ ||
    294 		    uio_in->uio_resid != uio_out->uio_resid) {
    295 			/* XXXLOCK */
    296 			dreq->dreq_error = EINVAL;
    297 			dreq->dreq_flags |= DMOVER_REQ_ERROR;
    298 			/* XXXUNLOCK */
    299 			break;
    300 		}
    301 
    302 		buflen = uio_in->uio_resid;
    303 		if (buflen > 1024)
    304 			buflen = 1024;
    305 		cp = alloca(buflen);
    306 
    307 		while ((count = uio_in->uio_resid) != 0) {
    308 			if (count > buflen)
    309 				count = buflen;
    310 			error = uiomove(cp, count, uio_in);
    311 			if (error == 0)
    312 				error = uiomove(cp, count, uio_out);
    313 			if (error) {
    314 				/* XXXLOCK */
    315 				dreq->dreq_error = error;
    316 				dreq->dreq_flags |= DMOVER_REQ_ERROR;
    317 				/* XXXUNLOCK */
    318 				break;
    319 			}
    320 		}
    321 		break;
    322 	    }
    323 
    324 	default:
    325 		/* XXXLOCK */
    326 		dreq->dreq_error = EINVAL;
    327 		dreq->dreq_flags |= DMOVER_REQ_ERROR;
    328 		/* XXXUNLOCK */
    329 	}
    330 
    331 	dmover_done(dreq);
    332 }
    333 
    334 static struct swdmover_function swdmover_func_zero = {
    335 	swdmover_func_zero_process
    336 };
    337 
    338 static struct swdmover_function swdmover_func_fill8 = {
    339 	swdmover_func_fill8_process
    340 };
    341 
    342 struct swdmover_function swdmover_func_copy = {
    343 	swdmover_func_copy_process
    344 };
    345 
    346 const struct dmover_algdesc swdmover_algdescs[] = {
    347 	{
    348 	  DMOVER_FUNC_ZERO,
    349 	  &swdmover_func_zero,
    350 	  0
    351 	},
    352 	{
    353 	  DMOVER_FUNC_FILL8,
    354 	  &swdmover_func_fill8,
    355 	  0
    356 	},
    357 	{
    358 	  DMOVER_FUNC_COPY,
    359 	  &swdmover_func_copy,
    360 	  1
    361 	},
    362 };
    363 #define	SWDMOVER_ALGDESC_COUNT \
    364 	(sizeof(swdmover_algdescs) / sizeof(swdmover_algdescs[0]))
    365 
    366 /*
    367  * swdmover_create_thread:
    368  *
    369  *	Actually create the swdmover processing thread.
    370  */
    371 static void
    372 swdmover_create_thread(void *arg)
    373 {
    374 	int error;
    375 
    376 	error = kthread_create1(swdmover_thread, arg, &swdmover_proc,
    377 	    "swdmover");
    378 	if (error)
    379 		printf("WARNING: unable to create swdmover thread, "
    380 		    "error = %d\n", error);
    381 }
    382 
    383 /*
    384  * swdmoverattach:
    385  *
    386  *	Pesudo-device attach routine.
    387  */
    388 void
    389 swdmoverattach(int count)
    390 {
    391 
    392 	swdmover_backend.dmb_name = "swdmover";
    393 	swdmover_backend.dmb_speed = 1;		/* XXX */
    394 	swdmover_backend.dmb_cookie = NULL;
    395 	swdmover_backend.dmb_algdescs = swdmover_algdescs;
    396 	swdmover_backend.dmb_nalgdescs = SWDMOVER_ALGDESC_COUNT;
    397 	swdmover_backend.dmb_process = swdmover_process;
    398 
    399 	kthread_create(swdmover_create_thread, &swdmover_backend);
    400 
    401 	/* XXX Should only register this when kthread creation succeeds. */
    402 	dmover_backend_register(&swdmover_backend);
    403 }
    404