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