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