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