1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2020 Oxide Computer Company
14 */
15
16 /*
17 * Construct sensors based on the ksensor framework for PCI devices. The kernel
18 * will create devices such that they show up
19 * /dev/sensors/temperature/pci/<bus>.<func>/<sensors>. This iterates and adds a
20 * sensor for the device based on the total number that exist for all of them.
21 */
22
23 #include <sys/types.h>
24 #include <dirent.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <fm/topo_mod.h>
28 #include <fm/topo_hc.h>
29 #include <pcibus.h>
30 #include <topo_sensor.h>
31
32 static const char *pci_sensor_types[] = { "current", "voltage", "temperature" };
33
34 static int
pci_create_dev_scandir(topo_mod_t * mod,tnode_t * dev,const char * path)35 pci_create_dev_scandir(topo_mod_t *mod, tnode_t *dev, const char *path)
36 {
37 int ret;
38 DIR *d;
39 struct dirent *ent;
40
41 d = opendir(path);
42 if (d == NULL) {
43 if (errno == ENOENT) {
44 return (0);
45 }
46
47 topo_mod_dprintf(mod, "failed to open %s: %s", path,
48 strerror(errno));
49 return (topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM));
50 }
51
52 while ((ent = readdir(d)) != NULL) {
53 char spath[PATH_MAX];
54
55 if (strcmp(ent->d_name, ".") == 0 ||
56 strcmp(ent->d_name, "..") == 0) {
57 continue;
58 }
59
60 if (snprintf(spath, sizeof (spath), "%s/%s", path,
61 ent->d_name) >= sizeof (spath)) {
62 topo_mod_dprintf(mod, "failed to construct sensor path "
63 "for %s/%s, path too long", path, ent->d_name);
64 ret = topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM);
65 goto out;
66 }
67
68 topo_mod_dprintf(mod, "attempting to create sensor at %s",
69 spath);
70 if ((ret = topo_sensor_create_scalar_sensor(mod, dev, spath,
71 ent->d_name)) < 0) {
72 goto out;
73 }
74 }
75
76 ret = 0;
77
78 out:
79 (void) closedir(d);
80 return (ret);
81 }
82
83 int
pci_create_dev_sensors(topo_mod_t * mod,tnode_t * dev)84 pci_create_dev_sensors(topo_mod_t *mod, tnode_t *dev)
85 {
86 uint_t i;
87 char path[PATH_MAX];
88 topo_instance_t binst, dinst;
89 tnode_t *parent = topo_node_parent(dev);
90
91 binst = topo_node_instance(parent);
92 dinst = topo_node_instance(dev);
93
94 for (i = 0; i < ARRAY_SIZE(pci_sensor_types); i++) {
95 int ret;
96
97 if (snprintf(path, sizeof (path), "/dev/sensors/%s/pci/%x.%x",
98 pci_sensor_types[i], binst, dinst) >= sizeof (path)) {
99 topo_mod_dprintf(mod, "failed to construct %s sensor "
100 "directory path, path too long",
101 pci_sensor_types[i]);
102 return (topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM));
103 }
104
105 topo_mod_dprintf(mod, "searching for sensors in %s", path);
106 if ((ret = pci_create_dev_scandir(mod, dev, path)) != 0) {
107 return (ret);
108 }
109 }
110
111 return (0);
112 }
113