1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Device physical location support 4 * 5 * Author: Won Chung <wonchung@google.com> 6 */ 7 8 #include <linux/acpi.h> 9 #include <linux/sysfs.h> 10 #include <linux/string_choices.h> 11 12 #include "physical_location.h" 13 14 bool dev_add_physical_location(struct device *dev) 15 { 16 struct acpi_pld_info *pld; 17 18 if (!has_acpi_companion(dev)) 19 return false; 20 21 if (!acpi_get_physical_device_location(ACPI_HANDLE(dev), &pld)) 22 return false; 23 24 dev->physical_location = 25 kzalloc(sizeof(*dev->physical_location), GFP_KERNEL); 26 if (!dev->physical_location) { 27 ACPI_FREE(pld); 28 return false; 29 } 30 31 dev->physical_location->panel = pld->panel; 32 dev->physical_location->vertical_position = pld->vertical_position; 33 dev->physical_location->horizontal_position = pld->horizontal_position; 34 dev->physical_location->dock = pld->dock; 35 dev->physical_location->lid = pld->lid; 36 37 ACPI_FREE(pld); 38 return true; 39 } 40 41 static ssize_t panel_show(struct device *dev, struct device_attribute *attr, 42 char *buf) 43 { 44 const char *panel; 45 46 switch (dev->physical_location->panel) { 47 case DEVICE_PANEL_TOP: 48 panel = "top"; 49 break; 50 case DEVICE_PANEL_BOTTOM: 51 panel = "bottom"; 52 break; 53 case DEVICE_PANEL_LEFT: 54 panel = "left"; 55 break; 56 case DEVICE_PANEL_RIGHT: 57 panel = "right"; 58 break; 59 case DEVICE_PANEL_FRONT: 60 panel = "front"; 61 break; 62 case DEVICE_PANEL_BACK: 63 panel = "back"; 64 break; 65 default: 66 panel = "unknown"; 67 } 68 return sysfs_emit(buf, "%s\n", panel); 69 } 70 static DEVICE_ATTR_RO(panel); 71 72 static ssize_t vertical_position_show(struct device *dev, 73 struct device_attribute *attr, char *buf) 74 { 75 const char *vertical_position; 76 77 switch (dev->physical_location->vertical_position) { 78 case DEVICE_VERT_POS_UPPER: 79 vertical_position = "upper"; 80 break; 81 case DEVICE_VERT_POS_CENTER: 82 vertical_position = "center"; 83 break; 84 case DEVICE_VERT_POS_LOWER: 85 vertical_position = "lower"; 86 break; 87 default: 88 vertical_position = "unknown"; 89 } 90 return sysfs_emit(buf, "%s\n", vertical_position); 91 } 92 static DEVICE_ATTR_RO(vertical_position); 93 94 static ssize_t horizontal_position_show(struct device *dev, 95 struct device_attribute *attr, char *buf) 96 { 97 const char *horizontal_position; 98 99 switch (dev->physical_location->horizontal_position) { 100 case DEVICE_HORI_POS_LEFT: 101 horizontal_position = "left"; 102 break; 103 case DEVICE_HORI_POS_CENTER: 104 horizontal_position = "center"; 105 break; 106 case DEVICE_HORI_POS_RIGHT: 107 horizontal_position = "right"; 108 break; 109 default: 110 horizontal_position = "unknown"; 111 } 112 return sysfs_emit(buf, "%s\n", horizontal_position); 113 } 114 static DEVICE_ATTR_RO(horizontal_position); 115 116 static ssize_t dock_show(struct device *dev, struct device_attribute *attr, 117 char *buf) 118 { 119 return sysfs_emit(buf, "%s\n", 120 str_yes_no(dev->physical_location->dock)); 121 } 122 static DEVICE_ATTR_RO(dock); 123 124 static ssize_t lid_show(struct device *dev, struct device_attribute *attr, 125 char *buf) 126 { 127 return sysfs_emit(buf, "%s\n", 128 str_yes_no(dev->physical_location->lid)); 129 } 130 static DEVICE_ATTR_RO(lid); 131 132 static struct attribute *dev_attr_physical_location[] = { 133 &dev_attr_panel.attr, 134 &dev_attr_vertical_position.attr, 135 &dev_attr_horizontal_position.attr, 136 &dev_attr_dock.attr, 137 &dev_attr_lid.attr, 138 NULL, 139 }; 140 141 const struct attribute_group dev_attr_physical_location_group = { 142 .name = "physical_location", 143 .attrs = dev_attr_physical_location, 144 }; 145 146