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