1249257Smarkj/*-
2249257Smarkj * Copyright (c) 2013 Sandvine Inc.
3249257Smarkj * All rights reserved.
4249257Smarkj *
5249257Smarkj * Redistribution and use in source and binary forms, with or without
6249257Smarkj * modification, are permitted provided that the following conditions
7249257Smarkj * are met:
8249257Smarkj * 1. Redistributions of source code must retain the above copyright
9249257Smarkj *    notice, this list of conditions and the following disclaimer.
10249257Smarkj * 2. Redistributions in binary form must reproduce the above copyright
11249257Smarkj *    notice, this list of conditions and the following disclaimer in the
12249257Smarkj *    documentation and/or other materials provided with the distribution.
13249257Smarkj *
14249257Smarkj * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15249257Smarkj * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16249257Smarkj * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17249257Smarkj * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18249257Smarkj * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19249257Smarkj * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20249257Smarkj * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21249257Smarkj * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22249257Smarkj * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23249257Smarkj * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24249257Smarkj * SUCH DAMAGE.
25249257Smarkj *
26249257Smarkj * $FreeBSD: releng/11.0/usr.sbin/mfiutil/mfi_bbu.c 249258 2013-04-08 18:25:07Z markj $
27249257Smarkj */
28249257Smarkj
29249257Smarkj#include <sys/param.h>
30249257Smarkj#include <sys/errno.h>
31249257Smarkj#include <sys/stat.h>
32249257Smarkj#include <err.h>
33249257Smarkj#include <fcntl.h>
34249257Smarkj#include <stdio.h>
35249257Smarkj#include <stdlib.h>
36249257Smarkj#include <string.h>
37249257Smarkj#include <time.h>
38249257Smarkj#include <unistd.h>
39249257Smarkj#include "mfiutil.h"
40249257Smarkj
41249257Smarkj/* The autolearn period is given in seconds. */
42249257Smarkjvoid
43249257Smarkjmfi_autolearn_period(uint32_t period, char *buf, size_t sz)
44249257Smarkj{
45249257Smarkj	unsigned int d, h;
46249257Smarkj	char *tmp;
47249257Smarkj
48249257Smarkj	d = period / (24 * 3600);
49249257Smarkj	h = (period % (24 * 3600)) / 3600;
50249257Smarkj
51249257Smarkj	tmp = buf;
52249257Smarkj	if (d != 0) {
53249257Smarkj		tmp += snprintf(buf, sz, "%u day%s", d, d == 1 ? "" : "s");
54249257Smarkj		sz -= tmp - buf;
55249257Smarkj		if (h != 0) {
56249257Smarkj			tmp += snprintf(tmp, sz, ", ");
57249257Smarkj			sz -= 2;
58249257Smarkj		}
59249257Smarkj	}
60249257Smarkj	if (h != 0)
61249257Smarkj		snprintf(tmp, sz, "%u hour%s", h, h == 1 ? "" : "s");
62249257Smarkj
63249257Smarkj	if (d == 0 && h == 0)
64249257Smarkj		snprintf(tmp, sz, "less than 1 hour");
65249257Smarkj}
66249257Smarkj
67249257Smarkj/* The time to the next relearn is given in seconds since 1/1/2000. */
68249257Smarkjvoid
69249257Smarkjmfi_next_learn_time(uint32_t next_learn_time, char *buf, size_t sz)
70249257Smarkj{
71249257Smarkj	time_t basetime;
72249257Smarkj	struct tm tm;
73249257Smarkj	size_t len;
74249257Smarkj
75249257Smarkj	memset(&tm, 0, sizeof(tm));
76249257Smarkj	tm.tm_year = 100;
77249257Smarkj	basetime = timegm(&tm);
78249257Smarkj	basetime += (time_t)next_learn_time;
79249257Smarkj	len = snprintf(buf, sz, "%s", ctime(&basetime));
80249257Smarkj	if (len > 0)
81249257Smarkj		/* Get rid of the newline added by ctime(3). */
82249257Smarkj		buf[len - 1] = '\0';
83249257Smarkj}
84249257Smarkj
85249257Smarkjvoid
86249257Smarkjmfi_autolearn_mode(uint8_t mode, char *buf, size_t sz)
87249257Smarkj{
88249257Smarkj
89249257Smarkj	switch (mode) {
90249257Smarkj	case 0:
91249257Smarkj		snprintf(buf, sz, "enabled");
92249257Smarkj		break;
93249257Smarkj	case 1:
94249257Smarkj		snprintf(buf, sz, "disabled");
95249257Smarkj		break;
96249257Smarkj	case 2:
97249257Smarkj		snprintf(buf, sz, "warn via event");
98249257Smarkj		break;
99249257Smarkj	default:
100249257Smarkj		snprintf(buf, sz, "mode 0x%02x", mode);
101249257Smarkj		break;
102249257Smarkj	}
103249257Smarkj}
104249257Smarkj
105249257Smarkjint
106249257Smarkjmfi_bbu_get_props(int fd, struct mfi_bbu_properties *props, uint8_t *statusp)
107249257Smarkj{
108249257Smarkj
109249257Smarkj	return (mfi_dcmd_command(fd, MFI_DCMD_BBU_GET_PROP, props,
110249257Smarkj	    sizeof(*props), NULL, 0, statusp));
111249257Smarkj}
112249257Smarkj
113249257Smarkjint
114249257Smarkjmfi_bbu_set_props(int fd, struct mfi_bbu_properties *props, uint8_t *statusp)
115249257Smarkj{
116249257Smarkj
117249257Smarkj	return (mfi_dcmd_command(fd, MFI_DCMD_BBU_SET_PROP, props,
118249257Smarkj	    sizeof(*props), NULL, 0, statusp));
119249257Smarkj}
120249257Smarkj
121249257Smarkjstatic int
122249257Smarkjstart_bbu_learn(int ac, char **av __unused)
123249257Smarkj{
124249257Smarkj	uint8_t status;
125249257Smarkj	int error, fd;
126249257Smarkj
127249257Smarkj	status = MFI_STAT_OK;
128249257Smarkj	error = 0;
129249257Smarkj
130249257Smarkj	if (ac != 1) {
131249257Smarkj		warnx("start learn: unexpected arguments");
132249257Smarkj		return (EINVAL);
133249257Smarkj	}
134249257Smarkj
135249257Smarkj	fd = mfi_open(mfi_unit, O_RDWR);
136249257Smarkj	if (fd < 0) {
137249257Smarkj		error = errno;
138249257Smarkj		warn("mfi_open");
139249257Smarkj		return (error);
140249257Smarkj	}
141249257Smarkj
142249257Smarkj	if (mfi_dcmd_command(fd, MFI_DCMD_BBU_START_LEARN, NULL, 0, NULL, 0,
143249257Smarkj	    &status) < 0) {
144249257Smarkj		error = errno;
145249257Smarkj		warn("Failed to start BBU learn");
146249257Smarkj	} else if (status != MFI_STAT_OK) {
147249257Smarkj		warnx("Failed to start BBU learn: %s", mfi_status(status));
148249257Smarkj		error = EIO;
149249257Smarkj	}
150249257Smarkj
151249257Smarkj	return (error);
152249257Smarkj}
153249257SmarkjMFI_COMMAND(start, learn, start_bbu_learn);
154249257Smarkj
155249257Smarkjstatic int
156249257Smarkjupdate_bbu_props(int ac, char **av)
157249257Smarkj{
158249257Smarkj	struct mfi_bbu_properties props;
159249257Smarkj	unsigned long delay;
160249257Smarkj	uint8_t status;
161249257Smarkj	int error, fd;
162249257Smarkj	char *mode, *endptr;
163249257Smarkj
164249257Smarkj	status = MFI_STAT_OK;
165249257Smarkj	error = 0;
166249257Smarkj
167249257Smarkj	if (ac != 3) {
168249257Smarkj		warnx("bbu: property and value required");
169249257Smarkj		return (EINVAL);
170249257Smarkj	}
171249257Smarkj
172249257Smarkj	fd = mfi_open(mfi_unit, O_RDWR);
173249257Smarkj	if (fd < 0) {
174249257Smarkj		error = errno;
175249257Smarkj		warn("mfi_open");
176249257Smarkj		return (error);
177249257Smarkj	}
178249257Smarkj
179249257Smarkj	if (mfi_bbu_get_props(fd, &props, &status) < 0) {
180249257Smarkj		error = errno;
181249257Smarkj		warn("Failed to get BBU properties");
182249257Smarkj		goto done;
183249257Smarkj	} else if (status != MFI_STAT_OK) {
184249257Smarkj		warnx("Failed to get BBU properties: %s", mfi_status(status));
185249257Smarkj		error = EIO;
186249257Smarkj		goto done;
187249257Smarkj	}
188249257Smarkj
189249257Smarkj	if (strcmp(av[1], "learn-delay") == 0) {
190249257Smarkj		delay = strtoul(av[2], &endptr, 10);
191249257Smarkj		if (strlen(av[2]) == 0 || *endptr != '\0' || delay > 255) {
192249257Smarkj			warnx("Invalid learn delay '%s'", av[2]);
193249257Smarkj			error = EINVAL;
194249257Smarkj			goto done;
195249257Smarkj		}
196249257Smarkj
197249257Smarkj		props.learn_delay_interval = delay;
198249257Smarkj	} else if (strcmp(av[1], "autolearn-mode") == 0) {
199249257Smarkj		mode = av[2];
200249257Smarkj
201249257Smarkj		if (strcmp(av[2], "enable") == 0)
202249257Smarkj			props.auto_learn_mode = 0;
203249257Smarkj		else if (strcmp(av[2], "disable") == 0)
204249257Smarkj			props.auto_learn_mode = 1;
205249257Smarkj		else if (mode[0] >= '0' && mode[0] <= '2' && mode[1] == '\0')
206249257Smarkj			props.auto_learn_mode = mode[0] - '0';
207249257Smarkj		else {
208249257Smarkj			warnx("Invalid mode '%s'", mode);
209249257Smarkj			error = EINVAL;
210249257Smarkj			goto done;
211249257Smarkj		}
212249257Smarkj	} else if (strcmp(av[1], "bbu-mode") == 0) {
213249257Smarkj		if (props.bbu_mode == 0) {
214249257Smarkj			warnx("This BBU does not implement different modes");
215249257Smarkj			error = EINVAL;
216249257Smarkj			goto done;
217249257Smarkj		}
218249257Smarkj
219249257Smarkj		/* The mode must be an integer between 1 and 5. */
220249257Smarkj		mode = av[2];
221249257Smarkj		if (mode[0] < '1' || mode[0] > '5' || mode[1] != '\0') {
222249257Smarkj			warnx("Invalid mode '%s'", mode);
223249257Smarkj			error = EINVAL;
224249257Smarkj			goto done;
225249257Smarkj		}
226249257Smarkj
227249257Smarkj		props.bbu_mode = mode[0] - '0';
228249257Smarkj	} else {
229249257Smarkj		warnx("bbu: Invalid command '%s'", av[1]);
230249257Smarkj		error = EINVAL;
231249257Smarkj		goto done;
232249257Smarkj	}
233249257Smarkj
234249257Smarkj	if (mfi_bbu_set_props(fd, &props, &status) < 0) {
235249257Smarkj		error = errno;
236249257Smarkj		warn("Failed to set BBU properties");
237249257Smarkj		goto done;
238249257Smarkj	} else if (status != MFI_STAT_OK) {
239249257Smarkj		warnx("Failed to set BBU properties: %s", mfi_status(status));
240249257Smarkj		error = EIO;
241249257Smarkj		goto done;
242249257Smarkj	}
243249257Smarkj
244249257Smarkjdone:
245249257Smarkj	close(fd);
246249257Smarkj
247249257Smarkj	return (error);
248249257Smarkj}
249249257SmarkjMFI_COMMAND(top, bbu, update_bbu_props);
250