blob: c0f6241ef35384faaf2d12edc85098734e78742b [file] [log] [blame]
Yi Li2f3213f2016-08-03 11:09:55 +08001/**
2 * Copyright © 2016 IBM Corporation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16#include <stdio.h>
17#include <stdlib.h>
18#include <limits.h>
19#include <errno.h>
20#include <string.h>
21#include <getopt.h>
22#include <systemd/sd-bus.h>
23
24#define DBUS_MAX_NAME_LEN 256
25
Brad Bishope87cc292017-02-25 15:39:33 -050026const char *objectmapper_service_name = "xyz.openbmc_project.ObjectMapper";
Leonel Gonzaleze1e2e6d2017-03-16 13:47:46 -050027const char *objectmapper_object_name = "/xyz/openbmc_project/object_mapper";
Brad Bishope87cc292017-02-25 15:39:33 -050028const char *objectmapper_intf_name = "xyz.openbmc_project.ObjectMapper";
Yi Li2f3213f2016-08-03 11:09:55 +080029
30typedef struct {
31 int fan_num;
32 int cpu_num;
33 int core_num;
34 int dimm_num;
35 sd_bus *bus;
36 char sensor_service[DBUS_MAX_NAME_LEN];
37 char inventory_service[DBUS_MAX_NAME_LEN];
38} fan_info_t;
39
40/* Get an object's bus name from ObjectMapper */
41int get_connection(sd_bus *bus, char *connection, const char *obj_path)
42{
43 sd_bus_error bus_error = SD_BUS_ERROR_NULL;
44 sd_bus_message *m = NULL;
45 char *temp_buf = NULL, *intf = NULL;
46 int rc;
47
48 rc = sd_bus_call_method(bus,
49 objectmapper_service_name,
50 objectmapper_object_name,
51 objectmapper_intf_name,
52 "GetObject",
53 &bus_error,
54 &m,
55 "s",
56 obj_path);
57 if (rc < 0) {
58 fprintf(stderr,
59 "Failed to GetObject: %s\n", bus_error.message);
60 goto finish;
61 }
62
63 /* Get the key, aka, the bus name */
64 sd_bus_message_read(m, "a{sas}", 1, &temp_buf, 1, &intf);
65 strncpy(connection, temp_buf, DBUS_MAX_NAME_LEN);
66
67finish:
68 sd_bus_error_free(&bus_error);
69 sd_bus_message_unref(m);
70 sd_bus_flush(bus);
71
72 return rc;
73}
74
75
76int set_dbus_sensor(sd_bus *bus, const char *obj_path, int val)
77{
78 char connection[DBUS_MAX_NAME_LEN];
79 sd_bus_error bus_error = SD_BUS_ERROR_NULL;
80 sd_bus_message *response = NULL;
81 int rc;
82
83 if (!bus || !obj_path)
84 return -1;
85
86 rc = get_connection(bus, connection, obj_path);
87 if (rc < 0) {
88 fprintf(stderr,
89 "fanctl: Failed to get bus name for %s\n", obj_path);
90 goto finish;
91 }
92
93 rc = sd_bus_call_method(bus,
94 connection,
95 obj_path,
96 "org.openbmc.SensorValue",
97 "setValue",
98 &bus_error,
99 &response,
100 "i",
101 val);
102 if (rc < 0)
103 fprintf(stderr,
104 "fanctl: Failed to set sensor %s:[%s]\n",
105 obj_path, strerror(-rc));
106
107finish:
108 sd_bus_error_free(&bus_error);
109 sd_bus_message_unref(response);
110 sd_bus_flush(bus);
111
112 return rc;
113}
114
115/* Read sensor value from "org.openbmc.Sensors" */
116int read_dbus_sensor(sd_bus *bus, const char *obj_path)
117{
118 char connection[DBUS_MAX_NAME_LEN];
119 sd_bus_error bus_error = SD_BUS_ERROR_NULL;
120 sd_bus_message *response = NULL;
121 int rc;
122 int val = 0;
123
124 if (!bus || !obj_path)
125 return 0;
126
127 rc = get_connection(bus, connection, obj_path);
128 if (rc < 0) {
129 val = 0;
130 fprintf(stderr,
131 "fanctl: Failed to get bus name for %s\n", obj_path);
132 goto finish;
133 }
134
135 rc = sd_bus_call_method(bus,
136 connection,
137 obj_path,
138 "org.openbmc.SensorValue",
139 "getValue",
140 &bus_error,
141 &response,
142 NULL);
143 if (rc < 0) {
144 val = 0;
145 fprintf(stderr,
146 "fanctl: Failed to read sensor value from %s:[%s]\n",
147 obj_path, strerror(-rc));
148 goto finish;
149 }
150
151 rc = sd_bus_message_read(response, "v","i", &val);
152 if (rc < 0) {
153 val = 0;
154 fprintf(stderr,
155 "fanctl: Failed to parse sensor value "
156 "response message from %s:[%s]\n",
157 obj_path, strerror(-rc));
158 }
159
160finish:
161 sd_bus_error_free(&bus_error);
162 sd_bus_message_unref(response);
163 sd_bus_flush(bus);
164
165 return val;
166}
167
168/* set fan speed with /org/openbmc/sensors/speed/fan* object */
169static int fan_set_speed(sd_bus *bus, int fan_id, uint8_t fan_speed)
170{
171 char obj_path[DBUS_MAX_NAME_LEN];
172 int rc;
173
174 if (!bus)
175 return -1;
176
177 snprintf(obj_path, sizeof(obj_path),
178 "/org/openbmc/sensors/speed/fan%d", fan_id);
179 rc = set_dbus_sensor(bus, obj_path, fan_speed);
180 if (rc < 0)
181 fprintf(stderr, "fanctl: Failed to set fan[%d] speed[%d]\n",
182 fan_id, fan_speed);
183
184 return rc;
185}
186
187static int fan_set_max_speed(fan_info_t *info)
188{
189 int i;
190 int rc = -1;
191
192 if (!info)
193 return -1;
194 for (i = 0; i < info->fan_num; i++) {
195 rc = fan_set_speed(info->bus, i, 255);
196 if (rc < 0)
197 break;
198 fprintf(stderr, "fanctl: Set fan%d to max speed\n", i);
199 }
200
201 return rc;
202}
203/*
204 * FAN_TACH_OFFSET is specific to Barreleye.
205 * Barreleye uses NTC7904D HW Monitor as Fan tachometoer.
Gunnar Millsaa4c3102017-10-25 20:50:47 -0500206 * The 13-bit FANIN value is made up Higher part: [12:5],
Yi Li2f3213f2016-08-03 11:09:55 +0800207 * and Lower part: [4:0], which are read from two sensors.
208 * see: https://www.nuvoton.com/resource-files/NCT7904D_Datasheet_V1.44.pdf
209 */
210#define FAN_TACH_OFFSET 5
211static int fan_get_speed(sd_bus *bus, int fan_id)
212{
213 int fan_tach_H = 0, fan_tach_L = 0;
214 char obj_path[DBUS_MAX_NAME_LEN];
215 int fan_speed;
216
217 /* get fan tach */
218 /* The object path is specific to Barreleye */
219 snprintf(obj_path, sizeof(obj_path),
220 "/org/openbmc/sensors/tach/fan%dH", fan_id);
221 fan_tach_H = read_dbus_sensor(bus, obj_path);
222 snprintf(obj_path, sizeof(obj_path),
223 "/org/openbmc/sensors/tach/fan%dL", fan_id);
224 fan_tach_L = read_dbus_sensor(bus, obj_path);
225
226 /* invalid sensor value is -1 */
227 if (fan_tach_H <= 0 || fan_tach_L <= 0)
228 fan_speed = 0;
229 else
230 fan_speed = fan_tach_H << FAN_TACH_OFFSET | fan_tach_L;
231
232 fprintf(stderr, "fan%d speed: %d\n", fan_id, fan_speed);
233 return fan_speed;
234}
235
236/* set Fan Inventory 'Present' status */
237int fan_set_present(sd_bus *bus, int fan_id, int val)
238{
239 sd_bus_error bus_error = SD_BUS_ERROR_NULL;
240 sd_bus_message *response = NULL;
241 int rc;
242 char obj_path[DBUS_MAX_NAME_LEN];
243 char connection[DBUS_MAX_NAME_LEN];
244
245 snprintf(obj_path, sizeof(obj_path),
246 "/org/openbmc/inventory/system/chassis/fan%d", fan_id);
247
248 rc = get_connection(bus, connection, obj_path);
249 if (rc < 0) {
250 fprintf(stderr,
251 "fanctl: Failed to get bus name for %s\n", obj_path);
252 goto finish;
253 }
254
255 rc = sd_bus_call_method(bus,
256 connection,
257 obj_path,
258 "org.openbmc.InventoryItem",
259 "setPresent",
260 &bus_error,
261 &response,
262 "s",
263 (val == 1 ? "True" : "False"));
264 if(rc < 0)
265 fprintf(stderr,
266 "fanctl: Failed to update fan presence via dbus: %s\n",
267 bus_error.message);
268
269 fprintf(stderr, "fanctl: Set fan%d present status to: %s\n",
270 fan_id, (val == 1 ? "True" : "False"));
271
272finish:
273 sd_bus_error_free(&bus_error);
274 sd_bus_message_unref(response);
275 sd_bus_flush(bus);
276
277 return rc;
278}
279
280/*
281 * Update Fan Invertory 'Present' status by first reading fan speed.
282 * If fan speed is '0', the fan is considerred not 'Present'.
283 */
284static int fan_update_present(fan_info_t *info)
285{
286 int i;
287 int rc = -1;
288 int fan_speed;
289
290 if (!info)
291 return -1;
292
293 for (i = 0; i < info->fan_num; i++) {
294 fan_speed = fan_get_speed(info->bus, i);
295 if (fan_speed > 0)
296 rc = fan_set_present(info->bus, i, 1);
297 else
298 rc = fan_set_present(info->bus, i, 0);
299
300 if (rc < 0) {
301 fprintf(stderr,
302 "fanctl: Failed to set fan present status\n");
303 break;
304 }
305 }
306
307 return rc;
308}
309/*
310 * Router function for any FAN operations that come via dbus
311 */
312static int fan_function_router(sd_bus_message *msg, void *user_data,
313 sd_bus_error *ret_error)
314{
315 /* Generic error reporter. */
316 int rc = -1;
317 fan_info_t *info = user_data;
318
319 /* Get the Operation. */
320 const char *fan_function = sd_bus_message_get_member(msg);
321 if (fan_function == NULL) {
Gunnar Mills9d572eb2018-04-08 14:30:41 -0500322 fprintf(stderr, "fanctl: Null FAN function specified\n");
Yi Li2f3213f2016-08-03 11:09:55 +0800323 return sd_bus_reply_method_return(msg, "i", rc);
324 }
325
326 /* Route the user action to appropriate handlers. */
327 if ((strcmp(fan_function, "setMax") == 0)) {
328 rc = fan_set_max_speed(info);
329 return sd_bus_reply_method_return(msg, "i", rc);
330 }
331 if ((strcmp(fan_function, "updatePresent") == 0)) {
332 rc = fan_update_present(info);
333 return sd_bus_reply_method_return(msg, "i", rc);
334 }
335
336 return sd_bus_reply_method_return(msg, "i", rc);
337}
338
339/* Dbus Services offered by this FAN controller */
340static const sd_bus_vtable fan_control_vtable[] =
341{
342 SD_BUS_VTABLE_START(0),
343 SD_BUS_METHOD("setMax", "", "i", &fan_function_router,
344 SD_BUS_VTABLE_UNPRIVILEGED),
345 SD_BUS_METHOD("updatePresent", "", "i", &fan_function_router,
346 SD_BUS_VTABLE_UNPRIVILEGED),
347 SD_BUS_VTABLE_END,
348};
349
350int start_fan_services(fan_info_t *info)
351{
352 /* Generic error reporter. */
353 int rc = -1;
354 /* slot where we are offering the FAN dbus service. */
355 sd_bus_slot *fan_slot = NULL;
356 const char *fan_object = "/org/openbmc/control/fans";
357
358 info->bus = NULL;
359 /* Get a hook onto system bus. */
360 rc = sd_bus_open_system(&info->bus);
361 if (rc < 0) {
362 fprintf(stderr,"fanctl: Error opening system bus.\n");
363 return rc;
364 }
365
366 /* Install the object */
367 rc = sd_bus_add_object_vtable(info->bus,
368 &fan_slot,
369 fan_object, /* object path */
370 "org.openbmc.control.Fans", /* interface name */
371 fan_control_vtable,
372 info);
373 if (rc < 0) {
374 fprintf(stderr, "fanctl: Failed to add object to dbus: %s\n",
375 strerror(-rc));
376 return rc;
377 }
378
379 /* If we had success in adding the providers, request for a bus name. */
380 rc = sd_bus_request_name(info->bus,
381 "org.openbmc.control.Fans", 0);
382 if (rc < 0) {
383 fprintf(stderr, "fanctl: Failed to acquire service name: %s\n",
384 strerror(-rc));
385 return rc;
386 }
387
388 for (;;) {
389 /* Process requests */
390 rc = sd_bus_process(info->bus, NULL);
391 if (rc < 0) {
392 fprintf(stderr, "fanctl: Failed to process bus: %s\n",
393 strerror(-rc));
394 break;
395 }
396 if (rc > 0) {
397 continue;
398 }
399
400 rc = sd_bus_wait(info->bus, (uint64_t) - 1);
401 if (rc < 0) {
402 fprintf(stderr, "fanctl: Failed to wait on bus: %s\n",
403 strerror(-rc));
404 break;
405 }
406 }
407
408 sd_bus_slot_unref(fan_slot);
409 sd_bus_unref(info->bus);
410
411 return rc;
412}
413
414static int str_to_int(char *str)
415{
416 long val;
417 char *temp;
418
419 val = strtol(str, &temp, 10);
420 if (temp == str || *temp != '\0' ||
421 ((val == LONG_MIN || val == LONG_MAX) && errno == ERANGE))
422 return -1;
423 if (val < 0)
424 return -1;
425
426 return (int)val;
427}
428
429static int parse_argument(int argc, char **argv, fan_info_t *info)
430{
431 int c;
432 struct option long_options[] =
433 {
434 {"fan_num", required_argument, 0, 'f'},
435 {"core_num", required_argument, 0, 'c'},
436 {"cpu_num", required_argument, 0, 'p'},
437 {"dimm_num", required_argument, 0, 'd'},
438 {0, 0, 0, 0}
439 };
440
441 while (1) {
442 c = getopt_long (argc, argv, "c:d:f:p:", long_options, NULL);
443
444 /* Detect the end of the options. */
445 if (c == -1)
446 break;
447
448 switch (c) {
449 case 'f':
450 info->fan_num = str_to_int(optarg);
451 if (info->fan_num == -1) {
452 fprintf(stderr, "fanctl: Wrong fan_num: %s\n", optarg);
453 return -1;
454 }
455 break;
456 case 'c':
457 info->core_num = str_to_int(optarg);
458 if (info->core_num == -1) {
459 fprintf(stderr, "fanctl: Wrong core_num: %s\n", optarg);
460 return -1;
461 }
462 break;
463 case 'p':
464 info->cpu_num = str_to_int(optarg);
465 if (info->cpu_num == -1) {
466 fprintf(stderr, "fanctl: Wrong cpu_num: %s\n", optarg);
467 return -1;
468 }
469 break;
470 case 'd':
471 info->dimm_num = str_to_int(optarg);
472 if (info->dimm_num == -1) {
473 fprintf(stderr, "fanctl: Wrong dimm_num: %s\n", optarg);
474 return -1;
475 }
476 break;
477 default:
478 fprintf(stderr, "fanctl: Wrong argument\n");
479 return -1;
480 }
481 }
482
483 return 0;
484}
485
486int main(int argc, char **argv)
487{
488 int rc = 0;
489 fan_info_t fan_info;
490
491 memset(&fan_info, 0, sizeof(fan_info));
492 rc = parse_argument(argc, argv, &fan_info);
493 if (rc < 0) {
494 fprintf(stderr, "fanctl: Error parse argument\n");
495 return rc;
496 }
497 /* This call is not supposed to return. If it does, then an error */
498 rc = start_fan_services(&fan_info);
499 if (rc < 0) {
500 fprintf(stderr, "fanctl: Error starting FAN Services. Exiting");
501 }
502
503 return rc;
504}