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