1 /* $NetBSD: pud.c,v 1.11 2011/07/08 09:32:45 mrg 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.c,v 1.11 2011/07/08 09:32:45 mrg Exp $"); 33 34 #include <sys/param.h> 35 #include <sys/conf.h> 36 #include <sys/kmem.h> 37 #include <sys/module.h> 38 #include <sys/poll.h> 39 #include <sys/queue.h> 40 41 #include <dev/pud/pud_sys.h> 42 #include <dev/putter/putter_sys.h> 43 44 void pudattach(void); 45 46 static int pud_putter_getout(void *, size_t, int, uint8_t **, 47 size_t *, void **); 48 static void pud_putter_releaseout(void *, void *, int); 49 static int pud_putter_dispatch(void *, struct putter_hdr *); 50 static size_t pud_putter_waitcount(void *); 51 static int pud_putter_close(void *); 52 53 struct putter_ops pud_putter = { 54 .pop_getout = pud_putter_getout, 55 .pop_releaseout = pud_putter_releaseout, 56 .pop_waitcount = pud_putter_waitcount, 57 .pop_dispatch = pud_putter_dispatch, 58 .pop_close = pud_putter_close, 59 }; 60 61 extern struct bdevsw pud_bdevsw; 62 extern struct cdevsw pud_cdevsw; 63 64 kmutex_t pud_mtx; 65 static LIST_HEAD(, pud_dev) pudlist = LIST_HEAD_INITIALIZER(pudlist); 66 67 static uint64_t 68 nextreq(struct pud_dev *pd) 69 { 70 uint64_t rv; 71 72 mutex_enter(&pd->pd_mtx); 73 rv = pd->pd_nextreq++; 74 mutex_exit(&pd->pd_mtx); 75 76 return rv; 77 } 78 79 static int 80 pud_putter_getout(void *this, size_t maxsize, int nonblock, 81 uint8_t **data, size_t *dlen, void **cookie) 82 { 83 struct pud_dev *pd = this; 84 struct pud_touser *putp = NULL; 85 int error = 0; 86 87 mutex_enter(&pd->pd_mtx); 88 for (;;) { 89 if (TAILQ_EMPTY(&pd->pd_waitq_req)) { 90 if (nonblock) { 91 error = EWOULDBLOCK; 92 break; 93 } 94 95 error = cv_wait_sig(&pd->pd_waitq_req_cv, &pd->pd_mtx); 96 if (error) 97 break; 98 else 99 continue; 100 } 101 102 putp = TAILQ_FIRST(&pd->pd_waitq_req); 103 TAILQ_REMOVE(&pd->pd_waitq_req, putp, pt_entries); 104 KASSERT(error == 0); 105 break; 106 } 107 mutex_exit(&pd->pd_mtx); 108 109 if (error == 0) { 110 *data = (uint8_t *)putp->pt_pdr; 111 *dlen = putp->pt_pdr->pdr_pth.pth_framelen; 112 *cookie = putp; 113 } 114 115 return error; 116 } 117 118 static void 119 pud_putter_releaseout(void *this, void *cookie, int status) 120 { 121 struct pud_dev *pd = this; 122 struct pud_touser *putp = cookie; 123 124 mutex_enter(&pd->pd_mtx); 125 TAILQ_INSERT_TAIL(&pd->pd_waitq_resp, putp, pt_entries); 126 mutex_exit(&pd->pd_mtx); 127 128 } 129 130 static size_t 131 pud_putter_waitcount(void *this) 132 { 133 struct pud_dev *pd = this; 134 size_t rv; 135 136 mutex_enter(&pd->pd_mtx); 137 rv = pd->pd_waitcount; 138 mutex_exit(&pd->pd_mtx); 139 140 return rv; 141 } 142 143 static int 144 pudop_dev(struct pud_dev *pd, struct pud_req *pdr) 145 { 146 struct putter_hdr *pth = (void *)pdr; 147 struct pud_touser *putp; 148 149 mutex_enter(&pd->pd_mtx); 150 TAILQ_FOREACH(putp, &pd->pd_waitq_resp, pt_entries) 151 if (putp->pt_pdr->pdr_reqid == pdr->pdr_reqid) 152 break; 153 if (putp == NULL) { 154 mutex_exit(&pd->pd_mtx); 155 return EINVAL; 156 } 157 TAILQ_REMOVE(&pd->pd_waitq_resp, putp, pt_entries); 158 mutex_exit(&pd->pd_mtx); 159 160 if (pth->pth_framelen > putp->pt_pdr->pdr_len) { 161 return EINVAL; 162 } 163 memcpy(putp->pt_pdr, pth, pth->pth_framelen); 164 cv_signal(&putp->pt_cv); 165 166 return 0; 167 } 168 169 /* 170 * Register our major number. Always register char device functions, 171 * register block devices optionally. 172 * 173 * XXX: no way to configure "any major you like" currently. 174 */ 175 static int 176 pudconf_reg(struct pud_dev *pd, struct pud_conf_reg *pcr) 177 { 178 struct bdevsw *bsw; 179 devmajor_t cmajor, bmajor; 180 int error; 181 182 if (pcr->pm_version != (PUD_DEVELVERSION | PUD_VERSION)) { 183 printf("pud version mismatch %d vs %d\n", 184 pcr->pm_version & ~PUD_DEVELVERSION, PUD_VERSION); 185 return EINVAL; /* XXX */ 186 } 187 188 cmajor = major(pcr->pm_regdev); 189 if (pcr->pm_flags & PUD_CONFFLAG_BDEV) { 190 bsw = &pud_bdevsw; 191 bmajor = cmajor; 192 } else { 193 bsw = NULL; 194 bmajor = NODEVMAJOR; 195 } 196 197 pcr->pm_devname[PUD_DEVNAME_MAX] = '\0'; 198 error = devsw_attach(pcr->pm_devname, bsw, &bmajor, 199 &pud_cdevsw, &cmajor); 200 if (error == 0) 201 pd->pd_dev = pcr->pm_regdev; 202 203 return error; 204 } 205 206 static int 207 pudop_conf(struct pud_dev *pd, struct pud_req *pdr) 208 { 209 int rv; 210 211 switch (pdr->pdr_reqtype) { 212 case PUD_CONF_REG: 213 rv = pudconf_reg(pd, (struct pud_conf_reg *)pdr); 214 break; 215 case PUD_CONF_DEREG: 216 /* unimplemented */ 217 rv = 0; 218 break; 219 default: 220 rv = EINVAL; 221 break; 222 } 223 224 return rv; 225 } 226 227 static int 228 pud_putter_dispatch(void *this, struct putter_hdr *pth) 229 { 230 struct pud_dev *pd = this; 231 struct pud_req *pdr = (void *)pth; 232 int rv; 233 234 if (pdr->pdr_pth.pth_framelen < sizeof(struct pud_req)) 235 return EINVAL; 236 237 switch (pdr->pdr_reqclass) { 238 case PUD_REQ_CDEV: 239 case PUD_REQ_BDEV: 240 rv = pudop_dev(pd, pdr); 241 break; 242 case PUD_REQ_CONF: 243 rv = pudop_conf(pd, pdr); 244 break; 245 default: 246 rv = EINVAL; 247 break; 248 } 249 250 return rv; 251 } 252 253 /* Device server severed the umbilical cord */ 254 static int 255 pud_putter_close(void *this) 256 { 257 struct pud_dev *pd = this; 258 struct pud_touser *putp; 259 260 mutex_enter(&pud_mtx); 261 LIST_REMOVE(pd, pd_entries); 262 mutex_exit(&pud_mtx); 263 264 mutex_enter(&pd->pd_mtx); 265 while ((putp = TAILQ_FIRST(&pd->pd_waitq_req)) != NULL) { 266 putp->pt_pdr->pdr_rv = ENXIO; 267 cv_signal(&putp->pt_cv); 268 TAILQ_REMOVE(&pd->pd_waitq_req, putp, pt_entries); 269 } 270 271 while ((putp = TAILQ_FIRST(&pd->pd_waitq_resp)) != NULL) { 272 putp->pt_pdr->pdr_rv = ENXIO; 273 cv_signal(&putp->pt_cv); 274 TAILQ_REMOVE(&pd->pd_waitq_resp, putp, pt_entries); 275 } 276 if (pd->pd_waitcount) 277 cv_wait(&pd->pd_draincv, &pd->pd_mtx); 278 KASSERT(pd->pd_waitcount == 0); 279 280 mutex_exit(&pd->pd_mtx); 281 282 if (pd->pd_dev) 283 devsw_detach(&pud_bdevsw /* XXX */, &pud_cdevsw); 284 285 putter_detach(pd->pd_pi); 286 287 mutex_destroy(&pd->pd_mtx); 288 cv_destroy(&pd->pd_draincv); 289 cv_destroy(&pd->pd_waitq_req_cv); 290 kmem_free(pd, sizeof(struct pud_dev)); 291 292 return 0; 293 } 294 295 struct pud_dev * 296 pud_dev2pud(dev_t dev) 297 { 298 struct pud_dev *pd; 299 300 mutex_enter(&pud_mtx); 301 LIST_FOREACH(pd, &pudlist, pd_entries) 302 if (major(pd->pd_dev) == major(dev)) 303 break; 304 mutex_exit(&pud_mtx); 305 306 return pd; 307 } 308 309 /* Toss request to the device server and wait for result */ 310 int 311 pud_request(dev_t dev, void *data, size_t dlen, int class, int type) 312 { 313 struct pud_touser put; 314 struct pud_req *pdr = data; 315 struct pud_dev *pd; 316 317 pd = pud_dev2pud(dev); 318 if (pd == NULL) 319 return ENXIO; 320 321 pdr->pdr_dev = dev; 322 pdr->pdr_len = pdr->pdr_pth.pth_framelen = dlen; 323 pdr->pdr_reqid = nextreq(pd); 324 325 pdr->pdr_reqclass = class; 326 pdr->pdr_reqtype = type; 327 328 put.pt_pdr = pdr; 329 cv_init(&put.pt_cv, "pudresp"); 330 331 mutex_enter(&pd->pd_mtx); 332 pd->pd_waitcount++; 333 334 TAILQ_INSERT_TAIL(&pd->pd_waitq_req, &put, pt_entries); 335 putter_notify(pd->pd_pi); 336 cv_broadcast(&pd->pd_waitq_req_cv); 337 cv_wait(&put.pt_cv, &pd->pd_mtx); 338 339 if (--pd->pd_waitcount == 0) 340 cv_signal(&pd->pd_draincv); 341 mutex_exit(&pd->pd_mtx); 342 cv_destroy(&put.pt_cv); 343 344 return pdr->pdr_rv; 345 } 346 347 /* Called from putter based on minor dev number */ 348 int 349 pud_config(int fd, int flags, int fmt) 350 { 351 struct pud_dev *pd; 352 353 pd = kmem_zalloc(sizeof(struct pud_dev), KM_SLEEP); 354 pd->pd_pi = putter_attach(curlwp->l_proc->p_pid, fd, pd, &pud_putter); 355 if (pd->pd_pi == NULL) { 356 kmem_free(pd, sizeof(struct pud_dev)); 357 return ENOENT; /* XXX */ 358 } 359 pd->pd_dev = NODEV; 360 361 mutex_init(&pd->pd_mtx, MUTEX_DEFAULT, IPL_NONE); 362 TAILQ_INIT(&pd->pd_waitq_req); 363 TAILQ_INIT(&pd->pd_waitq_resp); 364 cv_init(&pd->pd_waitq_req_cv, "pudreq"); 365 cv_init(&pd->pd_draincv, "pudrain"); 366 367 mutex_enter(&pud_mtx); 368 LIST_INSERT_HEAD(&pudlist, pd, pd_entries); 369 mutex_exit(&pud_mtx); 370 371 return 0; 372 } 373 374 void 375 pudattach(void) 376 { 377 int error; 378 379 if ((error = putter_register(pud_config, PUTTER_MINOR_PUD)) != 0) { 380 printf("pudattach: can't register to putter: %d\n", error); 381 return; 382 } 383 mutex_init(&pud_mtx, MUTEX_DEFAULT, IPL_NONE); 384 } 385 386 MODULE(MODULE_CLASS_DRIVER, pud, "putter"); 387 388 static int 389 pud_modcmd(modcmd_t cmd, void *arg) 390 { 391 #ifdef _MODULE 392 devmajor_t bmajor = NODEVMAJOR, cmajor = NODEVMAJOR; 393 394 switch (cmd) { 395 case MODULE_CMD_INIT: 396 pudattach(); 397 return devsw_attach("pud", NULL, &bmajor, 398 &pud_cdevsw, &cmajor); 399 case MODULE_CMD_FINI: 400 return ENOTTY; /* XXX: puddetach */ 401 default: 402 return ENOTTY; 403 } 404 #else 405 if (cmd == MODULE_CMD_INIT) 406 return 0; 407 return ENOTTY; 408 #endif 409 } 410