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