1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * button.c - ACPI Button Driver 4 * 5 * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> 6 * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> 7 */ 8 9 #define pr_fmt(fmt) "ACPI: button: " fmt 10 11 #include <linux/compiler.h> 12 #include <linux/kernel.h> 13 #include <linux/module.h> 14 #include <linux/init.h> 15 #include <linux/types.h> 16 #include <linux/proc_fs.h> 17 #include <linux/seq_file.h> 18 #include <linux/input.h> 19 #include <linux/slab.h> 20 #include <linux/acpi.h> 21 #include <linux/dmi.h> 22 #include <acpi/button.h> 23 24 #define PREFIX "ACPI: " 25 26 #define ACPI_BUTTON_CLASS "button" 27 #define ACPI_BUTTON_FILE_INFO "info" 28 #define ACPI_BUTTON_FILE_STATE "state" 29 #define ACPI_BUTTON_TYPE_UNKNOWN 0x00 30 #define ACPI_BUTTON_NOTIFY_STATUS 0x80 31 32 #define ACPI_BUTTON_SUBCLASS_POWER "power" 33 #define ACPI_BUTTON_DEVICE_NAME_POWER "Power Button" 34 #define ACPI_BUTTON_TYPE_POWER 0x01 35 36 #define ACPI_BUTTON_SUBCLASS_SLEEP "sleep" 37 #define ACPI_BUTTON_DEVICE_NAME_SLEEP "Sleep Button" 38 #define ACPI_BUTTON_TYPE_SLEEP 0x03 39 40 #define ACPI_BUTTON_SUBCLASS_LID "lid" 41 #define ACPI_BUTTON_DEVICE_NAME_LID "Lid Switch" 42 #define ACPI_BUTTON_TYPE_LID 0x05 43 44 enum { 45 ACPI_BUTTON_LID_INIT_IGNORE, 46 ACPI_BUTTON_LID_INIT_OPEN, 47 ACPI_BUTTON_LID_INIT_METHOD, 48 ACPI_BUTTON_LID_INIT_DISABLED, 49 }; 50 51 static const char * const lid_init_state_str[] = { 52 [ACPI_BUTTON_LID_INIT_IGNORE] = "ignore", 53 [ACPI_BUTTON_LID_INIT_OPEN] = "open", 54 [ACPI_BUTTON_LID_INIT_METHOD] = "method", 55 [ACPI_BUTTON_LID_INIT_DISABLED] = "disabled", 56 }; 57 58 #define _COMPONENT ACPI_BUTTON_COMPONENT 59 ACPI_MODULE_NAME("button"); 60 61 MODULE_AUTHOR("Paul Diefenbaugh"); 62 MODULE_DESCRIPTION("ACPI Button Driver"); 63 MODULE_LICENSE("GPL"); 64 65 static const struct acpi_device_id button_device_ids[] = { 66 {ACPI_BUTTON_HID_LID, 0}, 67 {ACPI_BUTTON_HID_SLEEP, 0}, 68 {ACPI_BUTTON_HID_SLEEPF, 0}, 69 {ACPI_BUTTON_HID_POWER, 0}, 70 {ACPI_BUTTON_HID_POWERF, 0}, 71 {"", 0}, 72 }; 73 MODULE_DEVICE_TABLE(acpi, button_device_ids); 74 75 /* Please keep this list sorted alphabetically by vendor and model */ 76 static const struct dmi_system_id dmi_lid_quirks[] = { 77 { 78 /* 79 * Acer Switch 10 SW5-012. _LID method messes with home and 80 * power button GPIO IRQ settings causing an interrupt storm on 81 * both GPIOs. This is unfixable without a DSDT override, so we 82 * have to disable the lid-switch functionality altogether :| 83 */ 84 .matches = { 85 DMI_MATCH(DMI_SYS_VENDOR, "Acer"), 86 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire SW5-012"), 87 }, 88 .driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_DISABLED, 89 }, 90 { 91 /* 92 * Asus T200TA, _LID keeps reporting closed after every second 93 * openening of the lid. Causing immediate re-suspend after 94 * opening every other open. Using LID_INIT_OPEN fixes this. 95 */ 96 .matches = { 97 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 98 DMI_MATCH(DMI_PRODUCT_NAME, "T200TA"), 99 }, 100 .driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_OPEN, 101 }, 102 { 103 /* GP-electronic T701, _LID method points to a floating GPIO */ 104 .matches = { 105 DMI_MATCH(DMI_SYS_VENDOR, "Insyde"), 106 DMI_MATCH(DMI_PRODUCT_NAME, "T701"), 107 DMI_MATCH(DMI_BIOS_VERSION, "BYT70A.YNCHENG.WIN.007"), 108 }, 109 .driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_DISABLED, 110 }, 111 { 112 /* 113 * Medion Akoya E2215T, notification of the LID device only 114 * happens on close, not on open and _LID always returns closed. 115 */ 116 .matches = { 117 DMI_MATCH(DMI_SYS_VENDOR, "MEDION"), 118 DMI_MATCH(DMI_PRODUCT_NAME, "E2215T MD60198"), 119 }, 120 .driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_OPEN, 121 }, 122 { 123 /* 124 * Razer Blade Stealth 13 late 2019, notification of the LID device 125 * only happens on close, not on open and _LID always returns closed. 126 */ 127 .matches = { 128 DMI_MATCH(DMI_SYS_VENDOR, "Razer"), 129 DMI_MATCH(DMI_PRODUCT_NAME, "Razer Blade Stealth 13 Late 2019"), 130 }, 131 .driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_OPEN, 132 }, 133 {} 134 }; 135 136 static int acpi_button_add(struct acpi_device *device); 137 static int acpi_button_remove(struct acpi_device *device); 138 static void acpi_button_notify(struct acpi_device *device, u32 event); 139 140 #ifdef CONFIG_PM_SLEEP 141 static int acpi_button_suspend(struct device *dev); 142 static int acpi_button_resume(struct device *dev); 143 #else 144 #define acpi_button_suspend NULL 145 #define acpi_button_resume NULL 146 #endif 147 static SIMPLE_DEV_PM_OPS(acpi_button_pm, acpi_button_suspend, acpi_button_resume); 148 149 static struct acpi_driver acpi_button_driver = { 150 .name = "button", 151 .class = ACPI_BUTTON_CLASS, 152 .ids = button_device_ids, 153 .ops = { 154 .add = acpi_button_add, 155 .remove = acpi_button_remove, 156 .notify = acpi_button_notify, 157 }, 158 .drv.pm = &acpi_button_pm, 159 }; 160 161 struct acpi_button { 162 unsigned int type; 163 struct input_dev *input; 164 char phys[32]; /* for input device */ 165 unsigned long pushed; 166 int last_state; 167 ktime_t last_time; 168 bool suspended; 169 }; 170 171 static struct acpi_device *lid_device; 172 static long lid_init_state = -1; 173 174 static unsigned long lid_report_interval __read_mostly = 500; 175 module_param(lid_report_interval, ulong, 0644); 176 MODULE_PARM_DESC(lid_report_interval, "Interval (ms) between lid key events"); 177 178 /* -------------------------------------------------------------------------- 179 FS Interface (/proc) 180 -------------------------------------------------------------------------- */ 181 182 static struct proc_dir_entry *acpi_button_dir; 183 static struct proc_dir_entry *acpi_lid_dir; 184 185 static int acpi_lid_evaluate_state(struct acpi_device *device) 186 { 187 unsigned long long lid_state; 188 acpi_status status; 189 190 status = acpi_evaluate_integer(device->handle, "_LID", NULL, &lid_state); 191 if (ACPI_FAILURE(status)) 192 return -ENODEV; 193 194 return lid_state ? 1 : 0; 195 } 196 197 static int acpi_lid_notify_state(struct acpi_device *device, int state) 198 { 199 struct acpi_button *button = acpi_driver_data(device); 200 ktime_t next_report; 201 bool do_update; 202 203 /* 204 * In lid_init_state=ignore mode, if user opens/closes lid 205 * frequently with "open" missing, and "last_time" is also updated 206 * frequently, "close" cannot be delivered to the userspace. 207 * So "last_time" is only updated after a timeout or an actual 208 * switch. 209 */ 210 if (lid_init_state != ACPI_BUTTON_LID_INIT_IGNORE || 211 button->last_state != !!state) 212 do_update = true; 213 else 214 do_update = false; 215 216 next_report = ktime_add(button->last_time, 217 ms_to_ktime(lid_report_interval)); 218 if (button->last_state == !!state && 219 ktime_after(ktime_get(), next_report)) { 220 /* Complain the buggy firmware */ 221 pr_warn_once("The lid device is not compliant to SW_LID.\n"); 222 223 /* 224 * Send the unreliable complement switch event: 225 * 226 * On most platforms, the lid device is reliable. However 227 * there are exceptions: 228 * 1. Platforms returning initial lid state as "close" by 229 * default after booting/resuming: 230 * https://bugzilla.kernel.org/show_bug.cgi?id=89211 231 * https://bugzilla.kernel.org/show_bug.cgi?id=106151 232 * 2. Platforms never reporting "open" events: 233 * https://bugzilla.kernel.org/show_bug.cgi?id=106941 234 * On these buggy platforms, the usage model of the ACPI 235 * lid device actually is: 236 * 1. The initial returning value of _LID may not be 237 * reliable. 238 * 2. The open event may not be reliable. 239 * 3. The close event is reliable. 240 * 241 * But SW_LID is typed as input switch event, the input 242 * layer checks if the event is redundant. Hence if the 243 * state is not switched, the userspace cannot see this 244 * platform triggered reliable event. By inserting a 245 * complement switch event, it then is guaranteed that the 246 * platform triggered reliable one can always be seen by 247 * the userspace. 248 */ 249 if (lid_init_state == ACPI_BUTTON_LID_INIT_IGNORE) { 250 do_update = true; 251 /* 252 * Do generate complement switch event for "close" 253 * as "close" is reliable and wrong "open" won't 254 * trigger unexpected behaviors. 255 * Do not generate complement switch event for 256 * "open" as "open" is not reliable and wrong 257 * "close" will trigger unexpected behaviors. 258 */ 259 if (!state) { 260 input_report_switch(button->input, 261 SW_LID, state); 262 input_sync(button->input); 263 } 264 } 265 } 266 /* Send the platform triggered reliable event */ 267 if (do_update) { 268 acpi_handle_debug(device->handle, "ACPI LID %s\n", 269 state ? "open" : "closed"); 270 input_report_switch(button->input, SW_LID, !state); 271 input_sync(button->input); 272 button->last_state = !!state; 273 button->last_time = ktime_get(); 274 } 275 276 return 0; 277 } 278 279 static int __maybe_unused acpi_button_state_seq_show(struct seq_file *seq, 280 void *offset) 281 { 282 struct acpi_device *device = seq->private; 283 int state; 284 285 state = acpi_lid_evaluate_state(device); 286 seq_printf(seq, "state: %s\n", 287 state < 0 ? "unsupported" : (state ? "open" : "closed")); 288 return 0; 289 } 290 291 static int acpi_button_add_fs(struct acpi_device *device) 292 { 293 struct acpi_button *button = acpi_driver_data(device); 294 struct proc_dir_entry *entry = NULL; 295 int ret = 0; 296 297 /* procfs I/F for ACPI lid device only */ 298 if (button->type != ACPI_BUTTON_TYPE_LID) 299 return 0; 300 301 if (acpi_button_dir || acpi_lid_dir) { 302 printk(KERN_ERR PREFIX "More than one Lid device found!\n"); 303 return -EEXIST; 304 } 305 306 /* create /proc/acpi/button */ 307 acpi_button_dir = proc_mkdir(ACPI_BUTTON_CLASS, acpi_root_dir); 308 if (!acpi_button_dir) 309 return -ENODEV; 310 311 /* create /proc/acpi/button/lid */ 312 acpi_lid_dir = proc_mkdir(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir); 313 if (!acpi_lid_dir) { 314 ret = -ENODEV; 315 goto remove_button_dir; 316 } 317 318 /* create /proc/acpi/button/lid/LID/ */ 319 acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), acpi_lid_dir); 320 if (!acpi_device_dir(device)) { 321 ret = -ENODEV; 322 goto remove_lid_dir; 323 } 324 325 /* create /proc/acpi/button/lid/LID/state */ 326 entry = proc_create_single_data(ACPI_BUTTON_FILE_STATE, S_IRUGO, 327 acpi_device_dir(device), acpi_button_state_seq_show, 328 device); 329 if (!entry) { 330 ret = -ENODEV; 331 goto remove_dev_dir; 332 } 333 334 done: 335 return ret; 336 337 remove_dev_dir: 338 remove_proc_entry(acpi_device_bid(device), 339 acpi_lid_dir); 340 acpi_device_dir(device) = NULL; 341 remove_lid_dir: 342 remove_proc_entry(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir); 343 acpi_lid_dir = NULL; 344 remove_button_dir: 345 remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir); 346 acpi_button_dir = NULL; 347 goto done; 348 } 349 350 static int acpi_button_remove_fs(struct acpi_device *device) 351 { 352 struct acpi_button *button = acpi_driver_data(device); 353 354 if (button->type != ACPI_BUTTON_TYPE_LID) 355 return 0; 356 357 remove_proc_entry(ACPI_BUTTON_FILE_STATE, 358 acpi_device_dir(device)); 359 remove_proc_entry(acpi_device_bid(device), 360 acpi_lid_dir); 361 acpi_device_dir(device) = NULL; 362 remove_proc_entry(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir); 363 acpi_lid_dir = NULL; 364 remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir); 365 acpi_button_dir = NULL; 366 367 return 0; 368 } 369 370 /* -------------------------------------------------------------------------- 371 Driver Interface 372 -------------------------------------------------------------------------- */ 373 int acpi_lid_open(void) 374 { 375 if (!lid_device) 376 return -ENODEV; 377 378 return acpi_lid_evaluate_state(lid_device); 379 } 380 EXPORT_SYMBOL(acpi_lid_open); 381 382 static int acpi_lid_update_state(struct acpi_device *device, 383 bool signal_wakeup) 384 { 385 int state; 386 387 state = acpi_lid_evaluate_state(device); 388 if (state < 0) 389 return state; 390 391 if (state && signal_wakeup) 392 acpi_pm_wakeup_event(&device->dev); 393 394 return acpi_lid_notify_state(device, state); 395 } 396 397 static void acpi_lid_initialize_state(struct acpi_device *device) 398 { 399 switch (lid_init_state) { 400 case ACPI_BUTTON_LID_INIT_OPEN: 401 (void)acpi_lid_notify_state(device, 1); 402 break; 403 case ACPI_BUTTON_LID_INIT_METHOD: 404 (void)acpi_lid_update_state(device, false); 405 break; 406 case ACPI_BUTTON_LID_INIT_IGNORE: 407 default: 408 break; 409 } 410 } 411 412 static void acpi_button_notify(struct acpi_device *device, u32 event) 413 { 414 struct acpi_button *button = acpi_driver_data(device); 415 struct input_dev *input; 416 int users; 417 418 switch (event) { 419 case ACPI_FIXED_HARDWARE_EVENT: 420 event = ACPI_BUTTON_NOTIFY_STATUS; 421 /* fall through */ 422 case ACPI_BUTTON_NOTIFY_STATUS: 423 input = button->input; 424 if (button->type == ACPI_BUTTON_TYPE_LID) { 425 mutex_lock(&button->input->mutex); 426 users = button->input->users; 427 mutex_unlock(&button->input->mutex); 428 if (users) 429 acpi_lid_update_state(device, true); 430 } else { 431 int keycode; 432 433 acpi_pm_wakeup_event(&device->dev); 434 if (button->suspended) 435 break; 436 437 keycode = test_bit(KEY_SLEEP, input->keybit) ? 438 KEY_SLEEP : KEY_POWER; 439 input_report_key(input, keycode, 1); 440 input_sync(input); 441 input_report_key(input, keycode, 0); 442 input_sync(input); 443 444 acpi_bus_generate_netlink_event( 445 device->pnp.device_class, 446 dev_name(&device->dev), 447 event, ++button->pushed); 448 } 449 break; 450 default: 451 ACPI_DEBUG_PRINT((ACPI_DB_INFO, 452 "Unsupported event [0x%x]\n", event)); 453 break; 454 } 455 } 456 457 #ifdef CONFIG_PM_SLEEP 458 static int acpi_button_suspend(struct device *dev) 459 { 460 struct acpi_device *device = to_acpi_device(dev); 461 struct acpi_button *button = acpi_driver_data(device); 462 463 button->suspended = true; 464 return 0; 465 } 466 467 static int acpi_button_resume(struct device *dev) 468 { 469 struct acpi_device *device = to_acpi_device(dev); 470 struct acpi_button *button = acpi_driver_data(device); 471 472 button->suspended = false; 473 if (button->type == ACPI_BUTTON_TYPE_LID && button->input->users) { 474 button->last_state = !!acpi_lid_evaluate_state(device); 475 button->last_time = ktime_get(); 476 acpi_lid_initialize_state(device); 477 } 478 return 0; 479 } 480 #endif 481 482 static int acpi_lid_input_open(struct input_dev *input) 483 { 484 struct acpi_device *device = input_get_drvdata(input); 485 struct acpi_button *button = acpi_driver_data(device); 486 487 button->last_state = !!acpi_lid_evaluate_state(device); 488 button->last_time = ktime_get(); 489 acpi_lid_initialize_state(device); 490 491 return 0; 492 } 493 494 static int acpi_button_add(struct acpi_device *device) 495 { 496 struct acpi_button *button; 497 struct input_dev *input; 498 const char *hid = acpi_device_hid(device); 499 char *name, *class; 500 int error; 501 502 if (!strcmp(hid, ACPI_BUTTON_HID_LID) && 503 lid_init_state == ACPI_BUTTON_LID_INIT_DISABLED) 504 return -ENODEV; 505 506 button = kzalloc(sizeof(struct acpi_button), GFP_KERNEL); 507 if (!button) 508 return -ENOMEM; 509 510 device->driver_data = button; 511 512 button->input = input = input_allocate_device(); 513 if (!input) { 514 error = -ENOMEM; 515 goto err_free_button; 516 } 517 518 name = acpi_device_name(device); 519 class = acpi_device_class(device); 520 521 if (!strcmp(hid, ACPI_BUTTON_HID_POWER) || 522 !strcmp(hid, ACPI_BUTTON_HID_POWERF)) { 523 button->type = ACPI_BUTTON_TYPE_POWER; 524 strcpy(name, ACPI_BUTTON_DEVICE_NAME_POWER); 525 sprintf(class, "%s/%s", 526 ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_POWER); 527 } else if (!strcmp(hid, ACPI_BUTTON_HID_SLEEP) || 528 !strcmp(hid, ACPI_BUTTON_HID_SLEEPF)) { 529 button->type = ACPI_BUTTON_TYPE_SLEEP; 530 strcpy(name, ACPI_BUTTON_DEVICE_NAME_SLEEP); 531 sprintf(class, "%s/%s", 532 ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_SLEEP); 533 } else if (!strcmp(hid, ACPI_BUTTON_HID_LID)) { 534 button->type = ACPI_BUTTON_TYPE_LID; 535 strcpy(name, ACPI_BUTTON_DEVICE_NAME_LID); 536 sprintf(class, "%s/%s", 537 ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_LID); 538 input->open = acpi_lid_input_open; 539 } else { 540 printk(KERN_ERR PREFIX "Unsupported hid [%s]\n", hid); 541 error = -ENODEV; 542 goto err_free_input; 543 } 544 545 error = acpi_button_add_fs(device); 546 if (error) 547 goto err_free_input; 548 549 snprintf(button->phys, sizeof(button->phys), "%s/button/input0", hid); 550 551 input->name = name; 552 input->phys = button->phys; 553 input->id.bustype = BUS_HOST; 554 input->id.product = button->type; 555 input->dev.parent = &device->dev; 556 557 switch (button->type) { 558 case ACPI_BUTTON_TYPE_POWER: 559 input_set_capability(input, EV_KEY, KEY_POWER); 560 break; 561 562 case ACPI_BUTTON_TYPE_SLEEP: 563 input_set_capability(input, EV_KEY, KEY_SLEEP); 564 break; 565 566 case ACPI_BUTTON_TYPE_LID: 567 input_set_capability(input, EV_SW, SW_LID); 568 break; 569 } 570 571 input_set_drvdata(input, device); 572 error = input_register_device(input); 573 if (error) 574 goto err_remove_fs; 575 if (button->type == ACPI_BUTTON_TYPE_LID) { 576 /* 577 * This assumes there's only one lid device, or if there are 578 * more we only care about the last one... 579 */ 580 lid_device = device; 581 } 582 583 device_init_wakeup(&device->dev, true); 584 printk(KERN_INFO PREFIX "%s [%s]\n", name, acpi_device_bid(device)); 585 return 0; 586 587 err_remove_fs: 588 acpi_button_remove_fs(device); 589 err_free_input: 590 input_free_device(input); 591 err_free_button: 592 kfree(button); 593 return error; 594 } 595 596 static int acpi_button_remove(struct acpi_device *device) 597 { 598 struct acpi_button *button = acpi_driver_data(device); 599 600 acpi_button_remove_fs(device); 601 input_unregister_device(button->input); 602 kfree(button); 603 return 0; 604 } 605 606 static int param_set_lid_init_state(const char *val, 607 const struct kernel_param *kp) 608 { 609 int i; 610 611 i = sysfs_match_string(lid_init_state_str, val); 612 if (i < 0) 613 return i; 614 615 lid_init_state = i; 616 pr_info("Initial lid state set to '%s'\n", lid_init_state_str[i]); 617 return 0; 618 } 619 620 static int param_get_lid_init_state(char *buf, const struct kernel_param *kp) 621 { 622 int i, c = 0; 623 624 for (i = 0; i < ARRAY_SIZE(lid_init_state_str); i++) 625 if (i == lid_init_state) 626 c += sprintf(buf + c, "[%s] ", lid_init_state_str[i]); 627 else 628 c += sprintf(buf + c, "%s ", lid_init_state_str[i]); 629 630 buf[c - 1] = '\n'; /* Replace the final space with a newline */ 631 632 return c; 633 } 634 635 module_param_call(lid_init_state, 636 param_set_lid_init_state, param_get_lid_init_state, 637 NULL, 0644); 638 MODULE_PARM_DESC(lid_init_state, "Behavior for reporting LID initial state"); 639 640 static int acpi_button_register_driver(struct acpi_driver *driver) 641 { 642 const struct dmi_system_id *dmi_id; 643 644 if (lid_init_state == -1) { 645 dmi_id = dmi_first_match(dmi_lid_quirks); 646 if (dmi_id) 647 lid_init_state = (long)dmi_id->driver_data; 648 else 649 lid_init_state = ACPI_BUTTON_LID_INIT_METHOD; 650 } 651 652 /* 653 * Modules such as nouveau.ko and i915.ko have a link time dependency 654 * on acpi_lid_open(), and would therefore not be loadable on ACPI 655 * capable kernels booted in non-ACPI mode if the return value of 656 * acpi_bus_register_driver() is returned from here with ACPI disabled 657 * when this driver is built as a module. 658 */ 659 if (acpi_disabled) 660 return 0; 661 662 return acpi_bus_register_driver(driver); 663 } 664 665 static void acpi_button_unregister_driver(struct acpi_driver *driver) 666 { 667 if (!acpi_disabled) 668 acpi_bus_unregister_driver(driver); 669 } 670 671 module_driver(acpi_button_driver, acpi_button_register_driver, 672 acpi_button_unregister_driver); 673