swdmover.c revision 1.4
1/*	$NetBSD: swdmover.c,v 1.4 2003/03/06 21:10:45 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.4 2003/03/06 21:10:45 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
62struct swdmover_function {
63	void	(*sdf_process)(struct dmover_request *);
64};
65
66static struct dmover_backend swdmover_backend;
67static struct proc *swdmover_proc;
68static int swdmover_cv;
69
70void	swdmoverattach(int);
71
72/*
73 * swdmover_process:
74 *
75 *	Dmover back-end entry point.
76 */
77static void
78swdmover_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 */
100static void
101swdmover_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 */
138static void
139swdmover_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 */
199static void
200swdmover_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 */
261static void
262swdmover_func_copy_process(struct dmover_request *dreq)
263{
264
265	/* XXX Currently, both buffers must be of same type. */
266	if (dreq->dreq_inbuf_type != dreq->dreq_outbuf_type) {
267		/* XXXLOCK */
268		dreq->dreq_error = EINVAL;
269		dreq->dreq_flags |= DMOVER_REQ_ERROR;
270		/* XXXUNLOCK */
271		goto done;
272	}
273
274	switch (dreq->dreq_outbuf_type) {
275	case DMOVER_BUF_LINEAR:
276		if (dreq->dreq_outbuf.dmbuf_linear.l_len !=
277		    dreq->dreq_inbuf[0].dmbuf_linear.l_len) {
278			/* XXXLOCK */
279			dreq->dreq_error = EINVAL;
280			dreq->dreq_flags |= DMOVER_REQ_ERROR;
281			/* XXXUNLOCK */
282			break;
283		}
284		memcpy(dreq->dreq_outbuf.dmbuf_linear.l_addr,
285		    dreq->dreq_inbuf[0].dmbuf_linear.l_addr,
286		    dreq->dreq_outbuf.dmbuf_linear.l_len);
287		break;
288
289	case DMOVER_BUF_UIO:
290	    {
291		struct uio *uio_out = dreq->dreq_outbuf.dmbuf_uio;
292		struct uio *uio_in = dreq->dreq_inbuf[0].dmbuf_uio;
293		char *cp;
294		size_t count, buflen;
295		int error;
296
297		if (uio_in->uio_rw != UIO_WRITE ||
298		    uio_out->uio_rw != UIO_READ ||
299		    uio_in->uio_resid != uio_out->uio_resid) {
300			/* XXXLOCK */
301			dreq->dreq_error = EINVAL;
302			dreq->dreq_flags |= DMOVER_REQ_ERROR;
303			/* XXXUNLOCK */
304			break;
305		}
306
307		buflen = uio_in->uio_resid;
308		if (buflen > 1024)
309			buflen = 1024;
310		cp = alloca(buflen);
311
312		while ((count = uio_in->uio_resid) != 0) {
313			if (count > buflen)
314				count = buflen;
315			error = uiomove(cp, count, uio_in);
316			if (error == 0)
317				error = uiomove(cp, count, uio_out);
318			if (error) {
319				/* XXXLOCK */
320				dreq->dreq_error = error;
321				dreq->dreq_flags |= DMOVER_REQ_ERROR;
322				/* XXXUNLOCK */
323				break;
324			}
325		}
326		break;
327	    }
328
329	default:
330		/* XXXLOCK */
331		dreq->dreq_error = EINVAL;
332		dreq->dreq_flags |= DMOVER_REQ_ERROR;
333		/* XXXUNLOCK */
334	}
335
336 done:
337	dmover_done(dreq);
338}
339
340static struct swdmover_function swdmover_func_zero = {
341	swdmover_func_zero_process
342};
343
344static struct swdmover_function swdmover_func_fill8 = {
345	swdmover_func_fill8_process
346};
347
348struct swdmover_function swdmover_func_copy = {
349	swdmover_func_copy_process
350};
351
352const struct dmover_algdesc swdmover_algdescs[] = {
353	{
354	  DMOVER_FUNC_ZERO,
355	  &swdmover_func_zero,
356	  0
357	},
358	{
359	  DMOVER_FUNC_FILL8,
360	  &swdmover_func_fill8,
361	  0
362	},
363	{
364	  DMOVER_FUNC_COPY,
365	  &swdmover_func_copy,
366	  1
367	},
368};
369#define	SWDMOVER_ALGDESC_COUNT \
370	(sizeof(swdmover_algdescs) / sizeof(swdmover_algdescs[0]))
371
372/*
373 * swdmover_create_thread:
374 *
375 *	Actually create the swdmover processing thread.
376 */
377static void
378swdmover_create_thread(void *arg)
379{
380	int error;
381
382	error = kthread_create1(swdmover_thread, arg, &swdmover_proc,
383	    "swdmover");
384	if (error)
385		printf("WARNING: unable to create swdmover thread, "
386		    "error = %d\n", error);
387}
388
389/*
390 * swdmoverattach:
391 *
392 *	Pesudo-device attach routine.
393 */
394void
395swdmoverattach(int count)
396{
397
398	swdmover_backend.dmb_name = "swdmover";
399	swdmover_backend.dmb_speed = 1;		/* XXX */
400	swdmover_backend.dmb_cookie = NULL;
401	swdmover_backend.dmb_algdescs = swdmover_algdescs;
402	swdmover_backend.dmb_nalgdescs = SWDMOVER_ALGDESC_COUNT;
403	swdmover_backend.dmb_process = swdmover_process;
404
405	kthread_create(swdmover_create_thread, &swdmover_backend);
406
407	/* XXX Should only register this when kthread creation succeeds. */
408	dmover_backend_register(&swdmover_backend);
409}
410