1255643Snwhitehorn/*- 2255643Snwhitehorn * Copyright (C) 2011 by Nathan Whitehorn. All rights reserved. 3255643Snwhitehorn * 4255643Snwhitehorn * Redistribution and use in source and binary forms, with or without 5255643Snwhitehorn * modification, are permitted provided that the following conditions 6255643Snwhitehorn * are met: 7255643Snwhitehorn * 1. Redistributions of source code must retain the above copyright 8255643Snwhitehorn * notice, this list of conditions and the following disclaimer. 9255643Snwhitehorn * 2. Redistributions in binary form must reproduce the above copyright 10255643Snwhitehorn * notice, this list of conditions and the following disclaimer in the 11255643Snwhitehorn * documentation and/or other materials provided with the distribution. 12255643Snwhitehorn * 13255643Snwhitehorn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14255643Snwhitehorn * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15255643Snwhitehorn * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16255643Snwhitehorn * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 17255643Snwhitehorn * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 18255643Snwhitehorn * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 19255643Snwhitehorn * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 20255643Snwhitehorn * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 21255643Snwhitehorn * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 22255643Snwhitehorn * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23255643Snwhitehorn */ 24255643Snwhitehorn 25255643Snwhitehorn#include <sys/cdefs.h> 26255643Snwhitehorn__FBSDID("$FreeBSD: releng/10.3/sys/powerpc/pseries/phyp_console.c 266019 2014-05-14 14:08:45Z ian $"); 27255643Snwhitehorn 28255643Snwhitehorn#include <sys/param.h> 29255643Snwhitehorn#include <sys/kdb.h> 30255643Snwhitehorn#include <sys/kernel.h> 31255643Snwhitehorn#include <sys/priv.h> 32255643Snwhitehorn#include <sys/systm.h> 33255643Snwhitehorn#include <sys/module.h> 34255643Snwhitehorn#include <sys/types.h> 35255643Snwhitehorn#include <sys/conf.h> 36255643Snwhitehorn#include <sys/cons.h> 37255643Snwhitehorn#include <sys/tty.h> 38255643Snwhitehorn#include <machine/bus.h> 39255643Snwhitehorn 40255643Snwhitehorn#include <dev/ofw/openfirm.h> 41255643Snwhitehorn#include <dev/ofw/ofw_bus.h> 42255643Snwhitehorn#include <dev/ofw/ofw_bus_subr.h> 43255643Snwhitehorn#include <dev/uart/uart.h> 44255643Snwhitehorn#include <dev/uart/uart_cpu.h> 45255643Snwhitehorn#include <dev/uart/uart_bus.h> 46255643Snwhitehorn 47255643Snwhitehorn#include "phyp-hvcall.h" 48255643Snwhitehorn#include "uart_if.h" 49255643Snwhitehorn 50255643Snwhitehornstruct uart_phyp_softc { 51255643Snwhitehorn device_t dev; 52255643Snwhitehorn phandle_t node; 53255643Snwhitehorn int vtermid; 54255643Snwhitehorn 55255643Snwhitehorn struct tty *tp; 56255643Snwhitehorn struct resource *irqres; 57255643Snwhitehorn int irqrid; 58255643Snwhitehorn struct callout callout; 59255643Snwhitehorn void *sc_icookie; 60255643Snwhitehorn int polltime; 61255643Snwhitehorn 62255643Snwhitehorn struct mtx sc_mtx; 63255643Snwhitehorn int protocol; 64255643Snwhitehorn 65255643Snwhitehorn union { 66255643Snwhitehorn uint64_t u64[2]; 67255643Snwhitehorn char str[16]; 68255643Snwhitehorn } phyp_inbuf; 69255643Snwhitehorn uint64_t inbuflen; 70255643Snwhitehorn uint8_t outseqno; 71255643Snwhitehorn}; 72255643Snwhitehorn 73255643Snwhitehornstatic struct uart_phyp_softc *console_sc = NULL; 74255643Snwhitehorn#if defined(KDB) 75255643Snwhitehornstatic int alt_break_state; 76255643Snwhitehorn#endif 77255643Snwhitehorn 78255643Snwhitehornenum { 79255643Snwhitehorn HVTERM1, HVTERMPROT 80255643Snwhitehorn}; 81255643Snwhitehorn 82255643Snwhitehorn#define VS_DATA_PACKET_HEADER 0xff 83255643Snwhitehorn#define VS_CONTROL_PACKET_HEADER 0xfe 84255643Snwhitehorn#define VSV_SET_MODEM_CTL 0x01 85255643Snwhitehorn#define VSV_MODEM_CTL_UPDATE 0x02 86255643Snwhitehorn#define VSV_RENEGOTIATE_CONNECTION 0x03 87255643Snwhitehorn#define VS_QUERY_PACKET_HEADER 0xfd 88255643Snwhitehorn#define VSV_SEND_VERSION_NUMBER 0x01 89255643Snwhitehorn#define VSV_SEND_MODEM_CTL_STATUS 0x02 90255643Snwhitehorn#define VS_QUERY_RESPONSE_PACKET_HEADER 0xfc 91255643Snwhitehorn 92255643Snwhitehornstatic int uart_phyp_probe(device_t dev); 93255643Snwhitehornstatic int uart_phyp_attach(device_t dev); 94255643Snwhitehornstatic void uart_phyp_intr(void *v); 95255643Snwhitehorn 96255643Snwhitehornstatic device_method_t uart_phyp_methods[] = { 97255643Snwhitehorn /* Device interface */ 98255643Snwhitehorn DEVMETHOD(device_probe, uart_phyp_probe), 99255643Snwhitehorn DEVMETHOD(device_attach, uart_phyp_attach), 100255643Snwhitehorn 101255643Snwhitehorn DEVMETHOD_END 102255643Snwhitehorn}; 103255643Snwhitehorn 104255643Snwhitehornstatic driver_t uart_phyp_driver = { 105255643Snwhitehorn "uart", 106255643Snwhitehorn uart_phyp_methods, 107255643Snwhitehorn sizeof(struct uart_phyp_softc), 108255643Snwhitehorn}; 109255643Snwhitehorn 110255643SnwhitehornDRIVER_MODULE(uart_phyp, vdevice, uart_phyp_driver, uart_devclass, 0, 0); 111255643Snwhitehorn 112255643Snwhitehornstatic cn_probe_t uart_phyp_cnprobe; 113255643Snwhitehornstatic cn_init_t uart_phyp_cninit; 114255643Snwhitehornstatic cn_term_t uart_phyp_cnterm; 115255643Snwhitehornstatic cn_getc_t uart_phyp_cngetc; 116255643Snwhitehornstatic cn_putc_t uart_phyp_cnputc; 117255643Snwhitehornstatic cn_grab_t uart_phyp_cngrab; 118255643Snwhitehornstatic cn_ungrab_t uart_phyp_cnungrab; 119255643Snwhitehorn 120255643SnwhitehornCONSOLE_DRIVER(uart_phyp); 121255643Snwhitehorn 122255643Snwhitehornstatic void uart_phyp_ttyoutwakeup(struct tty *tp); 123255643Snwhitehorn 124255643Snwhitehornstatic struct ttydevsw uart_phyp_tty_class = { 125255643Snwhitehorn .tsw_flags = TF_INITLOCK|TF_CALLOUT, 126255643Snwhitehorn .tsw_outwakeup = uart_phyp_ttyoutwakeup, 127255643Snwhitehorn}; 128255643Snwhitehorn 129255643Snwhitehornstatic int 130255643Snwhitehornuart_phyp_probe_node(struct uart_phyp_softc *sc) 131255643Snwhitehorn{ 132255643Snwhitehorn phandle_t node = sc->node; 133255643Snwhitehorn uint32_t reg; 134255643Snwhitehorn char buf[64]; 135255643Snwhitehorn 136255643Snwhitehorn sc->inbuflen = 0; 137255643Snwhitehorn sc->outseqno = 0; 138255643Snwhitehorn 139255643Snwhitehorn if (OF_getprop(node, "name", buf, sizeof(buf)) <= 0) 140255643Snwhitehorn return (ENXIO); 141255643Snwhitehorn if (strcmp(buf, "vty") != 0) 142255643Snwhitehorn return (ENXIO); 143255643Snwhitehorn 144255643Snwhitehorn if (OF_getprop(node, "device_type", buf, sizeof(buf)) <= 0) 145255643Snwhitehorn return (ENXIO); 146255643Snwhitehorn if (strcmp(buf, "serial") != 0) 147255643Snwhitehorn return (ENXIO); 148255643Snwhitehorn 149255643Snwhitehorn reg = -1; 150255643Snwhitehorn OF_getprop(node, "reg", ®, sizeof(reg)); 151255643Snwhitehorn if (reg == -1) 152255643Snwhitehorn return (ENXIO); 153255895Snwhitehorn sc->vtermid = reg; 154255643Snwhitehorn sc->node = node; 155255643Snwhitehorn 156255643Snwhitehorn if (OF_getprop(node, "compatible", buf, sizeof(buf)) <= 0) 157255643Snwhitehorn return (ENXIO); 158255643Snwhitehorn if (strcmp(buf, "hvterm1") == 0) { 159255643Snwhitehorn sc->protocol = HVTERM1; 160255643Snwhitehorn return (0); 161255643Snwhitehorn } else if (strcmp(buf, "hvterm-protocol") == 0) { 162255643Snwhitehorn sc->protocol = HVTERMPROT; 163255643Snwhitehorn return (0); 164255643Snwhitehorn } 165255643Snwhitehorn 166255643Snwhitehorn return (ENXIO); 167255643Snwhitehorn} 168255643Snwhitehorn 169255643Snwhitehornstatic int 170255643Snwhitehornuart_phyp_probe(device_t dev) 171255643Snwhitehorn{ 172255643Snwhitehorn const char *name; 173255643Snwhitehorn struct uart_phyp_softc sc; 174255643Snwhitehorn int err; 175255643Snwhitehorn 176255643Snwhitehorn name = ofw_bus_get_name(dev); 177255643Snwhitehorn if (name == NULL || strcmp(name, "vty") != 0) 178255643Snwhitehorn return (ENXIO); 179255643Snwhitehorn 180255643Snwhitehorn sc.node = ofw_bus_get_node(dev); 181255643Snwhitehorn err = uart_phyp_probe_node(&sc); 182255643Snwhitehorn if (err != 0) 183255643Snwhitehorn return (err); 184255643Snwhitehorn 185255643Snwhitehorn device_set_desc(dev, "POWER Hypervisor Virtual Serial Port"); 186255643Snwhitehorn 187255643Snwhitehorn return (err); 188255643Snwhitehorn} 189255643Snwhitehorn 190255643Snwhitehornstatic void 191255643Snwhitehornuart_phyp_cnprobe(struct consdev *cp) 192255643Snwhitehorn{ 193255643Snwhitehorn char buf[64]; 194255643Snwhitehorn ihandle_t stdout; 195266019Sian phandle_t input, chosen; 196255643Snwhitehorn static struct uart_phyp_softc sc; 197255643Snwhitehorn 198255643Snwhitehorn if ((chosen = OF_finddevice("/chosen")) == -1) 199255643Snwhitehorn goto fail; 200255643Snwhitehorn 201255643Snwhitehorn /* Check if OF has an active stdin/stdout */ 202255643Snwhitehorn input = -1; 203255643Snwhitehorn if (OF_getprop(chosen, "stdout", &stdout, 204255643Snwhitehorn sizeof(stdout)) == sizeof(stdout) && stdout != 0) 205255643Snwhitehorn input = OF_instance_to_package(stdout); 206255643Snwhitehorn if (input == -1) 207255643Snwhitehorn goto fail; 208255643Snwhitehorn 209255643Snwhitehorn if (OF_getprop(input, "device_type", buf, sizeof(buf)) == -1) 210255643Snwhitehorn goto fail; 211255643Snwhitehorn if (strcmp(buf, "serial") != 0) 212255643Snwhitehorn goto fail; 213255643Snwhitehorn 214255643Snwhitehorn sc.node = input; 215255643Snwhitehorn if (uart_phyp_probe_node(&sc) != 0) 216255643Snwhitehorn goto fail; 217255643Snwhitehorn mtx_init(&sc.sc_mtx, "uart_phyp", NULL, MTX_SPIN | MTX_QUIET | 218255643Snwhitehorn MTX_NOWITNESS); 219255643Snwhitehorn 220255643Snwhitehorn cp->cn_pri = CN_NORMAL; 221255643Snwhitehorn console_sc = ≻ 222255643Snwhitehorn return; 223255643Snwhitehorn 224255643Snwhitehornfail: 225255643Snwhitehorn cp->cn_pri = CN_DEAD; 226255643Snwhitehorn return; 227255643Snwhitehorn} 228255643Snwhitehorn 229255643Snwhitehornstatic int 230255643Snwhitehornuart_phyp_attach(device_t dev) 231255643Snwhitehorn{ 232255643Snwhitehorn struct uart_phyp_softc *sc; 233255643Snwhitehorn int unit; 234255643Snwhitehorn 235255643Snwhitehorn sc = device_get_softc(dev); 236255643Snwhitehorn sc->dev = dev; 237255643Snwhitehorn sc->node = ofw_bus_get_node(dev); 238255643Snwhitehorn uart_phyp_probe_node(sc); 239255643Snwhitehorn 240255643Snwhitehorn unit = device_get_unit(dev); 241255643Snwhitehorn sc->tp = tty_alloc(&uart_phyp_tty_class, sc); 242255643Snwhitehorn mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, 243255643Snwhitehorn MTX_SPIN | MTX_QUIET | MTX_NOWITNESS); 244255643Snwhitehorn 245255643Snwhitehorn if (console_sc != NULL && console_sc->vtermid == sc->vtermid) { 246255643Snwhitehorn sc->outseqno = console_sc->outseqno; 247255643Snwhitehorn console_sc = sc; 248255643Snwhitehorn sprintf(uart_phyp_consdev.cn_name, "ttyu%r", unit); 249255643Snwhitehorn tty_init_console(sc->tp, 0); 250255643Snwhitehorn } 251255643Snwhitehorn 252255643Snwhitehorn sc->irqrid = 0; 253255643Snwhitehorn sc->irqres = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irqrid, 254255643Snwhitehorn RF_ACTIVE | RF_SHAREABLE); 255255643Snwhitehorn if (sc->irqres != NULL) { 256255643Snwhitehorn bus_setup_intr(dev, sc->irqres, INTR_TYPE_TTY | INTR_MPSAFE, 257255643Snwhitehorn NULL, uart_phyp_intr, sc, &sc->sc_icookie); 258255643Snwhitehorn } else { 259255643Snwhitehorn callout_init(&sc->callout, CALLOUT_MPSAFE); 260255643Snwhitehorn sc->polltime = hz / 20; 261255643Snwhitehorn if (sc->polltime < 1) 262255643Snwhitehorn sc->polltime = 1; 263255643Snwhitehorn callout_reset(&sc->callout, sc->polltime, uart_phyp_intr, sc); 264255643Snwhitehorn } 265255643Snwhitehorn 266255643Snwhitehorn tty_makedev(sc->tp, NULL, "u%r", unit); 267255643Snwhitehorn 268255643Snwhitehorn return (0); 269255643Snwhitehorn} 270255643Snwhitehorn 271255643Snwhitehornstatic void 272255643Snwhitehornuart_phyp_cninit(struct consdev *cp) 273255643Snwhitehorn{ 274255643Snwhitehorn 275255643Snwhitehorn strcpy(cp->cn_name, "phypcons"); 276255643Snwhitehorn} 277255643Snwhitehorn 278255643Snwhitehornstatic void 279255643Snwhitehornuart_phyp_cnterm(struct consdev *cp) 280255643Snwhitehorn{ 281255643Snwhitehorn} 282255643Snwhitehorn 283255643Snwhitehornstatic int 284255643Snwhitehornuart_phyp_get(struct uart_phyp_softc *sc, void *buffer, size_t bufsize) 285255643Snwhitehorn{ 286255643Snwhitehorn int err; 287259230Sandreast int hdr = 0; 288255643Snwhitehorn 289255643Snwhitehorn uart_lock(&sc->sc_mtx); 290255643Snwhitehorn if (sc->inbuflen == 0) { 291255643Snwhitehorn err = phyp_pft_hcall(H_GET_TERM_CHAR, sc->vtermid, 292255643Snwhitehorn 0, 0, 0, &sc->inbuflen, &sc->phyp_inbuf.u64[0], 293255643Snwhitehorn &sc->phyp_inbuf.u64[1]); 294255643Snwhitehorn if (err != H_SUCCESS) { 295255643Snwhitehorn uart_unlock(&sc->sc_mtx); 296255643Snwhitehorn return (-1); 297255643Snwhitehorn } 298259230Sandreast hdr = 1; 299255643Snwhitehorn } 300255643Snwhitehorn 301255643Snwhitehorn if (sc->inbuflen == 0) { 302255643Snwhitehorn uart_unlock(&sc->sc_mtx); 303255643Snwhitehorn return (0); 304255643Snwhitehorn } 305255643Snwhitehorn 306255643Snwhitehorn if (bufsize > sc->inbuflen) 307255643Snwhitehorn bufsize = sc->inbuflen; 308259230Sandreast 309259230Sandreast if ((sc->protocol == HVTERMPROT) && (hdr == 1)) { 310259230Sandreast sc->inbuflen = sc->inbuflen - 4; 311259230Sandreast /* The VTERM protocol has a 4 byte header, skip it here. */ 312259230Sandreast memmove(&sc->phyp_inbuf.str[0], &sc->phyp_inbuf.str[4], 313259230Sandreast sc->inbuflen); 314259230Sandreast } 315259230Sandreast 316255643Snwhitehorn memcpy(buffer, sc->phyp_inbuf.str, bufsize); 317255643Snwhitehorn sc->inbuflen -= bufsize; 318255643Snwhitehorn if (sc->inbuflen > 0) 319255643Snwhitehorn memmove(&sc->phyp_inbuf.str[0], &sc->phyp_inbuf.str[bufsize], 320255643Snwhitehorn sc->inbuflen); 321255643Snwhitehorn 322255643Snwhitehorn uart_unlock(&sc->sc_mtx); 323255643Snwhitehorn return (bufsize); 324255643Snwhitehorn} 325255643Snwhitehorn 326255643Snwhitehornstatic int 327255643Snwhitehornuart_phyp_put(struct uart_phyp_softc *sc, void *buffer, size_t bufsize) 328255643Snwhitehorn{ 329255643Snwhitehorn uint16_t seqno; 330255643Snwhitehorn uint64_t len = 0; 331259230Sandreast int err; 332259230Sandreast 333255643Snwhitehorn union { 334259230Sandreast uint64_t u64[2]; 335259230Sandreast char bytes[16]; 336255643Snwhitehorn } cbuf; 337255643Snwhitehorn 338255643Snwhitehorn uart_lock(&sc->sc_mtx); 339255643Snwhitehorn switch (sc->protocol) { 340255643Snwhitehorn case HVTERM1: 341259230Sandreast if (bufsize > 16) 342259230Sandreast bufsize = 16; 343255643Snwhitehorn memcpy(&cbuf, buffer, bufsize); 344255643Snwhitehorn len = bufsize; 345255643Snwhitehorn break; 346255643Snwhitehorn case HVTERMPROT: 347259230Sandreast if (bufsize > 12) 348259230Sandreast bufsize = 12; 349255643Snwhitehorn seqno = sc->outseqno++; 350255643Snwhitehorn cbuf.bytes[0] = VS_DATA_PACKET_HEADER; 351259230Sandreast cbuf.bytes[1] = 4 + bufsize; /* total length, max 16 bytes */ 352255643Snwhitehorn cbuf.bytes[2] = (seqno >> 8) & 0xff; 353255643Snwhitehorn cbuf.bytes[3] = seqno & 0xff; 354255643Snwhitehorn memcpy(&cbuf.bytes[4], buffer, bufsize); 355255643Snwhitehorn len = 4 + bufsize; 356255643Snwhitehorn break; 357255643Snwhitehorn } 358259230Sandreast 359259230Sandreast do { 360259230Sandreast err = phyp_hcall(H_PUT_TERM_CHAR, sc->vtermid, len, cbuf.u64[0], 361259230Sandreast cbuf.u64[1]); 362259230Sandreast DELAY(100); 363259230Sandreast } while (err == H_BUSY); 364259230Sandreast 365255643Snwhitehorn uart_unlock(&sc->sc_mtx); 366255643Snwhitehorn 367255643Snwhitehorn return (bufsize); 368255643Snwhitehorn} 369255643Snwhitehorn 370255643Snwhitehornstatic int 371255643Snwhitehornuart_phyp_cngetc(struct consdev *cp) 372255643Snwhitehorn{ 373255643Snwhitehorn unsigned char c; 374255643Snwhitehorn int retval; 375255643Snwhitehorn 376255643Snwhitehorn retval = uart_phyp_get(console_sc, &c, 1); 377255643Snwhitehorn if (retval != 1) 378255643Snwhitehorn return (-1); 379255643Snwhitehorn#if defined(KDB) 380255643Snwhitehorn kdb_alt_break(c, &alt_break_state); 381255643Snwhitehorn#endif 382255643Snwhitehorn 383255643Snwhitehorn return (c); 384255643Snwhitehorn} 385255643Snwhitehorn 386255643Snwhitehornstatic void 387255643Snwhitehornuart_phyp_cnputc(struct consdev *cp, int c) 388255643Snwhitehorn{ 389255643Snwhitehorn unsigned char ch = c; 390255643Snwhitehorn uart_phyp_put(console_sc, &ch, 1); 391255643Snwhitehorn} 392255643Snwhitehorn 393255643Snwhitehornstatic void 394255643Snwhitehornuart_phyp_cngrab(struct consdev *cp) 395255643Snwhitehorn{ 396255643Snwhitehorn} 397255643Snwhitehorn 398255643Snwhitehornstatic void 399255643Snwhitehornuart_phyp_cnungrab(struct consdev *cp) 400255643Snwhitehorn{ 401255643Snwhitehorn} 402255643Snwhitehorn 403255643Snwhitehornstatic void 404255643Snwhitehornuart_phyp_ttyoutwakeup(struct tty *tp) 405255643Snwhitehorn{ 406255643Snwhitehorn struct uart_phyp_softc *sc; 407255643Snwhitehorn char buffer[8]; 408255643Snwhitehorn int len; 409255643Snwhitehorn 410255643Snwhitehorn sc = tty_softc(tp); 411255643Snwhitehorn 412255643Snwhitehorn while ((len = ttydisc_getc(tp, buffer, sizeof(buffer))) != 0) 413255643Snwhitehorn uart_phyp_put(sc, buffer, len); 414255643Snwhitehorn} 415255643Snwhitehorn 416255643Snwhitehornstatic void 417255643Snwhitehornuart_phyp_intr(void *v) 418255643Snwhitehorn{ 419255643Snwhitehorn struct uart_phyp_softc *sc = v; 420255643Snwhitehorn struct tty *tp = sc->tp; 421255643Snwhitehorn unsigned char c; 422255643Snwhitehorn int len; 423255643Snwhitehorn 424255643Snwhitehorn tty_lock(tp); 425255643Snwhitehorn while ((len = uart_phyp_get(sc, &c, 1)) > 0) 426255643Snwhitehorn ttydisc_rint(tp, c, 0); 427255643Snwhitehorn ttydisc_rint_done(tp); 428255643Snwhitehorn tty_unlock(tp); 429255643Snwhitehorn 430255643Snwhitehorn if (sc->irqres == NULL) 431255643Snwhitehorn callout_reset(&sc->callout, sc->polltime, uart_phyp_intr, sc); 432255643Snwhitehorn} 433255643Snwhitehorn 434