1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <sys/stat.h> /* ddi_create_minor_node S_IFCHR */ 30 #include <sys/modctl.h> /* for modldrv */ 31 #include <sys/open.h> /* for open params. */ 32 #include <sys/types.h> 33 #include <sys/kmem.h> 34 #include <sys/sunddi.h> 35 #include <sys/conf.h> /* req. by dev_ops flags MTSAFE etc. */ 36 #include <sys/ddi.h> 37 #include <sys/file.h> 38 #include <sys/note.h> 39 #include <sys/i2c/clients/i2c_gpio.h> 40 #include <sys/i2c/clients/adm1026_impl.h> 41 42 /* 43 * This driver supports the GPIO subset of the full ADM1026 register set. 44 * The driver is designed to allow modifying and reading the Polarity and 45 * Direction bits of the ADM1026's 16 GPIO pins via the 4 GPIO Config 46 * registers. In addition, the driver supports modifying and reading 47 * the 16 GPIO pins via the 2 GPIO input/output registers. 48 * 49 * The 4 GPIO Config registers configure the direction and polarity of 50 * the 16 GPIO pins. When a Polarity bit is set to 0, the GPIO pin is 51 * active low, otherwise, it is active high. When a Direction bit is set 52 * to 0, the GPIO pin configured as an input; otherwise, it is an output. 53 * 54 * The 2 GPIO input/output registers (Status Register 5 & 6 ) behave as follows. 55 * When a GPIO pin is configured as an input, the bit is set when its GPIO 56 * pin is asserted. When a GPIO pin is configured as an output, the bit 57 * asserts the GPIO pin. 58 * 59 * The commands supported in the ioctl routine are: 60 * GPIO_GET_OUTPUT -- Read GPIO0-GPIO15 bits in Status Register 5 & 6 61 * GPIO_SET_OUTPUT -- Modify GPIO0-GPIO15 bits in Status Register 5 & 6 62 * GPIO_GET_POLARITY -- Read GPIO0-GPIO15 Polarity bits in GPIO Config 1-4 63 * GPIO_SET_POLARITY -- Modify GPIO0-GPIO15 Polarity bits in GPIO Config 1-4 64 * GPIO_GET_CONFIG -- Read GPIO0-GPIO15 Direction bits in GPIO Config 1-4 65 * GPIO_SET_CONFIG -- Modify GPIO0-GPIO15 Direction bits in GPIO Config 1-4 66 * 67 * A pointer to the i2c_gpio_t data structure is sent as the third argument 68 * in the ioctl call. The reg_mask and reg_val members of i2c_gpio_t are 69 * used to logically represent the 16 GPIO pins, thus only the lower 16 bits 70 * of each member is used. The reg_mask member identifies the GPIO pin(s) 71 * that the user wants to read or modify and reg_val has the actual value of 72 * what the corresponding GPIO pin should be set to. 73 * 74 * For example, a reg_mask of 0x8001 indicates that the ioctl should only 75 * access GPIO15 and GPIO0. 76 */ 77 78 static void *adm1026soft_statep; 79 80 static int adm1026_do_attach(dev_info_t *); 81 static int adm1026_do_detach(dev_info_t *); 82 static int adm1026_do_resume(void); 83 static int adm1026_do_suspend(void); 84 static int adm1026_get8(adm1026_unit_t *unitp, uint8_t reg, uint8_t *val); 85 static int adm1026_put8(adm1026_unit_t *unitp, uint8_t reg, uint8_t val); 86 87 /* 88 * cb ops (only need ioctl) 89 */ 90 static int adm1026_open(dev_t *, int, int, cred_t *); 91 static int adm1026_close(dev_t, int, int, cred_t *); 92 static int adm1026_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 93 94 static struct cb_ops adm1026_cbops = { 95 adm1026_open, /* open */ 96 adm1026_close, /* close */ 97 nodev, /* strategy */ 98 nodev, /* print */ 99 nodev, /* dump */ 100 nodev, /* read */ 101 nodev, /* write */ 102 adm1026_ioctl, /* ioctl */ 103 nodev, /* devmap */ 104 nodev, /* mmap */ 105 nodev, /* segmap */ 106 nochpoll, /* poll */ 107 ddi_prop_op, /* cb_prop_op */ 108 NULL, /* streamtab */ 109 D_NEW | D_MP | D_HOTPLUG, /* Driver compatibility flag */ 110 CB_REV, /* rev */ 111 nodev, /* int (*cb_aread)() */ 112 nodev /* int (*cb_awrite)() */ 113 }; 114 115 /* 116 * dev ops 117 */ 118 static int adm1026_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); 119 static int adm1026_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); 120 121 static struct dev_ops adm1026_ops = { 122 DEVO_REV, 123 0, 124 ddi_getinfo_1to1, 125 nulldev, 126 nulldev, 127 adm1026_attach, 128 adm1026_detach, 129 nodev, 130 &adm1026_cbops, 131 NULL 132 }; 133 134 extern struct mod_ops mod_driverops; 135 136 static struct modldrv adm1026_modldrv = { 137 &mod_driverops, /* type of module - driver */ 138 "ADM1026 i2c device driver: %I%", 139 &adm1026_ops 140 }; 141 142 static struct modlinkage adm1026_modlinkage = { 143 MODREV_1, 144 &adm1026_modldrv, 145 0 146 }; 147 148 149 int 150 _init(void) 151 { 152 int error; 153 154 error = mod_install(&adm1026_modlinkage); 155 156 if (!error) 157 (void) ddi_soft_state_init(&adm1026soft_statep, 158 sizeof (struct adm1026_unit), 1); 159 return (error); 160 } 161 162 int 163 _fini(void) 164 { 165 int error; 166 167 error = mod_remove(&adm1026_modlinkage); 168 if (!error) 169 ddi_soft_state_fini(&adm1026soft_statep); 170 171 return (error); 172 } 173 174 int 175 _info(struct modinfo *modinfop) 176 { 177 return (mod_info(&adm1026_modlinkage, modinfop)); 178 } 179 180 static int 181 adm1026_open(dev_t *devp, int flags, int otyp, cred_t *credp) 182 { 183 _NOTE(ARGUNUSED(credp)) 184 185 adm1026_unit_t *unitp; 186 int instance; 187 int error = 0; 188 189 instance = getminor(*devp); 190 191 D2CMN_ERR((CE_WARN, "adm1026_open: instance=%d\n", instance)); 192 193 if (instance < 0) { 194 return (ENXIO); 195 } 196 197 unitp = (struct adm1026_unit *) 198 ddi_get_soft_state(adm1026soft_statep, instance); 199 200 if (unitp == NULL) { 201 return (ENXIO); 202 } 203 204 if (otyp != OTYP_CHR) { 205 return (EINVAL); 206 } 207 208 mutex_enter(&unitp->adm1026_mutex); 209 210 if (flags & FEXCL) { 211 if (unitp->adm1026_oflag != 0) { 212 error = EBUSY; 213 } else { 214 unitp->adm1026_oflag = FEXCL; 215 } 216 } else { 217 if (unitp->adm1026_oflag == FEXCL) { 218 error = EBUSY; 219 } else { 220 unitp->adm1026_oflag = FOPEN; 221 } 222 } 223 224 mutex_exit(&unitp->adm1026_mutex); 225 226 return (error); 227 } 228 229 static int 230 adm1026_close(dev_t dev, int flags, int otyp, cred_t *credp) 231 { 232 _NOTE(ARGUNUSED(flags, otyp, credp)) 233 234 adm1026_unit_t *unitp; 235 int instance; 236 237 instance = getminor(dev); 238 239 D2CMN_ERR((CE_WARN, "adm1026_close: instance=%d\n", instance)); 240 241 if (instance < 0) { 242 return (ENXIO); 243 } 244 245 unitp = (struct adm1026_unit *) 246 ddi_get_soft_state(adm1026soft_statep, instance); 247 248 if (unitp == NULL) { 249 return (ENXIO); 250 } 251 252 mutex_enter(&unitp->adm1026_mutex); 253 254 unitp->adm1026_oflag = 0; 255 256 mutex_exit(&unitp->adm1026_mutex); 257 return (DDI_SUCCESS); 258 } 259 260 static int 261 adm1026_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 262 { 263 D2CMN_ERR((CE_WARN, "adm1026_attach: cmd=%x\n", cmd)); 264 265 switch (cmd) { 266 case DDI_ATTACH: 267 return (adm1026_do_attach(dip)); 268 case DDI_RESUME: 269 return (adm1026_do_resume()); 270 default: 271 return (DDI_FAILURE); 272 } 273 } 274 275 static int 276 adm1026_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 277 { 278 D2CMN_ERR((CE_WARN, "adm1026_detach: cmd=%x\n", cmd)); 279 switch (cmd) { 280 case DDI_DETACH: 281 return (adm1026_do_detach(dip)); 282 case DDI_SUSPEND: 283 return (adm1026_do_suspend()); 284 default: 285 return (DDI_FAILURE); 286 } 287 } 288 289 static int 290 adm1026_do_attach(dev_info_t *dip) 291 { 292 adm1026_unit_t *unitp; 293 int instance; 294 295 instance = ddi_get_instance(dip); 296 297 D2CMN_ERR((CE_WARN, "adm1026_do_attach: instance=%d, dip=%p", 298 instance, (void *)dip)); 299 300 if (ddi_soft_state_zalloc(adm1026soft_statep, instance) != 0) { 301 cmn_err(CE_WARN, "%s%d: ddi_soft_state_zalloc() failed", 302 ddi_get_name(dip), instance); 303 return (DDI_FAILURE); 304 } 305 306 unitp = ddi_get_soft_state(adm1026soft_statep, instance); 307 308 if (unitp == NULL) { 309 cmn_err(CE_WARN, "%s%d: ddi_get_soft_state(), no memory", 310 ddi_get_name(dip), instance); 311 return (ENOMEM); 312 } 313 314 D2CMN_ERR((CE_WARN, "adm1026_do_attach: ddi_create_minor_node")); 315 if (ddi_create_minor_node(dip, "adm1026", S_IFCHR, instance, 316 "ddi_i2c:led_control", NULL) == DDI_FAILURE) { 317 cmn_err(CE_WARN, 318 "adm1026_do_attach: ddi_create_minor_node failed"); 319 ddi_soft_state_free(adm1026soft_statep, instance); 320 321 return (DDI_FAILURE); 322 } 323 324 D2CMN_ERR((CE_WARN, "adm1026_do_attach: i2c_client_register")); 325 if (i2c_client_register(dip, &unitp->adm1026_hdl) != I2C_SUCCESS) { 326 ddi_remove_minor_node(dip, NULL); 327 ddi_soft_state_free(adm1026soft_statep, instance); 328 cmn_err(CE_WARN, 329 "adm1026_do_attach: i2c_client_register failed"); 330 331 return (DDI_FAILURE); 332 } 333 334 mutex_init(&unitp->adm1026_mutex, NULL, MUTEX_DRIVER, NULL); 335 336 D2CMN_ERR((CE_WARN, "adm1026_do_attach: DDI_SUCCESS")); 337 return (DDI_SUCCESS); 338 } 339 340 static int 341 adm1026_do_resume(void) 342 { 343 int ret = DDI_SUCCESS; 344 345 return (ret); 346 } 347 348 static int 349 adm1026_do_suspend() 350 { 351 int ret = DDI_SUCCESS; 352 353 return (ret); 354 } 355 356 static int 357 adm1026_do_detach(dev_info_t *dip) 358 { 359 adm1026_unit_t *unitp; 360 int instance; 361 362 instance = ddi_get_instance(dip); 363 364 unitp = ddi_get_soft_state(adm1026soft_statep, instance); 365 366 if (unitp == NULL) { 367 cmn_err(CE_WARN, 368 "adm1026_do_detach: ddi_get_soft_state failed"); 369 return (ENOMEM); 370 } 371 372 i2c_client_unregister(unitp->adm1026_hdl); 373 374 ddi_remove_minor_node(dip, NULL); 375 376 mutex_destroy(&unitp->adm1026_mutex); 377 ddi_soft_state_free(adm1026soft_statep, instance); 378 379 return (DDI_SUCCESS); 380 } 381 382 static int 383 adm1026_get8(adm1026_unit_t *unitp, uint8_t reg, uint8_t *val) 384 { 385 i2c_transfer_t *i2c_tran_pointer = NULL; 386 int err = DDI_SUCCESS; 387 388 (void) i2c_transfer_alloc(unitp->adm1026_hdl, &i2c_tran_pointer, 389 1, 1, I2C_SLEEP); 390 if (i2c_tran_pointer == NULL) 391 return (ENOMEM); 392 393 i2c_tran_pointer->i2c_flags = I2C_WR_RD; 394 i2c_tran_pointer->i2c_wbuf[0] = (uchar_t)reg; 395 err = i2c_transfer(unitp->adm1026_hdl, i2c_tran_pointer); 396 if (err) { 397 D1CMN_ERR((CE_WARN, 398 "adm1026_get8: I2C_WR_RD reg=0x%x failed", reg)); 399 } else { 400 *val = i2c_tran_pointer->i2c_rbuf[0]; 401 D1CMN_ERR((CE_WARN, "adm1026_get8: reg=%02x, val=%02x", 402 reg, *val)); 403 } 404 i2c_transfer_free(unitp->adm1026_hdl, i2c_tran_pointer); 405 406 return (err); 407 } 408 409 static int 410 adm1026_put8(adm1026_unit_t *unitp, uint8_t reg, uint8_t val) 411 { 412 i2c_transfer_t *i2c_tran_pointer = NULL; 413 int err = DDI_SUCCESS; 414 415 D1CMN_ERR((CE_WARN, "adm1026_put8: reg=%02x, val=%02x\n", reg, val)); 416 417 (void) i2c_transfer_alloc(unitp->adm1026_hdl, &i2c_tran_pointer, 418 2, 0, I2C_SLEEP); 419 if (i2c_tran_pointer == NULL) 420 return (ENOMEM); 421 422 i2c_tran_pointer->i2c_flags = I2C_WR; 423 i2c_tran_pointer->i2c_wbuf[0] = reg; 424 i2c_tran_pointer->i2c_wbuf[1] = val; 425 426 err = i2c_transfer(unitp->adm1026_hdl, i2c_tran_pointer); 427 if (err) 428 D2CMN_ERR((CE_WARN, "adm1026_put8: return=%x", err)); 429 430 i2c_transfer_free(unitp->adm1026_hdl, i2c_tran_pointer); 431 432 return (err); 433 } 434 435 /* 436 * adm1026_send8: 437 * Read the i2c register, apply the mask to contents so that only 438 * bits in mask affected. Or in value and write it back to the i2c register. 439 */ 440 static int 441 adm1026_send8(adm1026_unit_t *unitp, uint8_t reg, uint8_t reg_val, 442 uint8_t reg_mask) 443 { 444 uint8_t val = 0; 445 int err; 446 447 if ((err = adm1026_get8(unitp, reg, &val)) != I2C_SUCCESS) 448 return (err); 449 val &= ~reg_mask; 450 val |= (reg_val & reg_mask); 451 452 return (adm1026_put8(unitp, reg, val)); 453 } 454 455 /* 456 * adm1026_set_output: 457 * The low 16 bits of the mask is a 1:1 mask indicating which of the 458 * 16 GPIO pin(s) to set. 459 */ 460 static int 461 adm1026_set_output(adm1026_unit_t *unitp, uint32_t val, uint32_t mask) 462 { 463 int err = I2C_SUCCESS; 464 465 if (mask & 0xff) 466 err = adm1026_send8(unitp, ADM1026_STS_REG5, (uint8_t)val, 467 (uint8_t)mask); 468 469 if ((err == I2C_SUCCESS) && (mask & 0xff00)) 470 err = adm1026_send8(unitp, ADM1026_STS_REG6, 471 (uint8_t)(val >> OUTPUT_SHIFT), 472 (uint8_t)(mask >> OUTPUT_SHIFT)); 473 474 return (err); 475 } 476 477 /* 478 * adm1026_get_output: 479 * The low 16 bits of the mask is a 1:1 mask indicating which of the 480 * 16 GPIO pin(s) to get. 481 */ 482 static int 483 adm1026_get_output(adm1026_unit_t *unitp, uint32_t mask, uint32_t *val) 484 { 485 uint8_t reg_val = 0; 486 int err = I2C_SUCCESS; 487 488 if (mask & 0xff) { 489 err = adm1026_get8(unitp, ADM1026_STS_REG5, ®_val); 490 if (err != I2C_SUCCESS) 491 return (err); 492 493 *val = reg_val; 494 } 495 496 if (mask & 0xff00) { 497 err = adm1026_get8(unitp, ADM1026_STS_REG6, ®_val); 498 if (err != I2C_SUCCESS) 499 return (err); 500 501 *val |= ((reg_val << OUTPUT_SHIFT) & (mask & 0xff00)); 502 } 503 504 return (err); 505 } 506 507 /* 508 * adm1026_set_config: 509 * The low 16 bits of the mask is a 1:1 mask indicating which of the 510 * 16 GPIO pin(s) to set the polarity or direction configuration for. 511 * Each GPIO pin has 2 bits of configuration - 1 polarity bit and 1 512 * direction bit. Traverse the mask 4 bits at a time to determine 513 * which of the 4 GPIO Config registers to access and apply the value 514 * based on whether cmd is GPIO_SET_CONFIG (set Direction) or 515 * GPIO_SET_POLARITY. 516 */ 517 static int 518 adm1026_set_config(adm1026_unit_t *unitp, int cmd, uint32_t val, uint32_t mask) 519 { 520 int i; 521 uint8_t r; 522 uint32_t m = mask, v = val; 523 int err = I2C_SUCCESS; 524 525 for (i = 0, r = ADM1026_GPIO_CFG1; i < BYTES_PER_CONFIG; i++, r++) { 526 if (m & GPIO_CFG_MASK) { 527 int j; 528 uint8_t mm = 0, vv = 0; 529 uint8_t bit = (cmd == GPIO_SET_CONFIG) ? 530 DIR_BIT : POLARITY_BIT; 531 532 for (j = 0; j < GPIOS_PER_CFG_BYTE; j++) { 533 if (m & (1 << j)) { 534 mm |= (bit << (j * BITSPERCFG)); 535 } 536 if (v & (1 << j)) { 537 vv |= (bit << (j * BITSPERCFG)); 538 } 539 } 540 D2CMN_ERR((CE_WARN, "adm1026_set_config: r=%02x, " 541 "vv=%02x, mm=%02x, m=%02x", r, vv, mm, m)); 542 err = adm1026_send8(unitp, r, vv, mm); 543 if (err != I2C_SUCCESS) 544 return (err); 545 } 546 m >>= GPIOS_PER_CFG_BYTE; 547 v >>= GPIOS_PER_CFG_BYTE; 548 } 549 return (err); 550 } 551 552 /* 553 * adm1026_get_config: 554 * The low 16 bits of the mask is a 1:1 mask indicating which of the 555 * 16 GPIO pin(s) to get the polarity or direction configuration for. 556 * Each GPIO pin has 2 bits of configuration - 1 polarity bit and 1 557 * direction bit. Traverse the mask 4 bits at a time to determine 558 * which of the 4 GPIO Config registers to access and build the return 559 * value based on whether cmd is GPIO_GET_CONFIG (get Direction) or 560 * GPIO_GET_POLARITY. 561 */ 562 static int 563 adm1026_get_config(adm1026_unit_t *unitp, int cmd, uint32_t mask, uint32_t *val) 564 { 565 int i, j; 566 uint8_t r; 567 int err = I2C_SUCCESS; 568 569 *val = 0; 570 571 for (i = 0, r = ADM1026_GPIO_CFG1; i < BYTES_PER_CONFIG; i++, r++) { 572 if (mask & GPIO_CFG_MASK) { 573 uint8_t newval = 0, x; 574 uint8_t bit = (cmd == GPIO_GET_CONFIG) ? 575 DIR_BIT : POLARITY_BIT; 576 577 err = adm1026_get8(unitp, r, &x); 578 if (err != I2C_SUCCESS) 579 return (err); 580 for (j = 0; j < GPIOS_PER_CFG_BYTE; j++) { 581 if (mask & (1 << j)) { 582 if (x & (bit << (j * BITSPERCFG))) 583 newval |= (1 << j); 584 } 585 } 586 *val |= (newval << (i * GPIOS_PER_CFG_BYTE)); 587 } else 588 *val <<= GPIOS_PER_CFG_BYTE; 589 590 mask >>= GPIOS_PER_CFG_BYTE; 591 } 592 return (err); 593 } 594 595 static int 596 adm1026_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, 597 int *rvalp) 598 { 599 _NOTE(ARGUNUSED(credp, rvalp)) 600 601 adm1026_unit_t *unitp; 602 int instance; 603 int err = DDI_SUCCESS; 604 i2c_gpio_t g_buf; 605 606 instance = getminor(dev); 607 608 D2CMN_ERR((CE_WARN, "adm1026_ioctl: instance=%d, cmd=%x\n", 609 instance, cmd)); 610 611 unitp = (struct adm1026_unit *) 612 ddi_get_soft_state(adm1026soft_statep, instance); 613 614 if (unitp == NULL) { 615 cmn_err(CE_WARN, "adm1026_ioctl: ddi_get_soft_state failed"); 616 err = ENOMEM; 617 return (err); 618 } 619 620 mutex_enter(&unitp->adm1026_mutex); 621 622 if (ddi_copyin((caddr_t)arg, &g_buf, 623 sizeof (i2c_gpio_t), mode) != DDI_SUCCESS) { 624 625 mutex_exit(&unitp->adm1026_mutex); 626 return (EFAULT); 627 } 628 if (g_buf.reg_mask & 0xffff0000) { 629 cmn_err(CE_WARN, 630 "adm1026_ioctl: reg_mask too large. " 631 "Only bits 15-0 supported"); 632 mutex_exit(&unitp->adm1026_mutex); 633 return (EINVAL); 634 } 635 switch (cmd) { 636 case GPIO_SET_OUTPUT: 637 err = adm1026_set_output(unitp, g_buf.reg_val, g_buf.reg_mask); 638 break; 639 640 case GPIO_GET_OUTPUT: 641 err = adm1026_get_output(unitp, g_buf.reg_mask, &g_buf.reg_val); 642 if (err == DDI_SUCCESS) 643 err = ddi_copyout(&g_buf, (caddr_t)arg, 644 sizeof (i2c_gpio_t), mode); 645 break; 646 647 case GPIO_SET_CONFIG: 648 case GPIO_SET_POLARITY: 649 err = adm1026_set_config(unitp, cmd, g_buf.reg_val, 650 g_buf.reg_mask); 651 break; 652 653 case GPIO_GET_CONFIG: 654 case GPIO_GET_POLARITY: 655 err = adm1026_get_config(unitp, cmd, g_buf.reg_mask, 656 &g_buf.reg_val); 657 if (err == DDI_SUCCESS) 658 err = ddi_copyout(&g_buf, (caddr_t)arg, 659 sizeof (i2c_gpio_t), mode); 660 break; 661 default: 662 D2CMN_ERR((CE_WARN, 663 "adm1026_ioctl: Invalid ioctl cmd %x\n", cmd)); 664 err = EINVAL; 665 } 666 mutex_exit(&unitp->adm1026_mutex); 667 668 if (err) { 669 D2CMN_ERR((CE_WARN, 670 "adm1026_ioctl: failed, err=%x\n", err)); 671 if (err == DDI_FAILURE) 672 err = EIO; 673 } 674 675 return (err); 676 } 677