1 1.10 thorpej /* $NetBSD: ewsms.c,v 1.10 2021/08/07 16:18:53 thorpej Exp $ */ 2 1.1 tsutsui 3 1.1 tsutsui /* 4 1.1 tsutsui * Copyright (c) 2004 Steve Rumble 5 1.1 tsutsui * All rights reserved. 6 1.1 tsutsui * 7 1.1 tsutsui * Redistribution and use in source and binary forms, with or without 8 1.1 tsutsui * modification, are permitted provided that the following conditions 9 1.1 tsutsui * are met: 10 1.1 tsutsui * 1. Redistributions of source code must retain the above copyright 11 1.1 tsutsui * notice, this list of conditions and the following disclaimer. 12 1.1 tsutsui * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 tsutsui * notice, this list of conditions and the following disclaimer in the 14 1.1 tsutsui * documentation and/or other materials provided with the distribution. 15 1.1 tsutsui * 3. The name of the author may not be used to endorse or promote products 16 1.1 tsutsui * derived from this software without specific prior written permission. 17 1.1 tsutsui * 18 1.1 tsutsui * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 1.1 tsutsui * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 1.1 tsutsui * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 1.1 tsutsui * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 1.1 tsutsui * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 1.1 tsutsui * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 1.1 tsutsui * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 1.1 tsutsui * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 1.1 tsutsui * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 1.1 tsutsui * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 1.1 tsutsui */ 29 1.1 tsutsui 30 1.1 tsutsui /* 31 1.1 tsutsui * EWS4800 serial mouse driver attached to zs channel 0 at 4800 or 1200bps. 32 1.1 tsutsui * This layer feeds wsmouse. 33 1.1 tsutsui * 34 1.1 tsutsui * 5 byte packets: sync, x1, y1, x2, y2 35 1.1 tsutsui * sync format: binary 10000LMR (left, middle, right) 0 is down 36 1.1 tsutsui * 37 1.1 tsutsui * This driver is taken from sgimips, but ews4800 has the similar protocol. 38 1.1 tsutsui */ 39 1.1 tsutsui 40 1.1 tsutsui #include <sys/cdefs.h> 41 1.10 thorpej __KERNEL_RCSID(0, "$NetBSD: ewsms.c,v 1.10 2021/08/07 16:18:53 thorpej Exp $"); 42 1.1 tsutsui 43 1.1 tsutsui #include <sys/param.h> 44 1.1 tsutsui #include <sys/systm.h> 45 1.1 tsutsui #include <sys/conf.h> 46 1.1 tsutsui #include <sys/device.h> 47 1.1 tsutsui 48 1.1 tsutsui #include <dev/wscons/wsconsio.h> 49 1.1 tsutsui #include <dev/wscons/wsmousevar.h> 50 1.1 tsutsui 51 1.1 tsutsui #include <dev/ic/z8530reg.h> 52 1.1 tsutsui #include <machine/z8530var.h> 53 1.1 tsutsui 54 1.1 tsutsui #define EWSMS_BAUD 1200 55 1.1 tsutsui #define EWSMS_BAUD1 4800 56 1.1 tsutsui 57 1.1 tsutsui #define EWSMS_RXQ_LEN 64 /* power of 2 */ 58 1.1 tsutsui #define EWSMS_RXQ_LEN_MASK (EWSMS_RXQ_LEN - 1) 59 1.1 tsutsui #define EWSMS_NEXTRXQ(x) (((x) + 1) & EWSMS_RXQ_LEN_MASK) 60 1.1 tsutsui 61 1.1 tsutsui /* protocol */ 62 1.1 tsutsui #define EWSMS_SYNC0 0x80 63 1.1 tsutsui #define EWSMS_SYNC1 0x88 64 1.1 tsutsui #define EWSMS_SYNC_MASK 0xf8 65 1.1 tsutsui #define EWSMS_SYNC_BTN_R 0x01 66 1.1 tsutsui #define EWSMS_SYNC_BTN_M 0x02 67 1.1 tsutsui #define EWSMS_SYNC_BTN_L 0x04 68 1.1 tsutsui #define EWSMS_SYNC_BTN_MASK \ 69 1.1 tsutsui (EWSMS_SYNC_BTN_R|EWSMS_SYNC_BTN_M|EWSMS_SYNC_BTN_L) 70 1.1 tsutsui 71 1.1 tsutsui struct ewsms_softc { 72 1.5 tsutsui device_t sc_dev; 73 1.1 tsutsui 74 1.1 tsutsui /* tail-chasing fifo */ 75 1.1 tsutsui uint8_t sc_rxq[EWSMS_RXQ_LEN]; 76 1.1 tsutsui u_int sc_rxq_head; 77 1.1 tsutsui u_int sc_rxq_tail; 78 1.1 tsutsui 79 1.1 tsutsui /* 5-byte packet as described above */ 80 1.1 tsutsui int8_t sc_packet[5]; 81 1.1 tsutsui #define EWSMS_PACKET_SYNC 0 82 1.1 tsutsui #define EWSMS_PACKET_X1 1 83 1.1 tsutsui #define EWSMS_PACKET_Y1 2 84 1.1 tsutsui #define EWSMS_PACKET_X2 3 85 1.1 tsutsui #define EWSMS_PACKET_Y2 4 86 1.1 tsutsui 87 1.1 tsutsui u_int sc_state; 88 1.1 tsutsui #define EWSMS_STATE_SYNC 0x01 89 1.1 tsutsui #define EWSMS_STATE_X1 0x02 90 1.1 tsutsui #define EWSMS_STATE_Y1 0x04 91 1.1 tsutsui #define EWSMS_STATE_X2 0x08 92 1.1 tsutsui #define EWSMS_STATE_Y2 0x10 93 1.1 tsutsui 94 1.1 tsutsui u_int sc_baud; 95 1.1 tsutsui u_int sc_flags; 96 1.1 tsutsui #define EWSMS_F_FASTBAUD 0x01 97 1.1 tsutsui #define EWSMS_F_RSTCMD 0x02 98 1.1 tsutsui #define EWSMS_F_SWAPBTN 0x04 99 1.1 tsutsui #define EWSMS_F_SYNC1 0x08 100 1.1 tsutsui 101 1.1 tsutsui /* wsmouse bits */ 102 1.1 tsutsui int sc_enabled; 103 1.8 tsutsui device_t sc_wsmousedev; 104 1.1 tsutsui }; 105 1.1 tsutsui 106 1.5 tsutsui static int ewsms_zsc_match(device_t, cfdata_t, void *); 107 1.5 tsutsui static void ewsms_zsc_attach(device_t, device_t, void *); 108 1.1 tsutsui static int ewsms_zsc_reset(struct zs_chanstate *); 109 1.1 tsutsui static void ewsms_zsc_rxint(struct zs_chanstate *); 110 1.1 tsutsui static void ewsms_zsc_txint(struct zs_chanstate *); 111 1.1 tsutsui static void ewsms_zsc_stint(struct zs_chanstate *, int); 112 1.1 tsutsui static void ewsms_zsc_softint(struct zs_chanstate *); 113 1.1 tsutsui 114 1.1 tsutsui static void ewsms_wsmouse_input(struct ewsms_softc *); 115 1.1 tsutsui static int ewsms_wsmouse_enable(void *); 116 1.1 tsutsui static void ewsms_wsmouse_disable(void *); 117 1.4 christos static int ewsms_wsmouse_ioctl(void *, u_long, void *, int, struct lwp *); 118 1.1 tsutsui 119 1.5 tsutsui CFATTACH_DECL_NEW(ewsms_zsc, sizeof(struct ewsms_softc), 120 1.1 tsutsui ewsms_zsc_match, ewsms_zsc_attach, NULL, NULL); 121 1.1 tsutsui 122 1.1 tsutsui static struct zsops ewsms_zsops = { 123 1.1 tsutsui ewsms_zsc_rxint, 124 1.1 tsutsui ewsms_zsc_stint, 125 1.1 tsutsui ewsms_zsc_txint, 126 1.1 tsutsui ewsms_zsc_softint 127 1.1 tsutsui }; 128 1.1 tsutsui 129 1.1 tsutsui static const struct wsmouse_accessops ewsms_wsmouse_accessops = { 130 1.1 tsutsui ewsms_wsmouse_enable, 131 1.1 tsutsui ewsms_wsmouse_ioctl, 132 1.1 tsutsui ewsms_wsmouse_disable 133 1.1 tsutsui }; 134 1.1 tsutsui 135 1.1 tsutsui int 136 1.5 tsutsui ewsms_zsc_match(device_t parent, cfdata_t cf, void *aux) 137 1.1 tsutsui { 138 1.6 tsutsui struct zsc_softc *zsc = device_private(parent); 139 1.1 tsutsui struct zsc_attach_args *zsc_args = aux; 140 1.1 tsutsui 141 1.1 tsutsui /* mouse on channel A */ 142 1.1 tsutsui if ((zsc->zsc_flags & 0x0001 /* kbms port */) != 0 && 143 1.1 tsutsui zsc_args->channel == 0) 144 1.1 tsutsui /* prior to generic zstty(4) */ 145 1.1 tsutsui return 3; 146 1.1 tsutsui 147 1.1 tsutsui return 0; 148 1.1 tsutsui } 149 1.1 tsutsui 150 1.1 tsutsui void 151 1.5 tsutsui ewsms_zsc_attach(device_t parent, device_t self, void *aux) 152 1.1 tsutsui { 153 1.5 tsutsui struct ewsms_softc *sc = device_private(self); 154 1.6 tsutsui struct zsc_softc *zsc = device_private(parent); 155 1.1 tsutsui struct zsc_attach_args *zsc_args = aux; 156 1.1 tsutsui struct zs_chanstate *cs; 157 1.1 tsutsui struct wsmousedev_attach_args wsmaa; 158 1.1 tsutsui int channel; 159 1.1 tsutsui 160 1.1 tsutsui /* Establish ourself with the MD z8530 driver */ 161 1.1 tsutsui channel = zsc_args->channel; 162 1.1 tsutsui cs = zsc->zsc_cs[channel]; 163 1.1 tsutsui cs->cs_ops = &ewsms_zsops; 164 1.1 tsutsui cs->cs_private = sc; 165 1.1 tsutsui 166 1.5 tsutsui sc->sc_dev = self; 167 1.1 tsutsui sc->sc_enabled = 0; 168 1.1 tsutsui sc->sc_rxq_head = 0; 169 1.1 tsutsui sc->sc_rxq_tail = 0; 170 1.1 tsutsui sc->sc_state = EWSMS_STATE_SYNC; 171 1.1 tsutsui 172 1.1 tsutsui if (zsc->zsc_flags & 0x0002) { 173 1.1 tsutsui sc->sc_flags |= EWSMS_F_RSTCMD; 174 1.1 tsutsui sc->sc_flags |= EWSMS_F_FASTBAUD; 175 1.1 tsutsui sc->sc_flags |= EWSMS_F_SWAPBTN; 176 1.1 tsutsui sc->sc_flags |= EWSMS_F_SYNC1; 177 1.1 tsutsui } 178 1.1 tsutsui 179 1.1 tsutsui ewsms_zsc_reset(cs); 180 1.1 tsutsui 181 1.7 tsutsui aprint_normal(": baud rate %d\n", EWSMS_BAUD); 182 1.1 tsutsui 183 1.1 tsutsui /* attach wsmouse */ 184 1.1 tsutsui wsmaa.accessops = &ewsms_wsmouse_accessops; 185 1.1 tsutsui wsmaa.accesscookie = sc; 186 1.9 thorpej sc->sc_wsmousedev = config_found(self, &wsmaa, wsmousedevprint, 187 1.10 thorpej CFARGS_NONE); 188 1.1 tsutsui } 189 1.1 tsutsui 190 1.1 tsutsui int 191 1.1 tsutsui ewsms_zsc_reset(struct zs_chanstate *cs) 192 1.1 tsutsui { 193 1.1 tsutsui struct ewsms_softc *sc = cs->cs_private; 194 1.1 tsutsui u_int baud; 195 1.1 tsutsui int i, s; 196 1.7 tsutsui static const uint8_t cmd[] = 197 1.1 tsutsui { 0x02, 0x52, 0x53, 0x3b, 0x4d, 0x54, 0x2c, 0x36, 0x0d }; 198 1.1 tsutsui 199 1.1 tsutsui s = splzs(); 200 1.1 tsutsui zs_write_reg(cs, 9, ZSWR9_A_RESET); 201 1.1 tsutsui cs->cs_preg[1] = ZSWR1_RIE; 202 1.1 tsutsui baud = EWSMS_BAUD; 203 1.1 tsutsui if (sc->sc_flags & EWSMS_F_FASTBAUD) 204 1.1 tsutsui baud = EWSMS_BAUD1; 205 1.1 tsutsui zs_set_speed(cs, baud); 206 1.1 tsutsui zs_loadchannelregs(cs); 207 1.1 tsutsui 208 1.1 tsutsui if (sc->sc_flags & EWSMS_F_RSTCMD) { 209 1.1 tsutsui for (i = 0; i < sizeof(cmd); i++) 210 1.1 tsutsui zs_putc(cs, cmd[i]); 211 1.1 tsutsui } 212 1.1 tsutsui 213 1.1 tsutsui splx(s); 214 1.1 tsutsui 215 1.1 tsutsui return 0; 216 1.1 tsutsui } 217 1.1 tsutsui 218 1.1 tsutsui void 219 1.1 tsutsui ewsms_zsc_rxint(struct zs_chanstate *cs) 220 1.1 tsutsui { 221 1.1 tsutsui struct ewsms_softc *sc = cs->cs_private; 222 1.7 tsutsui uint8_t c, r; 223 1.1 tsutsui 224 1.1 tsutsui /* clear errors */ 225 1.1 tsutsui r = zs_read_reg(cs, 1); 226 1.1 tsutsui if (r & (ZSRR1_FE | ZSRR1_DO | ZSRR1_PE)) 227 1.1 tsutsui zs_write_csr(cs, ZSWR0_RESET_ERRORS); 228 1.1 tsutsui 229 1.1 tsutsui /* read byte and append to our queue */ 230 1.1 tsutsui c = zs_read_data(cs); 231 1.1 tsutsui 232 1.1 tsutsui sc->sc_rxq[sc->sc_rxq_tail] = c; 233 1.1 tsutsui sc->sc_rxq_tail = EWSMS_NEXTRXQ(sc->sc_rxq_tail); 234 1.1 tsutsui 235 1.1 tsutsui cs->cs_softreq = 1; 236 1.1 tsutsui } 237 1.1 tsutsui 238 1.1 tsutsui /* We should never get here. */ 239 1.1 tsutsui void 240 1.1 tsutsui ewsms_zsc_txint(struct zs_chanstate *cs) 241 1.1 tsutsui { 242 1.7 tsutsui 243 1.1 tsutsui zs_write_reg(cs, 0, ZSWR0_RESET_TXINT); 244 1.1 tsutsui 245 1.1 tsutsui /* seems like the in thing to do */ 246 1.1 tsutsui cs->cs_softreq = 1; 247 1.1 tsutsui } 248 1.1 tsutsui 249 1.1 tsutsui void 250 1.1 tsutsui ewsms_zsc_stint(struct zs_chanstate *cs, int force) 251 1.1 tsutsui { 252 1.1 tsutsui 253 1.1 tsutsui zs_write_csr(cs, ZSWR0_RESET_STATUS); 254 1.1 tsutsui cs->cs_softreq = 1; 255 1.1 tsutsui } 256 1.1 tsutsui 257 1.1 tsutsui void 258 1.1 tsutsui ewsms_zsc_softint(struct zs_chanstate *cs) 259 1.1 tsutsui { 260 1.1 tsutsui struct ewsms_softc *sc = cs->cs_private; 261 1.1 tsutsui uint8_t sync; 262 1.1 tsutsui 263 1.1 tsutsui /* No need to keep score if nobody is listening */ 264 1.1 tsutsui if (!sc->sc_enabled) { 265 1.1 tsutsui sc->sc_rxq_head = sc->sc_rxq_tail; 266 1.1 tsutsui return; 267 1.1 tsutsui } 268 1.1 tsutsui 269 1.1 tsutsui /* 270 1.1 tsutsui * Here's the real action. Read a full packet and 271 1.1 tsutsui * then let wsmouse know what has happened. 272 1.1 tsutsui */ 273 1.1 tsutsui while (sc->sc_rxq_head != sc->sc_rxq_tail) { 274 1.1 tsutsui int8_t c = sc->sc_rxq[sc->sc_rxq_head]; 275 1.1 tsutsui 276 1.1 tsutsui switch (sc->sc_state) { 277 1.1 tsutsui case EWSMS_STATE_SYNC: 278 1.1 tsutsui sync = EWSMS_SYNC0; 279 1.1 tsutsui if (sc->sc_flags & EWSMS_F_SYNC1) 280 1.1 tsutsui sync = EWSMS_SYNC1; 281 1.1 tsutsui if ((c & EWSMS_SYNC_MASK) == sync) { 282 1.1 tsutsui sc->sc_packet[EWSMS_PACKET_SYNC] = c; 283 1.1 tsutsui sc->sc_state = EWSMS_STATE_X1; 284 1.1 tsutsui } 285 1.1 tsutsui break; 286 1.1 tsutsui 287 1.1 tsutsui case EWSMS_STATE_X1: 288 1.1 tsutsui sc->sc_packet[EWSMS_PACKET_X1] = c; 289 1.1 tsutsui sc->sc_state = EWSMS_STATE_Y1; 290 1.1 tsutsui break; 291 1.1 tsutsui 292 1.1 tsutsui case EWSMS_STATE_Y1: 293 1.1 tsutsui sc->sc_packet[EWSMS_PACKET_Y1] = c; 294 1.1 tsutsui sc->sc_state = EWSMS_STATE_X2; 295 1.1 tsutsui break; 296 1.1 tsutsui 297 1.1 tsutsui case EWSMS_STATE_X2: 298 1.1 tsutsui sc->sc_packet[EWSMS_PACKET_X2] = c; 299 1.1 tsutsui sc->sc_state = EWSMS_STATE_Y2; 300 1.1 tsutsui break; 301 1.1 tsutsui 302 1.1 tsutsui case EWSMS_STATE_Y2: 303 1.1 tsutsui sc->sc_packet[EWSMS_PACKET_Y2] = c; 304 1.1 tsutsui 305 1.1 tsutsui /* tweak wsmouse */ 306 1.1 tsutsui ewsms_wsmouse_input(sc); 307 1.1 tsutsui 308 1.1 tsutsui sc->sc_state = EWSMS_STATE_SYNC; 309 1.1 tsutsui } 310 1.1 tsutsui 311 1.1 tsutsui sc->sc_rxq_head = EWSMS_NEXTRXQ(sc->sc_rxq_head); 312 1.1 tsutsui } 313 1.1 tsutsui } 314 1.1 tsutsui 315 1.1 tsutsui /****************************************************************************** 316 1.1 tsutsui * wsmouse glue 317 1.1 tsutsui ******************************************************************************/ 318 1.1 tsutsui 319 1.1 tsutsui static void 320 1.1 tsutsui ewsms_wsmouse_input(struct ewsms_softc *sc) 321 1.1 tsutsui { 322 1.1 tsutsui u_int btns; 323 1.3 thorpej bool bl, bm, br; 324 1.1 tsutsui int dx, dy; 325 1.1 tsutsui 326 1.1 tsutsui btns = (uint8_t)sc->sc_packet[EWSMS_PACKET_SYNC] & EWSMS_SYNC_BTN_MASK; 327 1.1 tsutsui if (sc->sc_flags & EWSMS_F_SWAPBTN) { 328 1.1 tsutsui bl = (btns & EWSMS_SYNC_BTN_R) == 0; 329 1.1 tsutsui bm = (btns & EWSMS_SYNC_BTN_M) == 0; 330 1.1 tsutsui br = (btns & EWSMS_SYNC_BTN_L) == 0; 331 1.1 tsutsui } else { 332 1.1 tsutsui bl = (btns & EWSMS_SYNC_BTN_L) == 0; 333 1.1 tsutsui bm = (btns & EWSMS_SYNC_BTN_M) == 0; 334 1.1 tsutsui br = (btns & EWSMS_SYNC_BTN_R) == 0; 335 1.1 tsutsui } 336 1.1 tsutsui /* for wsmouse(4), 1 is down, 0 is up, most left button is LSB */ 337 1.1 tsutsui btns = (bl ? (1 << 0) : 0) | (bm ? (1 << 1) : 0) | (br ? (1 << 2) : 0); 338 1.1 tsutsui 339 1.1 tsutsui /* delta values are signed */ 340 1.1 tsutsui /* moving left is minus, moving right is plus */ 341 1.1 tsutsui dx = (int)sc->sc_packet[EWSMS_PACKET_X1] + 342 1.1 tsutsui (int)sc->sc_packet[EWSMS_PACKET_X2]; 343 1.1 tsutsui /* moving back is minus, moving front is plus */ 344 1.1 tsutsui dy = (int)sc->sc_packet[EWSMS_PACKET_Y1] + 345 1.1 tsutsui (int)sc->sc_packet[EWSMS_PACKET_Y2]; 346 1.1 tsutsui 347 1.2 plunky wsmouse_input(sc->sc_wsmousedev, 348 1.2 plunky btns, 349 1.2 plunky dx, dy, 0, 0, 350 1.2 plunky WSMOUSE_INPUT_DELTA); 351 1.1 tsutsui } 352 1.1 tsutsui 353 1.1 tsutsui static int 354 1.1 tsutsui ewsms_wsmouse_enable(void *cookie) 355 1.1 tsutsui { 356 1.1 tsutsui struct ewsms_softc *sc = cookie; 357 1.1 tsutsui 358 1.1 tsutsui if (sc->sc_enabled) 359 1.1 tsutsui return EBUSY; 360 1.1 tsutsui 361 1.1 tsutsui sc->sc_state = EWSMS_STATE_SYNC; 362 1.1 tsutsui sc->sc_enabled = 1; 363 1.1 tsutsui 364 1.1 tsutsui return 0; 365 1.1 tsutsui } 366 1.1 tsutsui 367 1.1 tsutsui void 368 1.1 tsutsui ewsms_wsmouse_disable(void *cookie) 369 1.1 tsutsui { 370 1.1 tsutsui struct ewsms_softc *sc = cookie; 371 1.1 tsutsui 372 1.1 tsutsui sc->sc_enabled = 0; 373 1.1 tsutsui } 374 1.1 tsutsui 375 1.1 tsutsui static int 376 1.4 christos ewsms_wsmouse_ioctl(void *cookie, u_long cmd, void *data, int flag, 377 1.1 tsutsui struct lwp *l) 378 1.1 tsutsui { 379 1.1 tsutsui 380 1.1 tsutsui switch (cmd) { 381 1.1 tsutsui case WSMOUSEIO_GTYPE: 382 1.1 tsutsui *(u_int *)data = 0; 383 1.1 tsutsui break; 384 1.1 tsutsui 385 1.1 tsutsui #ifdef notyet 386 1.1 tsutsui case WSMOUSEIO_SRES: 387 1.1 tsutsui case WSMOUSEIO_SSCALE: 388 1.1 tsutsui case WSMOUSEIO_SRATE: 389 1.1 tsutsui case WSMOUSEIO_SCALIBCOORDS: 390 1.1 tsutsui case WSMOUSEIO_GCALIBCOORDS: 391 1.1 tsutsui case WSMOUSEIO_GETID: 392 1.1 tsutsui #endif 393 1.1 tsutsui 394 1.1 tsutsui default: 395 1.1 tsutsui return EPASSTHROUGH; 396 1.1 tsutsui } 397 1.1 tsutsui 398 1.1 tsutsui return 0; 399 1.1 tsutsui } 400