| // SPDX-License-Identifier: GPL-2.0 | |
| /* | |
| * Device physical location support | |
| * | |
| * Author: Won Chung <wonchung@google.com> | |
| */ | |
| #include <linux/acpi.h> | |
| #include <linux/sysfs.h> | |
| #include <linux/string_choices.h> | |
| #include "physical_location.h" | |
| bool dev_add_physical_location(struct device *dev) | |
| { | |
| struct acpi_pld_info *pld; | |
| if (!has_acpi_companion(dev)) | |
| return false; | |
| if (!acpi_get_physical_device_location(ACPI_HANDLE(dev), &pld)) | |
| return false; | |
| dev->physical_location = kzalloc_obj(*dev->physical_location); | |
| if (!dev->physical_location) { | |
| ACPI_FREE(pld); | |
| return false; | |
| } | |
| dev->physical_location->panel = pld->panel; | |
| dev->physical_location->vertical_position = pld->vertical_position; | |
| dev->physical_location->horizontal_position = pld->horizontal_position; | |
| dev->physical_location->dock = pld->dock; | |
| dev->physical_location->lid = pld->lid; | |
| ACPI_FREE(pld); | |
| return true; | |
| } | |
| static ssize_t panel_show(struct device *dev, struct device_attribute *attr, | |
| char *buf) | |
| { | |
| const char *panel; | |
| switch (dev->physical_location->panel) { | |
| case DEVICE_PANEL_TOP: | |
| panel = "top"; | |
| break; | |
| case DEVICE_PANEL_BOTTOM: | |
| panel = "bottom"; | |
| break; | |
| case DEVICE_PANEL_LEFT: | |
| panel = "left"; | |
| break; | |
| case DEVICE_PANEL_RIGHT: | |
| panel = "right"; | |
| break; | |
| case DEVICE_PANEL_FRONT: | |
| panel = "front"; | |
| break; | |
| case DEVICE_PANEL_BACK: | |
| panel = "back"; | |
| break; | |
| default: | |
| panel = "unknown"; | |
| } | |
| return sysfs_emit(buf, "%s\n", panel); | |
| } | |
| static DEVICE_ATTR_RO(panel); | |
| static ssize_t vertical_position_show(struct device *dev, | |
| struct device_attribute *attr, char *buf) | |
| { | |
| const char *vertical_position; | |
| switch (dev->physical_location->vertical_position) { | |
| case DEVICE_VERT_POS_UPPER: | |
| vertical_position = "upper"; | |
| break; | |
| case DEVICE_VERT_POS_CENTER: | |
| vertical_position = "center"; | |
| break; | |
| case DEVICE_VERT_POS_LOWER: | |
| vertical_position = "lower"; | |
| break; | |
| default: | |
| vertical_position = "unknown"; | |
| } | |
| return sysfs_emit(buf, "%s\n", vertical_position); | |
| } | |
| static DEVICE_ATTR_RO(vertical_position); | |
| static ssize_t horizontal_position_show(struct device *dev, | |
| struct device_attribute *attr, char *buf) | |
| { | |
| const char *horizontal_position; | |
| switch (dev->physical_location->horizontal_position) { | |
| case DEVICE_HORI_POS_LEFT: | |
| horizontal_position = "left"; | |
| break; | |
| case DEVICE_HORI_POS_CENTER: | |
| horizontal_position = "center"; | |
| break; | |
| case DEVICE_HORI_POS_RIGHT: | |
| horizontal_position = "right"; | |
| break; | |
| default: | |
| horizontal_position = "unknown"; | |
| } | |
| return sysfs_emit(buf, "%s\n", horizontal_position); | |
| } | |
| static DEVICE_ATTR_RO(horizontal_position); | |
| static ssize_t dock_show(struct device *dev, struct device_attribute *attr, | |
| char *buf) | |
| { | |
| return sysfs_emit(buf, "%s\n", | |
| str_yes_no(dev->physical_location->dock)); | |
| } | |
| static DEVICE_ATTR_RO(dock); | |
| static ssize_t lid_show(struct device *dev, struct device_attribute *attr, | |
| char *buf) | |
| { | |
| return sysfs_emit(buf, "%s\n", | |
| str_yes_no(dev->physical_location->lid)); | |
| } | |
| static DEVICE_ATTR_RO(lid); | |
| static struct attribute *dev_attr_physical_location[] = { | |
| &dev_attr_panel.attr, | |
| &dev_attr_vertical_position.attr, | |
| &dev_attr_horizontal_position.attr, | |
| &dev_attr_dock.attr, | |
| &dev_attr_lid.attr, | |
| NULL, | |
| }; | |
| const struct attribute_group dev_attr_physical_location_group = { | |
| .name = "physical_location", | |
| .attrs = dev_attr_physical_location, | |
| }; | |