1 /* 2 * Copyright (c) 2015, 2016, Stephen J. Kiernan 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 29 __FBSDID("$FreeBSD$"); 30 31 #include <sys/param.h> 32 #include <sys/kernel.h> 33 #include <sys/ktr.h> 34 #include <sys/lock.h> 35 #include <sys/malloc.h> 36 #include <sys/module.h> 37 #include <sys/random.h> 38 #include <sys/sbuf.h> 39 #include <sys/sysctl.h> 40 #include <sys/selinfo.h> 41 #include <sys/systm.h> 42 #include <sys/bus.h> 43 #include <sys/rman.h> 44 45 #include <machine/bus.h> 46 #include <machine/resource.h> 47 48 #include <dev/ofw/openfirm.h> 49 #include <dev/ofw/ofw_bus.h> 50 #include <dev/ofw/ofw_bus_subr.h> 51 52 #include <dev/random/randomdev.h> 53 #include <dev/random/random_harvestq.h> 54 55 static device_attach_t bcm2835_rng_attach; 56 static device_detach_t bcm2835_rng_detach; 57 static device_probe_t bcm2835_rng_probe; 58 59 #define RNG_CTRL 0x00 /* RNG Control Register */ 60 #define RNG_COMBLK1_OSC 0x003f0000 /* Combiner Blk 1 Oscillator */ 61 #define RNG_COMBLK1_OSC_SHIFT 16 62 #define RNG_COMBLK2_OSC 0x0fc00000 /* Combiner Blk 2 Oscillator */ 63 #define RNG_COMBLK2_OSC_SHIFT 22 64 #define RNG_JCLK_BYP_DIV_CNT 0x0000ff00 /* Jitter clk bypass divider 65 count */ 66 #define RNG_JCLK_BYP_DIV_CNT_SHIFT 8 67 #define RNG_JCLK_BYP_SRC 0x00000020 /* Jitter clk bypass source */ 68 #define RNG_JCLK_BYP_SEL 0x00000010 /* Jitter clk bypass select */ 69 #define RNG_RBG2X 0x00000002 /* RBG 2X SPEED */ 70 #define RNG_RBGEN_BIT 0x00000001 /* Enable RNG bit */ 71 72 #define BCM2835_RNG_STATUS 0x04 /* BCM2835 RNG status register */ 73 #define BCM2838_RNG_STATUS 0x18 /* BCM2838 RNG status register */ 74 75 #define BCM2838_RNG_COUNT 0x24 /* How many values available */ 76 #define BCM2838_COUNT_VAL_MASK 0x000000ff 77 78 #define BCM2835_RND_VAL_SHIFT 24 /* Shift for valid words */ 79 #define BCM2835_RND_VAL_MASK 0x000000ff /* Number valid words mask */ 80 #define BCM2835_RND_VAL_WARM_CNT 0x40000 /* RNG Warm Up count */ 81 #define BCM2835_RND_WARM_CNT 0xfffff /* RNG Warm Up Count mask */ 82 83 #define BCM2835_RNG_DATA 0x08 /* RNG Data Register */ 84 #define BCM2838_RNG_DATA 0x20 85 #define RNG_FF_THRES 0x0c 86 #define RNG_FF_THRES_MASK 0x0000001f 87 88 #define BCM2835_RNG_INT_MASK 0x10 89 #define BCM2835_RNG_INT_OFF_BIT 0x00000001 90 91 #define RNG_FF_DEFAULT 0x10 /* FIFO threshold default */ 92 #define RNG_FIFO_WORDS (RNG_FF_DEFAULT / sizeof(uint32_t)) 93 94 #define RNG_NUM_OSCILLATORS 6 95 #define RNG_STALL_COUNT_DEFAULT 10 96 97 #define RNG_CALLOUT_TICKS (hz * 4) 98 99 struct bcm_rng_conf { 100 bus_size_t control_reg; 101 bus_size_t status_reg; 102 bus_size_t count_reg; 103 bus_size_t data_reg; 104 bus_size_t intr_mask_reg; 105 uint32_t intr_disable_bit; 106 uint32_t count_value_shift; 107 uint32_t count_value_mask; 108 uint32_t warmup_count; 109 bool allow_2x_mode; 110 bool can_diagnose; 111 /* XXX diag regs */ 112 }; 113 114 static const struct bcm_rng_conf bcm2835_rng_conf = { 115 .control_reg = RNG_CTRL, 116 .status_reg = BCM2835_RNG_STATUS, 117 .count_reg = BCM2835_RNG_STATUS, /* Same register */ 118 .data_reg = BCM2835_RNG_DATA, 119 .intr_mask_reg = BCM2835_RNG_INT_MASK, 120 .intr_disable_bit = BCM2835_RNG_INT_OFF_BIT, 121 .count_value_shift = BCM2835_RND_VAL_SHIFT, 122 .count_value_mask = BCM2835_RND_VAL_MASK, 123 .warmup_count = BCM2835_RND_VAL_WARM_CNT, 124 .allow_2x_mode = true, 125 .can_diagnose = true 126 }; 127 128 static const struct bcm_rng_conf bcm2838_rng_conf = { 129 .control_reg = RNG_CTRL, 130 .status_reg = BCM2838_RNG_STATUS, 131 .count_reg = BCM2838_RNG_COUNT, 132 .data_reg = BCM2838_RNG_DATA, 133 .intr_mask_reg = 0, 134 .intr_disable_bit = 0, 135 .count_value_shift = 0, 136 .count_value_mask = BCM2838_COUNT_VAL_MASK, 137 .warmup_count = 0, 138 .allow_2x_mode = false, 139 .can_diagnose = false 140 }; 141 142 struct bcm2835_rng_softc { 143 device_t sc_dev; 144 struct resource * sc_mem_res; 145 struct resource * sc_irq_res; 146 void * sc_intr_hdl; 147 struct bcm_rng_conf const* conf; 148 uint32_t sc_buf[RNG_FIFO_WORDS]; 149 struct callout sc_rngto; 150 int sc_stall_count; 151 int sc_rbg2x; 152 long sc_underrun; 153 }; 154 155 static struct ofw_compat_data compat_data[] = { 156 {"broadcom,bcm2835-rng", (uintptr_t)&bcm2835_rng_conf}, 157 {"brcm,bcm2835-rng", (uintptr_t)&bcm2835_rng_conf}, 158 159 {"brcm,bcm2711-rng200", (uintptr_t)&bcm2838_rng_conf}, 160 {"brcm,bcm2838-rng", (uintptr_t)&bcm2838_rng_conf}, 161 {"brcm,bcm2838-rng200", (uintptr_t)&bcm2838_rng_conf}, 162 {"brcm,bcm7211-rng", (uintptr_t)&bcm2838_rng_conf}, 163 {"brcm,bcm7278-rng", (uintptr_t)&bcm2838_rng_conf}, 164 {"brcm,iproc-rng200", (uintptr_t)&bcm2838_rng_conf}, 165 {NULL, 0} 166 }; 167 168 static __inline void 169 bcm2835_rng_stat_inc_underrun(struct bcm2835_rng_softc *sc) 170 { 171 172 atomic_add_long(&sc->sc_underrun, 1); 173 } 174 175 static __inline uint32_t 176 bcm2835_rng_read4(struct bcm2835_rng_softc *sc, bus_size_t off) 177 { 178 179 return bus_read_4(sc->sc_mem_res, off); 180 } 181 182 static __inline void 183 bcm2835_rng_read_multi4(struct bcm2835_rng_softc *sc, bus_size_t off, 184 uint32_t *datap, bus_size_t count) 185 { 186 187 bus_read_multi_4(sc->sc_mem_res, off, datap, count); 188 } 189 190 static __inline void 191 bcm2835_rng_write4(struct bcm2835_rng_softc *sc, bus_size_t off, uint32_t val) 192 { 193 194 bus_write_4(sc->sc_mem_res, off, val); 195 } 196 197 static void 198 bcm2835_rng_dump_registers(struct bcm2835_rng_softc *sc, struct sbuf *sbp) 199 { 200 uint32_t comblk2_osc, comblk1_osc, jclk_byp_div, val; 201 int i; 202 203 if (!sc->conf->can_diagnose) 204 /* Not implemented. */ 205 return; 206 207 /* Display RNG control register contents */ 208 val = bcm2835_rng_read4(sc, sc->conf->control_reg); 209 sbuf_printf(sbp, "RNG_CTRL (%08x)\n", val); 210 211 comblk2_osc = (val & RNG_COMBLK2_OSC) >> RNG_COMBLK2_OSC_SHIFT; 212 sbuf_printf(sbp, " RNG_COMBLK2_OSC (%02x)\n", comblk2_osc); 213 for (i = 0; i < RNG_NUM_OSCILLATORS; i++) 214 if ((comblk2_osc & (1 << i)) == 0) 215 sbuf_printf(sbp, " Oscillator %d enabled\n", i + 1); 216 217 comblk1_osc = (val & RNG_COMBLK1_OSC) >> RNG_COMBLK1_OSC_SHIFT; 218 sbuf_printf(sbp, " RNG_COMBLK1_OSC (%02x)\n", comblk1_osc); 219 for (i = 0; i < RNG_NUM_OSCILLATORS; i++) 220 if ((comblk1_osc & (1 << i)) == 0) 221 sbuf_printf(sbp, " Oscillator %d enabled\n", i + 1); 222 223 jclk_byp_div = (val & RNG_JCLK_BYP_DIV_CNT) >> 224 RNG_JCLK_BYP_DIV_CNT_SHIFT; 225 sbuf_printf(sbp, 226 " RNG_JCLK_BYP_DIV_CNT (%02x)\n APB clock frequency / %d\n", 227 jclk_byp_div, 2 * (jclk_byp_div + 1)); 228 229 sbuf_printf(sbp, " RNG_JCLK_BYP_SRC:\n %s\n", 230 (val & RNG_JCLK_BYP_SRC) ? "Use divided down APB clock" : 231 "Use RNG clock (APB clock)"); 232 233 sbuf_printf(sbp, " RNG_JCLK_BYP_SEL:\n %s\n", 234 (val & RNG_JCLK_BYP_SEL) ? "Bypass internal jitter clock" : 235 "Use internal jitter clock"); 236 237 if ((val & RNG_RBG2X) != 0) 238 sbuf_cat(sbp, " RNG_RBG2X: RNG 2X SPEED enabled\n"); 239 240 if ((val & RNG_RBGEN_BIT) != 0) 241 sbuf_cat(sbp, " RNG_RBGEN_BIT: RBG enabled\n"); 242 243 /* Display RNG status register contents */ 244 val = bcm2835_rng_read4(sc, sc->conf->status_reg); 245 sbuf_printf(sbp, "RNG_CTRL (%08x)\n", val); 246 sbuf_printf(sbp, " RND_VAL: %02x\n", 247 (val >> sc->conf->count_value_shift) & sc->conf->count_value_mask); 248 sbuf_printf(sbp, " RND_WARM_CNT: %05x\n", val & sc->conf->warmup_count); 249 250 /* Display FIFO threshold register contents */ 251 val = bcm2835_rng_read4(sc, RNG_FF_THRES); 252 sbuf_printf(sbp, "RNG_FF_THRES: %05x\n", val & RNG_FF_THRES_MASK); 253 254 /* Display interrupt mask register contents */ 255 val = bcm2835_rng_read4(sc, sc->conf->intr_mask_reg); 256 sbuf_printf(sbp, "RNG_INT_MASK: interrupt %s\n", 257 ((val & sc->conf->intr_disable_bit) != 0) ? "disabled" : "enabled"); 258 } 259 260 static void 261 bcm2835_rng_disable_intr(struct bcm2835_rng_softc *sc) 262 { 263 uint32_t mask; 264 265 /* Set the interrupt off bit in the interrupt mask register */ 266 mask = bcm2835_rng_read4(sc, sc->conf->intr_mask_reg); 267 mask |= sc->conf->intr_disable_bit; 268 bcm2835_rng_write4(sc, sc->conf->intr_mask_reg, mask); 269 } 270 271 static void 272 bcm2835_rng_start(struct bcm2835_rng_softc *sc) 273 { 274 uint32_t ctrl; 275 276 /* Disable the interrupt */ 277 if (sc->conf->intr_mask_reg) 278 bcm2835_rng_disable_intr(sc); 279 280 /* Set the warmup count */ 281 if (sc->conf->warmup_count > 0) 282 bcm2835_rng_write4(sc, sc->conf->status_reg, 283 sc->conf->warmup_count); 284 285 /* Enable the RNG */ 286 ctrl = bcm2835_rng_read4(sc, sc->conf->control_reg); 287 ctrl |= RNG_RBGEN_BIT; 288 if (sc->sc_rbg2x && sc->conf->allow_2x_mode) 289 ctrl |= RNG_RBG2X; 290 bcm2835_rng_write4(sc, sc->conf->control_reg, ctrl); 291 } 292 293 static void 294 bcm2835_rng_stop(struct bcm2835_rng_softc *sc) 295 { 296 uint32_t ctrl; 297 298 /* Disable the RNG */ 299 ctrl = bcm2835_rng_read4(sc, sc->conf->control_reg); 300 ctrl &= ~RNG_RBGEN_BIT; 301 bcm2835_rng_write4(sc, sc->conf->control_reg, ctrl); 302 } 303 304 static void 305 bcm2835_rng_enqueue_harvest(struct bcm2835_rng_softc *sc, uint32_t nread) 306 { 307 char *sc_buf_chunk; 308 uint32_t chunk_size; 309 uint32_t cnt; 310 311 chunk_size = sizeof(((struct harvest_event *)0)->he_entropy); 312 cnt = nread * sizeof(uint32_t); 313 sc_buf_chunk = (void*)sc->sc_buf; 314 315 while (cnt > 0) { 316 uint32_t size; 317 318 size = MIN(cnt, chunk_size); 319 320 random_harvest_queue(sc_buf_chunk, size, RANDOM_PURE_BROADCOM); 321 322 sc_buf_chunk += size; 323 cnt -= size; 324 } 325 } 326 327 static void 328 bcm2835_rng_harvest(void *arg) 329 { 330 uint32_t *dest; 331 uint32_t hwcount; 332 u_int cnt, nread, num_avail, num_words; 333 int seen_underrun, num_stalls; 334 struct bcm2835_rng_softc *sc = arg; 335 336 dest = sc->sc_buf; 337 nread = num_words = 0; 338 seen_underrun = num_stalls = 0; 339 340 for (cnt = sizeof(sc->sc_buf) / sizeof(uint32_t); cnt > 0; 341 cnt -= num_words) { 342 /* Read count register to find out how many words available */ 343 hwcount = bcm2835_rng_read4(sc, sc->conf->count_reg); 344 num_avail = (hwcount >> sc->conf->count_value_shift) & 345 sc->conf->count_value_mask; 346 347 /* If we have none... */ 348 if (num_avail == 0) { 349 bcm2835_rng_stat_inc_underrun(sc); 350 if (++seen_underrun >= sc->sc_stall_count) { 351 if (num_stalls++ > 0) { 352 device_printf(sc->sc_dev, 353 "RNG stalled, disabling device\n"); 354 bcm2835_rng_stop(sc); 355 break; 356 } else { 357 device_printf(sc->sc_dev, 358 "Too many underruns, resetting\n"); 359 bcm2835_rng_stop(sc); 360 bcm2835_rng_start(sc); 361 seen_underrun = 0; 362 } 363 } 364 /* Try again */ 365 continue; 366 } 367 368 CTR2(KTR_DEV, "%s: %d words available in RNG FIFO", 369 device_get_nameunit(sc->sc_dev), num_avail); 370 371 /* Pull MIN(num_avail, cnt) words from the FIFO */ 372 num_words = (num_avail > cnt) ? cnt : num_avail; 373 bcm2835_rng_read_multi4(sc, sc->conf->data_reg, dest, 374 num_words); 375 dest += num_words; 376 nread += num_words; 377 } 378 379 bcm2835_rng_enqueue_harvest(sc, nread); 380 381 callout_reset(&sc->sc_rngto, RNG_CALLOUT_TICKS, bcm2835_rng_harvest, sc); 382 } 383 384 static int 385 sysctl_bcm2835_rng_2xspeed(SYSCTL_HANDLER_ARGS) 386 { 387 struct bcm2835_rng_softc *sc = arg1; 388 int error, rbg2x; 389 390 rbg2x = sc->sc_rbg2x; 391 error = sysctl_handle_int(oidp, &rbg2x, 0, req); 392 if (error) 393 return (error); 394 if (req->newptr == NULL) 395 return (error); 396 if (rbg2x == sc->sc_rbg2x) 397 return (0); 398 399 /* Reset the RNG */ 400 bcm2835_rng_stop(sc); 401 sc->sc_rbg2x = rbg2x; 402 bcm2835_rng_start(sc); 403 404 return (0); 405 } 406 407 #ifdef BCM2835_RNG_DEBUG_REGISTERS 408 static int 409 sysctl_bcm2835_rng_dump(SYSCTL_HANDLER_ARGS) 410 { 411 struct sbuf sb; 412 struct bcm2835_rng_softc *sc = arg1; 413 int error; 414 415 error = sysctl_wire_old_buffer(req, 0); 416 if (error != 0) 417 return (error); 418 sbuf_new_for_sysctl(&sb, NULL, 128, req); 419 bcm2835_rng_dump_registers(sc, &sb); 420 error = sbuf_finish(&sb); 421 sbuf_delete(&sb); 422 return (error); 423 } 424 #endif 425 426 static int 427 bcm2835_rng_probe(device_t dev) 428 { 429 430 if (!ofw_bus_status_okay(dev)) 431 return (ENXIO); 432 433 if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 434 return (ENXIO); 435 436 device_set_desc(dev, "Broadcom BCM2835/BCM2838 RNG"); 437 438 return (BUS_PROBE_DEFAULT); 439 } 440 441 static int 442 bcm2835_rng_attach(device_t dev) 443 { 444 struct bcm2835_rng_softc *sc; 445 struct sysctl_ctx_list *sysctl_ctx; 446 struct sysctl_oid *sysctl_tree; 447 int error, rid; 448 449 error = 0; 450 sc = device_get_softc(dev); 451 sc->sc_dev = dev; 452 453 sc->conf = (void const*)ofw_bus_search_compatible(dev, compat_data)->ocd_data; 454 KASSERT(sc->conf != NULL, ("bcm2835_rng_attach: sc->conf == NULL")); 455 456 sc->sc_stall_count = RNG_STALL_COUNT_DEFAULT; 457 458 /* Initialize callout */ 459 callout_init(&sc->sc_rngto, CALLOUT_MPSAFE); 460 461 TUNABLE_INT_FETCH("bcmrng.stall_count", &sc->sc_stall_count); 462 if (sc->conf->allow_2x_mode) 463 TUNABLE_INT_FETCH("bcmrng.2xspeed", &sc->sc_rbg2x); 464 465 /* Allocate memory resources */ 466 rid = 0; 467 sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 468 RF_ACTIVE); 469 if (sc->sc_mem_res == NULL) { 470 bcm2835_rng_detach(dev); 471 return (ENXIO); 472 } 473 474 /* Start the RNG */ 475 bcm2835_rng_start(sc); 476 477 /* Dump the registers if booting verbose */ 478 if (bootverbose) { 479 struct sbuf sb; 480 481 (void) sbuf_new(&sb, NULL, 256, 482 SBUF_AUTOEXTEND | SBUF_INCLUDENUL); 483 bcm2835_rng_dump_registers(sc, &sb); 484 sbuf_trim(&sb); 485 error = sbuf_finish(&sb); 486 if (error == 0) 487 device_printf(dev, "%s", sbuf_data(&sb)); 488 sbuf_delete(&sb); 489 } 490 491 sysctl_ctx = device_get_sysctl_ctx(dev); 492 sysctl_tree = device_get_sysctl_tree(dev); 493 SYSCTL_ADD_LONG(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO, 494 "underrun", CTLFLAG_RD, &sc->sc_underrun, 495 "Number of FIFO underruns"); 496 if (sc->conf->allow_2x_mode) 497 SYSCTL_ADD_PROC(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO, 498 "2xspeed", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, 499 sysctl_bcm2835_rng_2xspeed, "I", "Enable RBG 2X SPEED"); 500 SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO, 501 "stall_count", CTLFLAG_RW, &sc->sc_stall_count, 502 RNG_STALL_COUNT_DEFAULT, "Number of underruns to assume RNG stall"); 503 #ifdef BCM2835_RNG_DEBUG_REGISTERS 504 SYSCTL_ADD_PROC(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO, 505 "dumpregs", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc, 0, 506 sysctl_bcm2835_rng_dump, "S", "Dump RNG registers"); 507 #endif 508 509 /* 510 * Schedule the initial harvesting one second from now, which should give the 511 * hardware RNG plenty of time to generate the first random bytes. 512 */ 513 callout_reset(&sc->sc_rngto, hz, bcm2835_rng_harvest, sc); 514 515 return (0); 516 } 517 518 static int 519 bcm2835_rng_detach(device_t dev) 520 { 521 struct bcm2835_rng_softc *sc; 522 523 sc = device_get_softc(dev); 524 525 /* Stop the RNG */ 526 bcm2835_rng_stop(sc); 527 528 /* Drain the callout it */ 529 callout_drain(&sc->sc_rngto); 530 531 /* Release memory resource */ 532 if (sc->sc_mem_res != NULL) 533 bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); 534 535 return (0); 536 } 537 538 static device_method_t bcm2835_rng_methods[] = { 539 /* Device interface */ 540 DEVMETHOD(device_probe, bcm2835_rng_probe), 541 DEVMETHOD(device_attach, bcm2835_rng_attach), 542 DEVMETHOD(device_detach, bcm2835_rng_detach), 543 544 DEVMETHOD_END 545 }; 546 547 static driver_t bcm2835_rng_driver = { 548 "bcmrng", 549 bcm2835_rng_methods, 550 sizeof(struct bcm2835_rng_softc) 551 }; 552 553 DRIVER_MODULE(bcm2835_rng, simplebus, bcm2835_rng_driver, 0, 0); 554 DRIVER_MODULE(bcm2835_rng, ofwbus, bcm2835_rng_driver, 0, 0); 555 MODULE_VERSION(bcm2835_rng, 1); 556 MODULE_DEPEND(bcm2835_rng, randomdev, 1, 1, 1); 557