xref: /netbsd/sys/dev/pud/pud_dev.c (revision 6550d01e)
1 /*	$NetBSD: pud_dev.c,v 1.6 2009/12/22 17:32:03 pooka Exp $	*/
2 
3 /*
4  * Copyright (c) 2007  Antti Kantee.  All Rights Reserved.
5  *
6  * Development of this software was supported by the
7  * Research Foundation of Helsinki University of Technology
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  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
19  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21  * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30 
31 #include <sys/cdefs.h>
32 __KERNEL_RCSID(0, "$NetBSD: pud_dev.c,v 1.6 2009/12/22 17:32:03 pooka Exp $");
33 
34 #include <sys/param.h>
35 #include <sys/buf.h>
36 #include <sys/conf.h>
37 #include <sys/event.h>
38 #include <sys/ioccom.h>
39 #include <sys/kmem.h>
40 #include <sys/poll.h>
41 #include <sys/socketvar.h>
42 
43 #include <dev/pud/pud_sys.h>
44 
45 /*
46  * b/c independent helpers
47  */
48 
49 static int
50 doopenclose(dev_t dev, int flags, int fmt, int class, int type)
51 {
52 	struct pud_req_openclose pc_oc; /* XXX: stack = stupid */
53 
54 	pc_oc.pm_flags = flags;
55 	pc_oc.pm_fmt = fmt;
56 
57 	return pud_request(dev, &pc_oc, sizeof(pc_oc), class, type);
58 }
59 
60 #include <sys/disklabel.h>
61 /*
62  * XXX: this is not "reentrant".  But then again, partinfo isn't
63  * exactly safe in any case.
64  */
65 static struct disklabel dl_partinfo;
66 
67 static int
68 doioctl(dev_t dev, u_long cmd, void *data, int flag, int class, int type)
69 {
70 	struct pud_req_ioctl *pc_ioctl;
71 	size_t dlen, allocsize;
72 	u_long origcmd = cmd;
73 	void *origdata = NULL; /* XXXgcc */
74 	int error;
75 
76 	/*
77 	 * XXX: kludge.  This is a horrible abstraction violation, but
78 	 * then again DIOCGPART is a horrible ioctl (even more horrible
79 	 * than the generic ioctl).  We handle it specially here since
80 	 * the server in userspace has no chance to handle it.  And it's
81 	 * a common operation used by most file systems.  But really, it
82 	 * should be replaced by something a bit more ... transactional.
83 	 */
84 	if (cmd == DIOCGPART) {
85 		cmd = DIOCGDINFO;
86 		origdata = data;
87 		flag = 0;
88 		data = &dl_partinfo;
89 	}
90 
91 	dlen = IOCPARM_LEN(cmd);
92 	allocsize = sizeof(struct pud_req_ioctl) + dlen;
93 	pc_ioctl = kmem_zalloc(allocsize, KM_SLEEP);
94 
95 	pc_ioctl->pm_iocmd = cmd;
96 	pc_ioctl->pm_flag = flag;
97 
98 	if (cmd & IOC_IN)
99 		memcpy(pc_ioctl->pm_data, data, dlen);
100 	error = pud_request(dev, pc_ioctl, allocsize, class, type);
101 	if (error)
102 		goto out;
103 	if (cmd & IOC_OUT)
104 		memcpy(data, pc_ioctl->pm_data, dlen);
105 
106 	/*
107 	 * In case doing the infamous DIOCGPART, issue the real
108 	 * ioctl and do pointer arithmetic to figure out the right
109 	 * partition.  We could use DISKPART() too, but this seems
110 	 * "better".
111 	 */
112 	if (origcmd == DIOCGPART) {
113 		struct partinfo *pi, *pi_user;
114 		int labidx;
115 
116 		CTASSERT(sizeof(struct partinfo) <= sizeof(struct disklabel));
117 
118 		pc_ioctl->pm_iocmd = DIOCGPART;
119 		pc_ioctl->pm_flag = 0;
120 
121 		error = pud_request(dev, pc_ioctl, allocsize, class, type);
122 		if (error)
123 			goto out;
124 
125 		pi_user = (struct partinfo *)pc_ioctl->pm_data;
126 		labidx = pi_user->part - &pi_user->disklab->d_partitions[0];
127 		/* userspace error, but punish caller, since we have no infra */
128 		if (labidx >= MAXPARTITIONS) {
129 			error = E2BIG;
130 			goto out;
131 		}
132 
133 		pi = origdata;
134 		pi->disklab = &dl_partinfo;
135 		pi->part = &dl_partinfo.d_partitions[labidx];
136 
137 	}
138 
139  out:
140 	kmem_free(pc_ioctl, allocsize);
141 	return error;
142 }
143 
144 /*
145  * Block de-vices
146  */
147 
148 static dev_type_open(pud_bdev_open);
149 static dev_type_close(pud_bdev_close);
150 static dev_type_strategy(pud_bdev_strategy);
151 static dev_type_ioctl(pud_bdev_ioctl);
152 #if 0
153 static dev_type_dump(pud_bdev_dump);
154 static dev_type_size(pud_bdev_size);
155 #endif
156 
157 struct bdevsw pud_bdevsw = {
158 	.d_open		= pud_bdev_open,
159 	.d_close	= pud_bdev_close,
160 	.d_strategy	= pud_bdev_strategy,
161 	.d_ioctl	= pud_bdev_ioctl,
162 #if 0
163 	.d_dump		= pud_bdev_dump,
164 	.d_psize	= pud_bdev_size,
165 #endif
166 };
167 
168 static int
169 pud_bdev_open(dev_t dev, int flags, int fmt, lwp_t *l)
170 {
171 
172 	return doopenclose(dev, flags, fmt, PUD_REQ_BDEV, PUD_BDEV_OPEN);
173 }
174 
175 static int
176 pud_bdev_close(dev_t dev, int flags, int fmt, lwp_t *l)
177 {
178 
179 	return doopenclose(dev, flags, fmt, PUD_REQ_BDEV, PUD_BDEV_CLOSE);
180 }
181 
182 static void
183 pud_bdev_strategy(struct buf *bp)
184 {
185 	struct pud_req_readwrite *pc_rw;
186 	size_t allocsize;
187 	int error;
188 
189 	allocsize = sizeof(struct pud_req_readwrite) + bp->b_bcount;
190 	pc_rw = kmem_zalloc(allocsize, KM_SLEEP);
191 
192 	pc_rw->pm_offset = bp->b_blkno << DEV_BSHIFT;
193 	pc_rw->pm_resid = bp->b_bcount;
194 
195 	if (BUF_ISWRITE(bp))
196 		memcpy(pc_rw->pm_data, bp->b_data, bp->b_bcount);
197 
198 	error = pud_request(bp->b_dev, pc_rw, allocsize, PUD_REQ_BDEV,
199 	    BUF_ISREAD(bp) ? PUD_BDEV_STRATREAD : PUD_BDEV_STRATWRITE);
200 	if (error)
201 		goto out;
202 
203 	if (pc_rw->pm_resid > bp->b_bcount) {
204 		error = EINVAL;
205 		goto out;
206 	}
207 
208 	if (BUF_ISREAD(bp))
209 		memcpy(bp->b_data,pc_rw->pm_data,bp->b_bcount-pc_rw->pm_resid);
210 
211 	bp->b_resid = pc_rw->pm_resid;
212 
213  out:
214 	kmem_free(pc_rw, allocsize);
215 	bp->b_error = error;
216 	biodone(bp);
217 }
218 
219 int
220 pud_bdev_ioctl(dev_t dev, u_long cmd, void *data, int flag, lwp_t *l)
221 {
222 
223 	return doioctl(dev, cmd, data, flag, PUD_REQ_BDEV, PUD_BDEV_IOCTL);
224 }
225 
226 /* hnmmm */
227 #if 0
228 int
229 pud_bdev_dump(dev_t dev, daddr_t addr, void *data, size_t sz)
230 {
231 
232 	return EOPNOTSUPP;
233 }
234 
235 int
236 pud_bdev_size(dev_t dev)
237 {
238 
239 	return 0;
240 }
241 #endif
242 
243 /*
244  * Charrr devices
245  */
246 
247 static dev_type_open(pud_cdev_open);
248 static dev_type_close(pud_cdev_close);
249 static dev_type_read(pud_cdev_read);
250 static dev_type_write(pud_cdev_write);
251 static dev_type_ioctl(pud_cdev_ioctl);
252 static dev_type_poll(pud_cdev_poll);
253 static dev_type_mmap(pud_cdev_mmap);
254 static dev_type_kqfilter(pud_cdev_kqfilter);
255 
256 struct cdevsw pud_cdevsw = {
257 	.d_open		= pud_cdev_open,
258 	.d_close	= pud_cdev_close,
259 	.d_read		= pud_cdev_read,
260 	.d_write	= pud_cdev_write,
261 	.d_ioctl	= pud_cdev_ioctl,
262 #if 0
263 	.d_stop		= pud_cdev_stop,
264 	.d_tty		= pud_cdev_tty,
265 #endif
266 	.d_poll		= pud_cdev_poll,
267 	.d_mmap		= pud_cdev_mmap,
268 	.d_kqfilter	= pud_cdev_kqfilter,
269 	.d_flag		= D_OTHER,
270 };
271 
272 static int
273 pud_cdev_open(dev_t dev, int flags, int fmt, lwp_t *l)
274 {
275 
276 	return doopenclose(dev, flags, fmt, PUD_REQ_CDEV, PUD_CDEV_OPEN);
277 }
278 
279 static int
280 pud_cdev_close(dev_t dev, int flags, int fmt, lwp_t *l)
281 {
282 
283 	return doopenclose(dev, flags, fmt, PUD_REQ_CDEV, PUD_CDEV_CLOSE);
284 }
285 
286 static int
287 pud_cdev_read(dev_t dev, struct uio *uio, int flag)
288 {
289 	struct pud_creq_read *pc_read;
290 	size_t allocsize;
291 	int error;
292 
293 	allocsize = sizeof(struct pud_creq_read) + uio->uio_resid;
294 	pc_read = kmem_zalloc(allocsize, KM_SLEEP);
295 
296 	pc_read->pm_offset = uio->uio_offset;
297 	pc_read->pm_resid = uio->uio_resid;
298 
299 	error = pud_request(dev, pc_read, allocsize,
300 	    PUD_REQ_CDEV, PUD_CDEV_READ);
301 	if (error)
302 		goto out;
303 
304 	if (pc_read->pm_resid > uio->uio_resid) {
305 		error = EINVAL;
306 		goto out;
307 	}
308 
309 	error = uiomove(pc_read->pm_data,
310 	    uio->uio_resid - pc_read->pm_resid, uio);
311 
312  out:
313 	kmem_free(pc_read, allocsize);
314 	return error;
315 }
316 
317 static int
318 pud_cdev_write(dev_t dev, struct uio *uio, int flag)
319 {
320 	struct pud_creq_write *pc_write;
321 	size_t allocsize;
322 	int error;
323 
324 	allocsize = sizeof(struct pud_creq_write) + uio->uio_resid;
325 	pc_write = kmem_zalloc(allocsize, KM_SLEEP);
326 
327 	pc_write->pm_offset = uio->uio_offset;
328 	pc_write->pm_resid = uio->uio_resid;
329 
330 	error = uiomove(pc_write->pm_data, uio->uio_resid, uio);
331 	if (error)
332 		goto out;
333 
334 	error = pud_request(dev, pc_write, allocsize,
335 	    PUD_REQ_CDEV, PUD_CDEV_WRITE);
336 	if (error)
337 		goto out;
338 
339 	if (pc_write->pm_resid)
340 		error = EIO;
341 
342  out:
343 	kmem_free(pc_write, allocsize);
344 	return error;
345 }
346 
347 static int
348 pud_cdev_ioctl(dev_t dev, u_long cmd, void *data, int flag, lwp_t *l)
349 {
350 
351 	return doioctl(dev, cmd, data, flag, PUD_REQ_CDEV, PUD_CDEV_IOCTL);
352 }
353 
354 static paddr_t
355 pud_cdev_mmap(dev_t dev, off_t off, int flag)
356 {
357 
358 	return (paddr_t)-1;
359 }
360 
361 static int
362 pud_cdev_poll(dev_t dev, int flag, lwp_t *l)
363 {
364 
365 	return EOPNOTSUPP;
366 }
367 
368 static int
369 pud_cdev_kqfilter(dev_t dev, struct knote *kn)
370 {
371 
372 	return EOPNOTSUPP;
373 }
374