1 #include "bcm47xx_private.h" 2 3 #include <linux/input.h> 4 #include <linux/gpio_keys.h> 5 #include <linux/interrupt.h> 6 #include <bcm47xx_board.h> 7 #include <bcm47xx.h> 8 9 /************************************************** 10 * Database 11 **************************************************/ 12 13 #define BCM47XX_GPIO_KEY(_gpio, _code) \ 14 { \ 15 .code = _code, \ 16 .gpio = _gpio, \ 17 .active_low = 1, \ 18 } 19 20 /* Asus */ 21 22 static const struct gpio_keys_button 23 bcm47xx_buttons_asus_rtn12[] __initconst = { 24 BCM47XX_GPIO_KEY(0, KEY_WPS_BUTTON), 25 BCM47XX_GPIO_KEY(1, KEY_RESTART), 26 BCM47XX_GPIO_KEY(4, BTN_0), /* Router mode */ 27 BCM47XX_GPIO_KEY(5, BTN_1), /* Repeater mode */ 28 BCM47XX_GPIO_KEY(6, BTN_2), /* AP mode */ 29 }; 30 31 static const struct gpio_keys_button 32 bcm47xx_buttons_asus_rtn16[] __initconst = { 33 BCM47XX_GPIO_KEY(6, KEY_WPS_BUTTON), 34 BCM47XX_GPIO_KEY(8, KEY_RESTART), 35 }; 36 37 static const struct gpio_keys_button 38 bcm47xx_buttons_asus_rtn66u[] __initconst = { 39 BCM47XX_GPIO_KEY(4, KEY_WPS_BUTTON), 40 BCM47XX_GPIO_KEY(9, KEY_RESTART), 41 }; 42 43 static const struct gpio_keys_button 44 bcm47xx_buttons_asus_wl300g[] __initconst = { 45 BCM47XX_GPIO_KEY(6, KEY_RESTART), 46 }; 47 48 static const struct gpio_keys_button 49 bcm47xx_buttons_asus_wl320ge[] __initconst = { 50 BCM47XX_GPIO_KEY(6, KEY_RESTART), 51 }; 52 53 static const struct gpio_keys_button 54 bcm47xx_buttons_asus_wl330ge[] __initconst = { 55 BCM47XX_GPIO_KEY(2, KEY_RESTART), 56 }; 57 58 static const struct gpio_keys_button 59 bcm47xx_buttons_asus_wl500gd[] __initconst = { 60 BCM47XX_GPIO_KEY(6, KEY_RESTART), 61 }; 62 63 static const struct gpio_keys_button 64 bcm47xx_buttons_asus_wl500gpv1[] __initconst = { 65 BCM47XX_GPIO_KEY(0, KEY_RESTART), 66 BCM47XX_GPIO_KEY(4, KEY_WPS_BUTTON), 67 }; 68 69 static const struct gpio_keys_button 70 bcm47xx_buttons_asus_wl500gpv2[] __initconst = { 71 BCM47XX_GPIO_KEY(2, KEY_RESTART), 72 BCM47XX_GPIO_KEY(3, KEY_WPS_BUTTON), 73 }; 74 75 static const struct gpio_keys_button 76 bcm47xx_buttons_asus_wl500w[] __initconst = { 77 BCM47XX_GPIO_KEY(6, KEY_RESTART), 78 BCM47XX_GPIO_KEY(7, KEY_WPS_BUTTON), 79 }; 80 81 static const struct gpio_keys_button 82 bcm47xx_buttons_asus_wl520gc[] __initconst = { 83 BCM47XX_GPIO_KEY(2, KEY_RESTART), 84 BCM47XX_GPIO_KEY(3, KEY_WPS_BUTTON), 85 }; 86 87 static const struct gpio_keys_button 88 bcm47xx_buttons_asus_wl520gu[] __initconst = { 89 BCM47XX_GPIO_KEY(2, KEY_RESTART), 90 BCM47XX_GPIO_KEY(3, KEY_WPS_BUTTON), 91 }; 92 93 static const struct gpio_keys_button 94 bcm47xx_buttons_asus_wl700ge[] __initconst = { 95 BCM47XX_GPIO_KEY(0, KEY_POWER), /* Hard disk power switch */ 96 BCM47XX_GPIO_KEY(4, KEY_WPS_BUTTON), /* EZSetup */ 97 BCM47XX_GPIO_KEY(6, KEY_COPY), /* Copy data from USB to internal disk */ 98 BCM47XX_GPIO_KEY(7, KEY_RESTART), /* Hard reset */ 99 }; 100 101 static const struct gpio_keys_button 102 bcm47xx_buttons_asus_wlhdd[] __initconst = { 103 BCM47XX_GPIO_KEY(6, KEY_RESTART), 104 }; 105 106 /* Huawei */ 107 108 static const struct gpio_keys_button 109 bcm47xx_buttons_huawei_e970[] __initconst = { 110 BCM47XX_GPIO_KEY(6, KEY_RESTART), 111 }; 112 113 /* Belkin */ 114 115 static const struct gpio_keys_button 116 bcm47xx_buttons_belkin_f7d4301[] __initconst = { 117 BCM47XX_GPIO_KEY(6, KEY_RESTART), 118 BCM47XX_GPIO_KEY(8, KEY_WPS_BUTTON), 119 }; 120 121 /* Buffalo */ 122 123 static const struct gpio_keys_button 124 bcm47xx_buttons_buffalo_whr2_a54g54[] __initconst = { 125 BCM47XX_GPIO_KEY(4, KEY_RESTART), 126 }; 127 128 static const struct gpio_keys_button 129 bcm47xx_buttons_buffalo_whr_g125[] __initconst = { 130 BCM47XX_GPIO_KEY(0, KEY_WPS_BUTTON), 131 BCM47XX_GPIO_KEY(4, KEY_RESTART), 132 BCM47XX_GPIO_KEY(5, BTN_0), /* Router / AP mode swtich */ 133 }; 134 135 static const struct gpio_keys_button 136 bcm47xx_buttons_buffalo_whr_g54s[] __initconst = { 137 BCM47XX_GPIO_KEY(0, KEY_WPS_BUTTON), 138 BCM47XX_GPIO_KEY(4, KEY_RESTART), 139 BCM47XX_GPIO_KEY(5, BTN_0), /* Router / AP mode swtich */ 140 }; 141 142 static const struct gpio_keys_button 143 bcm47xx_buttons_buffalo_whr_hp_g54[] __initconst = { 144 BCM47XX_GPIO_KEY(0, KEY_WPS_BUTTON), 145 BCM47XX_GPIO_KEY(4, KEY_RESTART), 146 BCM47XX_GPIO_KEY(5, BTN_0), /* Router / AP mode swtich */ 147 }; 148 149 static const struct gpio_keys_button 150 bcm47xx_buttons_buffalo_wzr_g300n[] __initconst = { 151 BCM47XX_GPIO_KEY(4, KEY_RESTART), 152 }; 153 154 static const struct gpio_keys_button 155 bcm47xx_buttons_buffalo_wzr_rs_g54[] __initconst = { 156 BCM47XX_GPIO_KEY(0, KEY_WPS_BUTTON), 157 BCM47XX_GPIO_KEY(4, KEY_RESTART), 158 }; 159 160 static const struct gpio_keys_button 161 bcm47xx_buttons_buffalo_wzr_rs_g54hp[] __initconst = { 162 BCM47XX_GPIO_KEY(0, KEY_WPS_BUTTON), 163 BCM47XX_GPIO_KEY(4, KEY_RESTART), 164 }; 165 166 /* Dell */ 167 168 static const struct gpio_keys_button 169 bcm47xx_buttons_dell_tm2300[] __initconst = { 170 BCM47XX_GPIO_KEY(0, KEY_RESTART), 171 }; 172 173 /* D-Link */ 174 175 static const struct gpio_keys_button 176 bcm47xx_buttons_dlink_dir130[] __initconst = { 177 BCM47XX_GPIO_KEY(3, KEY_RESTART), 178 BCM47XX_GPIO_KEY(7, KEY_UNKNOWN), 179 }; 180 181 static const struct gpio_keys_button 182 bcm47xx_buttons_dlink_dir330[] __initconst = { 183 BCM47XX_GPIO_KEY(3, KEY_RESTART), 184 BCM47XX_GPIO_KEY(7, KEY_UNKNOWN), 185 }; 186 187 /* Linksys */ 188 189 static const struct gpio_keys_button 190 bcm47xx_buttons_linksys_e1000v1[] __initconst = { 191 BCM47XX_GPIO_KEY(5, KEY_WPS_BUTTON), 192 BCM47XX_GPIO_KEY(6, KEY_RESTART), 193 }; 194 195 static const struct gpio_keys_button 196 bcm47xx_buttons_linksys_e1000v21[] __initconst = { 197 BCM47XX_GPIO_KEY(9, KEY_WPS_BUTTON), 198 BCM47XX_GPIO_KEY(10, KEY_RESTART), 199 }; 200 201 static const struct gpio_keys_button 202 bcm47xx_buttons_linksys_e2000v1[] __initconst = { 203 BCM47XX_GPIO_KEY(5, KEY_WPS_BUTTON), 204 BCM47XX_GPIO_KEY(8, KEY_RESTART), 205 }; 206 207 static const struct gpio_keys_button 208 bcm47xx_buttons_linksys_e3000v1[] __initconst = { 209 BCM47XX_GPIO_KEY(4, KEY_WPS_BUTTON), 210 BCM47XX_GPIO_KEY(6, KEY_RESTART), 211 }; 212 213 static const struct gpio_keys_button 214 bcm47xx_buttons_linksys_e3200v1[] __initconst = { 215 BCM47XX_GPIO_KEY(5, KEY_RESTART), 216 BCM47XX_GPIO_KEY(8, KEY_WPS_BUTTON), 217 }; 218 219 static const struct gpio_keys_button 220 bcm47xx_buttons_linksys_e4200v1[] __initconst = { 221 BCM47XX_GPIO_KEY(4, KEY_WPS_BUTTON), 222 BCM47XX_GPIO_KEY(6, KEY_RESTART), 223 }; 224 225 static const struct gpio_keys_button 226 bcm47xx_buttons_linksys_wrt150nv1[] __initconst = { 227 BCM47XX_GPIO_KEY(4, KEY_WPS_BUTTON), 228 BCM47XX_GPIO_KEY(6, KEY_RESTART), 229 }; 230 231 static const struct gpio_keys_button 232 bcm47xx_buttons_linksys_wrt150nv11[] __initconst = { 233 BCM47XX_GPIO_KEY(4, KEY_WPS_BUTTON), 234 BCM47XX_GPIO_KEY(6, KEY_RESTART), 235 }; 236 237 static const struct gpio_keys_button 238 bcm47xx_buttons_linksys_wrt160nv1[] __initconst = { 239 BCM47XX_GPIO_KEY(4, KEY_WPS_BUTTON), 240 BCM47XX_GPIO_KEY(6, KEY_RESTART), 241 }; 242 243 static const struct gpio_keys_button 244 bcm47xx_buttons_linksys_wrt160nv3[] __initconst = { 245 BCM47XX_GPIO_KEY(5, KEY_WPS_BUTTON), 246 BCM47XX_GPIO_KEY(6, KEY_RESTART), 247 }; 248 249 static const struct gpio_keys_button 250 bcm47xx_buttons_linksys_wrt300nv11[] __initconst = { 251 BCM47XX_GPIO_KEY(4, KEY_UNKNOWN), 252 BCM47XX_GPIO_KEY(6, KEY_RESTART), 253 }; 254 255 static const struct gpio_keys_button 256 bcm47xx_buttons_linksys_wrt310nv1[] __initconst = { 257 BCM47XX_GPIO_KEY(6, KEY_RESTART), 258 BCM47XX_GPIO_KEY(8, KEY_UNKNOWN), 259 }; 260 261 static const struct gpio_keys_button 262 bcm47xx_buttons_linksys_wrt610nv1[] __initconst = { 263 BCM47XX_GPIO_KEY(6, KEY_RESTART), 264 BCM47XX_GPIO_KEY(8, KEY_WPS_BUTTON), 265 }; 266 267 static const struct gpio_keys_button 268 bcm47xx_buttons_linksys_wrt610nv2[] __initconst = { 269 BCM47XX_GPIO_KEY(4, KEY_WPS_BUTTON), 270 BCM47XX_GPIO_KEY(6, KEY_RESTART), 271 }; 272 273 /* Motorola */ 274 275 static const struct gpio_keys_button 276 bcm47xx_buttons_motorola_we800g[] __initconst = { 277 BCM47XX_GPIO_KEY(0, KEY_RESTART), 278 }; 279 280 static const struct gpio_keys_button 281 bcm47xx_buttons_motorola_wr850gp[] __initconst = { 282 BCM47XX_GPIO_KEY(5, KEY_RESTART), 283 }; 284 285 static const struct gpio_keys_button 286 bcm47xx_buttons_motorola_wr850gv2v3[] __initconst = { 287 BCM47XX_GPIO_KEY(5, KEY_RESTART), 288 }; 289 290 /* Netgear */ 291 292 static const struct gpio_keys_button 293 bcm47xx_buttons_netgear_wndr3400v1[] __initconst = { 294 BCM47XX_GPIO_KEY(4, KEY_RESTART), 295 BCM47XX_GPIO_KEY(6, KEY_WPS_BUTTON), 296 BCM47XX_GPIO_KEY(8, KEY_RFKILL), 297 }; 298 299 static const struct gpio_keys_button 300 bcm47xx_buttons_netgear_wndr3700v3[] __initconst = { 301 BCM47XX_GPIO_KEY(2, KEY_RFKILL), 302 BCM47XX_GPIO_KEY(3, KEY_RESTART), 303 BCM47XX_GPIO_KEY(4, KEY_WPS_BUTTON), 304 }; 305 306 static const struct gpio_keys_button 307 bcm47xx_buttons_netgear_wndr4500v1[] __initconst = { 308 BCM47XX_GPIO_KEY(4, KEY_WPS_BUTTON), 309 BCM47XX_GPIO_KEY(5, KEY_RFKILL), 310 BCM47XX_GPIO_KEY(6, KEY_RESTART), 311 }; 312 313 static const struct gpio_keys_button 314 bcm47xx_buttons_netgear_wnr834bv2[] __initconst = { 315 BCM47XX_GPIO_KEY(6, KEY_RESTART), 316 }; 317 318 /* SimpleTech */ 319 320 static const struct gpio_keys_button 321 bcm47xx_buttons_simpletech_simpleshare[] __initconst = { 322 BCM47XX_GPIO_KEY(0, KEY_RESTART), 323 }; 324 325 /************************************************** 326 * Init 327 **************************************************/ 328 329 static struct gpio_keys_platform_data bcm47xx_button_pdata; 330 331 static struct platform_device bcm47xx_buttons_gpio_keys = { 332 .name = "gpio-keys", 333 .dev = { 334 .platform_data = &bcm47xx_button_pdata, 335 } 336 }; 337 338 /* Copy data from __initconst */ 339 static int __init bcm47xx_buttons_copy(const struct gpio_keys_button *buttons, 340 size_t nbuttons) 341 { 342 size_t size = nbuttons * sizeof(*buttons); 343 344 bcm47xx_button_pdata.buttons = kmalloc(size, GFP_KERNEL); 345 if (!bcm47xx_button_pdata.buttons) 346 return -ENOMEM; 347 memcpy(bcm47xx_button_pdata.buttons, buttons, size); 348 bcm47xx_button_pdata.nbuttons = nbuttons; 349 350 return 0; 351 } 352 353 #define bcm47xx_copy_bdata(dev_buttons) \ 354 bcm47xx_buttons_copy(dev_buttons, ARRAY_SIZE(dev_buttons)); 355 356 int __init bcm47xx_buttons_register(void) 357 { 358 enum bcm47xx_board board = bcm47xx_board_get(); 359 int err; 360 361 switch (board) { 362 case BCM47XX_BOARD_ASUS_RTN12: 363 err = bcm47xx_copy_bdata(bcm47xx_buttons_asus_rtn12); 364 break; 365 case BCM47XX_BOARD_ASUS_RTN16: 366 err = bcm47xx_copy_bdata(bcm47xx_buttons_asus_rtn16); 367 break; 368 case BCM47XX_BOARD_ASUS_RTN66U: 369 err = bcm47xx_copy_bdata(bcm47xx_buttons_asus_rtn66u); 370 break; 371 case BCM47XX_BOARD_ASUS_WL300G: 372 err = bcm47xx_copy_bdata(bcm47xx_buttons_asus_wl300g); 373 break; 374 case BCM47XX_BOARD_ASUS_WL320GE: 375 err = bcm47xx_copy_bdata(bcm47xx_buttons_asus_wl320ge); 376 break; 377 case BCM47XX_BOARD_ASUS_WL330GE: 378 err = bcm47xx_copy_bdata(bcm47xx_buttons_asus_wl330ge); 379 break; 380 case BCM47XX_BOARD_ASUS_WL500GD: 381 err = bcm47xx_copy_bdata(bcm47xx_buttons_asus_wl500gd); 382 break; 383 case BCM47XX_BOARD_ASUS_WL500GPV1: 384 err = bcm47xx_copy_bdata(bcm47xx_buttons_asus_wl500gpv1); 385 break; 386 case BCM47XX_BOARD_ASUS_WL500GPV2: 387 err = bcm47xx_copy_bdata(bcm47xx_buttons_asus_wl500gpv2); 388 break; 389 case BCM47XX_BOARD_ASUS_WL500W: 390 err = bcm47xx_copy_bdata(bcm47xx_buttons_asus_wl500w); 391 break; 392 case BCM47XX_BOARD_ASUS_WL520GC: 393 err = bcm47xx_copy_bdata(bcm47xx_buttons_asus_wl520gc); 394 break; 395 case BCM47XX_BOARD_ASUS_WL520GU: 396 err = bcm47xx_copy_bdata(bcm47xx_buttons_asus_wl520gu); 397 break; 398 case BCM47XX_BOARD_ASUS_WL700GE: 399 err = bcm47xx_copy_bdata(bcm47xx_buttons_asus_wl700ge); 400 break; 401 case BCM47XX_BOARD_ASUS_WLHDD: 402 err = bcm47xx_copy_bdata(bcm47xx_buttons_asus_wlhdd); 403 break; 404 405 case BCM47XX_BOARD_BELKIN_F7D4301: 406 err = bcm47xx_copy_bdata(bcm47xx_buttons_belkin_f7d4301); 407 break; 408 409 case BCM47XX_BOARD_BUFFALO_WHR2_A54G54: 410 err = bcm47xx_copy_bdata(bcm47xx_buttons_buffalo_whr2_a54g54); 411 break; 412 case BCM47XX_BOARD_BUFFALO_WHR_G125: 413 err = bcm47xx_copy_bdata(bcm47xx_buttons_buffalo_whr_g125); 414 break; 415 case BCM47XX_BOARD_BUFFALO_WHR_G54S: 416 err = bcm47xx_copy_bdata(bcm47xx_buttons_buffalo_whr_g54s); 417 break; 418 case BCM47XX_BOARD_BUFFALO_WHR_HP_G54: 419 err = bcm47xx_copy_bdata(bcm47xx_buttons_buffalo_whr_hp_g54); 420 break; 421 case BCM47XX_BOARD_BUFFALO_WZR_G300N: 422 err = bcm47xx_copy_bdata(bcm47xx_buttons_buffalo_wzr_g300n); 423 break; 424 case BCM47XX_BOARD_BUFFALO_WZR_RS_G54: 425 err = bcm47xx_copy_bdata(bcm47xx_buttons_buffalo_wzr_rs_g54); 426 break; 427 case BCM47XX_BOARD_BUFFALO_WZR_RS_G54HP: 428 err = bcm47xx_copy_bdata(bcm47xx_buttons_buffalo_wzr_rs_g54hp); 429 break; 430 431 case BCM47XX_BOARD_DELL_TM2300: 432 err = bcm47xx_copy_bdata(bcm47xx_buttons_dell_tm2300); 433 break; 434 435 case BCM47XX_BOARD_DLINK_DIR130: 436 err = bcm47xx_copy_bdata(bcm47xx_buttons_dlink_dir130); 437 break; 438 case BCM47XX_BOARD_DLINK_DIR330: 439 err = bcm47xx_copy_bdata(bcm47xx_buttons_dlink_dir330); 440 break; 441 442 case BCM47XX_BOARD_HUAWEI_E970: 443 err = bcm47xx_copy_bdata(bcm47xx_buttons_huawei_e970); 444 break; 445 446 case BCM47XX_BOARD_LINKSYS_E1000V1: 447 err = bcm47xx_copy_bdata(bcm47xx_buttons_linksys_e1000v1); 448 break; 449 case BCM47XX_BOARD_LINKSYS_E1000V21: 450 err = bcm47xx_copy_bdata(bcm47xx_buttons_linksys_e1000v21); 451 break; 452 case BCM47XX_BOARD_LINKSYS_E2000V1: 453 err = bcm47xx_copy_bdata(bcm47xx_buttons_linksys_e2000v1); 454 break; 455 case BCM47XX_BOARD_LINKSYS_E3000V1: 456 err = bcm47xx_copy_bdata(bcm47xx_buttons_linksys_e3000v1); 457 break; 458 case BCM47XX_BOARD_LINKSYS_E3200V1: 459 err = bcm47xx_copy_bdata(bcm47xx_buttons_linksys_e3200v1); 460 break; 461 case BCM47XX_BOARD_LINKSYS_E4200V1: 462 err = bcm47xx_copy_bdata(bcm47xx_buttons_linksys_e4200v1); 463 break; 464 case BCM47XX_BOARD_LINKSYS_WRT150NV1: 465 err = bcm47xx_copy_bdata(bcm47xx_buttons_linksys_wrt150nv1); 466 break; 467 case BCM47XX_BOARD_LINKSYS_WRT150NV11: 468 err = bcm47xx_copy_bdata(bcm47xx_buttons_linksys_wrt150nv11); 469 break; 470 case BCM47XX_BOARD_LINKSYS_WRT160NV1: 471 err = bcm47xx_copy_bdata(bcm47xx_buttons_linksys_wrt160nv1); 472 break; 473 case BCM47XX_BOARD_LINKSYS_WRT160NV3: 474 err = bcm47xx_copy_bdata(bcm47xx_buttons_linksys_wrt160nv3); 475 break; 476 case BCM47XX_BOARD_LINKSYS_WRT300NV11: 477 err = bcm47xx_copy_bdata(bcm47xx_buttons_linksys_wrt300nv11); 478 break; 479 case BCM47XX_BOARD_LINKSYS_WRT310NV1: 480 err = bcm47xx_copy_bdata(bcm47xx_buttons_linksys_wrt310nv1); 481 break; 482 case BCM47XX_BOARD_LINKSYS_WRT610NV1: 483 err = bcm47xx_copy_bdata(bcm47xx_buttons_linksys_wrt610nv1); 484 break; 485 case BCM47XX_BOARD_LINKSYS_WRT610NV2: 486 err = bcm47xx_copy_bdata(bcm47xx_buttons_linksys_wrt610nv2); 487 break; 488 489 case BCM47XX_BOARD_MOTOROLA_WE800G: 490 err = bcm47xx_copy_bdata(bcm47xx_buttons_motorola_we800g); 491 break; 492 case BCM47XX_BOARD_MOTOROLA_WR850GP: 493 err = bcm47xx_copy_bdata(bcm47xx_buttons_motorola_wr850gp); 494 break; 495 case BCM47XX_BOARD_MOTOROLA_WR850GV2V3: 496 err = bcm47xx_copy_bdata(bcm47xx_buttons_motorola_wr850gv2v3); 497 break; 498 499 case BCM47XX_BOARD_NETGEAR_WNDR3400V1: 500 err = bcm47xx_copy_bdata(bcm47xx_buttons_netgear_wndr3400v1); 501 break; 502 case BCM47XX_BOARD_NETGEAR_WNDR3700V3: 503 err = bcm47xx_copy_bdata(bcm47xx_buttons_netgear_wndr3700v3); 504 break; 505 case BCM47XX_BOARD_NETGEAR_WNDR4500V1: 506 err = bcm47xx_copy_bdata(bcm47xx_buttons_netgear_wndr4500v1); 507 break; 508 case BCM47XX_BOARD_NETGEAR_WNR834BV2: 509 err = bcm47xx_copy_bdata(bcm47xx_buttons_netgear_wnr834bv2); 510 break; 511 512 case BCM47XX_BOARD_SIMPLETECH_SIMPLESHARE: 513 err = bcm47xx_copy_bdata(bcm47xx_buttons_simpletech_simpleshare); 514 break; 515 516 default: 517 pr_debug("No buttons configuration found for this device\n"); 518 return -ENOTSUPP; 519 } 520 521 if (err) 522 return -ENOMEM; 523 524 err = platform_device_register(&bcm47xx_buttons_gpio_keys); 525 if (err) { 526 pr_err("Failed to register platform device: %d\n", err); 527 return err; 528 } 529 530 return 0; 531 } 532