blob: 2a2fe9f63c0ff4056d110d69531f8e0a6ce3f450 [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,
Adriana Kobylakc6fa1dd2018-08-06 14:43:55 -050055 "ss",
56 obj_path,
57 "");
Yi Li2f3213f2016-08-03 11:09:55 +080058 if (rc < 0) {
59 fprintf(stderr,
60 "Failed to GetObject: %s\n", bus_error.message);
61 goto finish;
62 }
63
64 /* Get the key, aka, the bus name */
65 sd_bus_message_read(m, "a{sas}", 1, &temp_buf, 1, &intf);
66 strncpy(connection, temp_buf, DBUS_MAX_NAME_LEN);
67
68finish:
69 sd_bus_error_free(&bus_error);
70 sd_bus_message_unref(m);
71 sd_bus_flush(bus);
72
73 return rc;
74}
75
76
77int set_dbus_sensor(sd_bus *bus, const char *obj_path, int val)
78{
79 char connection[DBUS_MAX_NAME_LEN];
80 sd_bus_error bus_error = SD_BUS_ERROR_NULL;
81 sd_bus_message *response = NULL;
82 int rc;
83
84 if (!bus || !obj_path)
85 return -1;
86
87 rc = get_connection(bus, connection, obj_path);
88 if (rc < 0) {
89 fprintf(stderr,
90 "fanctl: Failed to get bus name for %s\n", obj_path);
91 goto finish;
92 }
93
Adriana Kobylakc6fa1dd2018-08-06 14:43:55 -050094 rc = sd_bus_set_property(bus,
Yi Li2f3213f2016-08-03 11:09:55 +080095 connection,
96 obj_path,
Adriana Kobylakc6fa1dd2018-08-06 14:43:55 -050097 "xyz.openbmc_project.Control.FanSpeed",
98 "Target",
Yi Li2f3213f2016-08-03 11:09:55 +080099 &bus_error,
Yi Li2f3213f2016-08-03 11:09:55 +0800100 "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
Adriana Kobylakc6fa1dd2018-08-06 14:43:55 -0500115/* Read sensor value from "xyz.openbmc_projects.Sensors" */
116int read_dbus_sensor(sd_bus *bus, const char *obj_path, const char *property)
Yi Li2f3213f2016-08-03 11:09:55 +0800117{
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
Adriana Kobylakc6fa1dd2018-08-06 14:43:55 -0500135 rc = sd_bus_get_property(bus,
Yi Li2f3213f2016-08-03 11:09:55 +0800136 connection,
137 obj_path,
Adriana Kobylakc6fa1dd2018-08-06 14:43:55 -0500138 "xyz.openbmc_project.Sensor.Value",
139 property,
Yi Li2f3213f2016-08-03 11:09:55 +0800140 &bus_error,
141 &response,
Adriana Kobylakc6fa1dd2018-08-06 14:43:55 -0500142 "i");
Yi Li2f3213f2016-08-03 11:09:55 +0800143 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
Adriana Kobylakc6fa1dd2018-08-06 14:43:55 -0500151 rc = sd_bus_message_read(response, "i", &val);
Yi Li2f3213f2016-08-03 11:09:55 +0800152 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
Adriana Kobylakc6fa1dd2018-08-06 14:43:55 -0500168/* set fan speed with /xyz/openbmc_project/sensors/fan_tach/fan* object */
Yi Li2f3213f2016-08-03 11:09:55 +0800169static 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),
Adriana Kobylakc6fa1dd2018-08-06 14:43:55 -0500178 "/xyz/openbmc_project/sensors/fan_tach/fan%d_0", fan_id);
Yi Li2f3213f2016-08-03 11:09:55 +0800179 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 */
Yi Li2f3213f2016-08-03 11:09:55 +0800218 snprintf(obj_path, sizeof(obj_path),
Adriana Kobylakc6fa1dd2018-08-06 14:43:55 -0500219 "/xyz/openbmc_project/sensors/fan_tach/fan%d_0", fan_id);
220 fan_tach_H = read_dbus_sensor(bus, obj_path, "MaxValue");
221 fan_tach_L = read_dbus_sensor(bus, obj_path, "MinValue");
Yi Li2f3213f2016-08-03 11:09:55 +0800222
223 /* invalid sensor value is -1 */
224 if (fan_tach_H <= 0 || fan_tach_L <= 0)
225 fan_speed = 0;
226 else
227 fan_speed = fan_tach_H << FAN_TACH_OFFSET | fan_tach_L;
228
229 fprintf(stderr, "fan%d speed: %d\n", fan_id, fan_speed);
230 return fan_speed;
231}
232
233/* set Fan Inventory 'Present' status */
234int fan_set_present(sd_bus *bus, int fan_id, int val)
235{
236 sd_bus_error bus_error = SD_BUS_ERROR_NULL;
237 sd_bus_message *response = NULL;
238 int rc;
239 char obj_path[DBUS_MAX_NAME_LEN];
240 char connection[DBUS_MAX_NAME_LEN];
241
242 snprintf(obj_path, sizeof(obj_path),
Adriana Kobylakc6fa1dd2018-08-06 14:43:55 -0500243 "/xyz/openbmc_project/inventory/system/chassis/motherboard/fan%d",
244 fan_id);
Yi Li2f3213f2016-08-03 11:09:55 +0800245
246 rc = get_connection(bus, connection, obj_path);
247 if (rc < 0) {
248 fprintf(stderr,
249 "fanctl: Failed to get bus name for %s\n", obj_path);
250 goto finish;
251 }
252
Adriana Kobylakc6fa1dd2018-08-06 14:43:55 -0500253 rc = sd_bus_set_property(bus,
Yi Li2f3213f2016-08-03 11:09:55 +0800254 connection,
255 obj_path,
Adriana Kobylakc6fa1dd2018-08-06 14:43:55 -0500256 "xyz.openbmc_project.Inventory.Item",
257 "Present",
Yi Li2f3213f2016-08-03 11:09:55 +0800258 &bus_error,
Adriana Kobylakc6fa1dd2018-08-06 14:43:55 -0500259 "b",
260 val);
Yi Li2f3213f2016-08-03 11:09:55 +0800261 if(rc < 0)
262 fprintf(stderr,
263 "fanctl: Failed to update fan presence via dbus: %s\n",
264 bus_error.message);
265
266 fprintf(stderr, "fanctl: Set fan%d present status to: %s\n",
267 fan_id, (val == 1 ? "True" : "False"));
268
269finish:
270 sd_bus_error_free(&bus_error);
271 sd_bus_message_unref(response);
272 sd_bus_flush(bus);
273
274 return rc;
275}
276
277/*
278 * Update Fan Invertory 'Present' status by first reading fan speed.
279 * If fan speed is '0', the fan is considerred not 'Present'.
280 */
281static int fan_update_present(fan_info_t *info)
282{
283 int i;
284 int rc = -1;
285 int fan_speed;
286
287 if (!info)
288 return -1;
289
290 for (i = 0; i < info->fan_num; i++) {
291 fan_speed = fan_get_speed(info->bus, i);
292 if (fan_speed > 0)
293 rc = fan_set_present(info->bus, i, 1);
294 else
295 rc = fan_set_present(info->bus, i, 0);
296
297 if (rc < 0) {
298 fprintf(stderr,
299 "fanctl: Failed to set fan present status\n");
300 break;
301 }
302 }
303
304 return rc;
305}
306/*
307 * Router function for any FAN operations that come via dbus
308 */
309static int fan_function_router(sd_bus_message *msg, void *user_data,
310 sd_bus_error *ret_error)
311{
312 /* Generic error reporter. */
313 int rc = -1;
314 fan_info_t *info = user_data;
315
316 /* Get the Operation. */
317 const char *fan_function = sd_bus_message_get_member(msg);
318 if (fan_function == NULL) {
Gunnar Mills9d572eb2018-04-08 14:30:41 -0500319 fprintf(stderr, "fanctl: Null FAN function specified\n");
Yi Li2f3213f2016-08-03 11:09:55 +0800320 return sd_bus_reply_method_return(msg, "i", rc);
321 }
322
323 /* Route the user action to appropriate handlers. */
324 if ((strcmp(fan_function, "setMax") == 0)) {
325 rc = fan_set_max_speed(info);
326 return sd_bus_reply_method_return(msg, "i", rc);
327 }
328 if ((strcmp(fan_function, "updatePresent") == 0)) {
329 rc = fan_update_present(info);
330 return sd_bus_reply_method_return(msg, "i", rc);
331 }
332
333 return sd_bus_reply_method_return(msg, "i", rc);
334}
335
336/* Dbus Services offered by this FAN controller */
337static const sd_bus_vtable fan_control_vtable[] =
338{
339 SD_BUS_VTABLE_START(0),
340 SD_BUS_METHOD("setMax", "", "i", &fan_function_router,
341 SD_BUS_VTABLE_UNPRIVILEGED),
342 SD_BUS_METHOD("updatePresent", "", "i", &fan_function_router,
343 SD_BUS_VTABLE_UNPRIVILEGED),
344 SD_BUS_VTABLE_END,
345};
346
347int start_fan_services(fan_info_t *info)
348{
349 /* Generic error reporter. */
350 int rc = -1;
351 /* slot where we are offering the FAN dbus service. */
352 sd_bus_slot *fan_slot = NULL;
353 const char *fan_object = "/org/openbmc/control/fans";
354
355 info->bus = NULL;
356 /* Get a hook onto system bus. */
357 rc = sd_bus_open_system(&info->bus);
358 if (rc < 0) {
359 fprintf(stderr,"fanctl: Error opening system bus.\n");
360 return rc;
361 }
362
363 /* Install the object */
364 rc = sd_bus_add_object_vtable(info->bus,
365 &fan_slot,
366 fan_object, /* object path */
367 "org.openbmc.control.Fans", /* interface name */
368 fan_control_vtable,
369 info);
370 if (rc < 0) {
371 fprintf(stderr, "fanctl: Failed to add object to dbus: %s\n",
372 strerror(-rc));
373 return rc;
374 }
375
376 /* If we had success in adding the providers, request for a bus name. */
377 rc = sd_bus_request_name(info->bus,
378 "org.openbmc.control.Fans", 0);
379 if (rc < 0) {
380 fprintf(stderr, "fanctl: Failed to acquire service name: %s\n",
381 strerror(-rc));
382 return rc;
383 }
384
385 for (;;) {
386 /* Process requests */
387 rc = sd_bus_process(info->bus, NULL);
388 if (rc < 0) {
389 fprintf(stderr, "fanctl: Failed to process bus: %s\n",
390 strerror(-rc));
391 break;
392 }
393 if (rc > 0) {
394 continue;
395 }
396
397 rc = sd_bus_wait(info->bus, (uint64_t) - 1);
398 if (rc < 0) {
399 fprintf(stderr, "fanctl: Failed to wait on bus: %s\n",
400 strerror(-rc));
401 break;
402 }
403 }
404
405 sd_bus_slot_unref(fan_slot);
406 sd_bus_unref(info->bus);
407
408 return rc;
409}
410
411static int str_to_int(char *str)
412{
413 long val;
414 char *temp;
415
416 val = strtol(str, &temp, 10);
417 if (temp == str || *temp != '\0' ||
418 ((val == LONG_MIN || val == LONG_MAX) && errno == ERANGE))
419 return -1;
420 if (val < 0)
421 return -1;
422
423 return (int)val;
424}
425
426static int parse_argument(int argc, char **argv, fan_info_t *info)
427{
428 int c;
429 struct option long_options[] =
430 {
431 {"fan_num", required_argument, 0, 'f'},
432 {"core_num", required_argument, 0, 'c'},
433 {"cpu_num", required_argument, 0, 'p'},
434 {"dimm_num", required_argument, 0, 'd'},
435 {0, 0, 0, 0}
436 };
437
438 while (1) {
439 c = getopt_long (argc, argv, "c:d:f:p:", long_options, NULL);
440
441 /* Detect the end of the options. */
442 if (c == -1)
443 break;
444
445 switch (c) {
446 case 'f':
447 info->fan_num = str_to_int(optarg);
448 if (info->fan_num == -1) {
449 fprintf(stderr, "fanctl: Wrong fan_num: %s\n", optarg);
450 return -1;
451 }
452 break;
453 case 'c':
454 info->core_num = str_to_int(optarg);
455 if (info->core_num == -1) {
456 fprintf(stderr, "fanctl: Wrong core_num: %s\n", optarg);
457 return -1;
458 }
459 break;
460 case 'p':
461 info->cpu_num = str_to_int(optarg);
462 if (info->cpu_num == -1) {
463 fprintf(stderr, "fanctl: Wrong cpu_num: %s\n", optarg);
464 return -1;
465 }
466 break;
467 case 'd':
468 info->dimm_num = str_to_int(optarg);
469 if (info->dimm_num == -1) {
470 fprintf(stderr, "fanctl: Wrong dimm_num: %s\n", optarg);
471 return -1;
472 }
473 break;
474 default:
475 fprintf(stderr, "fanctl: Wrong argument\n");
476 return -1;
477 }
478 }
479
480 return 0;
481}
482
483int main(int argc, char **argv)
484{
485 int rc = 0;
486 fan_info_t fan_info;
487
488 memset(&fan_info, 0, sizeof(fan_info));
489 rc = parse_argument(argc, argv, &fan_info);
490 if (rc < 0) {
491 fprintf(stderr, "fanctl: Error parse argument\n");
492 return rc;
493 }
494 /* This call is not supposed to return. If it does, then an error */
495 rc = start_fan_services(&fan_info);
496 if (rc < 0) {
497 fprintf(stderr, "fanctl: Error starting FAN Services. Exiting");
498 }
499
500 return rc;
501}