xref: /freebsd/usr.sbin/pwm/pwm.c (revision c93b6e5fa24ba172ab271432c6692f9cc604e15a)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2018 Emmanuel Vadot <manu@FreeBSD.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  * $FreeBSD$
29  */
30 
31 #include <sys/types.h>
32 #include <sys/ioctl.h>
33 #include <stdbool.h>
34 #include <sys/capsicum.h>
35 #include <dev/pwm/pwmc.h>
36 
37 #include <err.h>
38 #include <errno.h>
39 #include <fcntl.h>
40 #include <limits.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <unistd.h>
45 #include <capsicum_helpers.h>
46 
47 #define	PWM_ENABLE	0x0001
48 #define	PWM_DISABLE	0x0002
49 #define	PWM_SHOW_CONFIG	0x0004
50 #define	PWM_PERIOD	0x0008
51 #define	PWM_DUTY	0x0010
52 
53 static char device_name[PATH_MAX] = "/dev/pwm/pwmc0.0";
54 
55 static void
56 set_device_name(const char *name)
57 {
58 
59 	if (name[0] == '/')
60 		strlcpy(device_name, name, sizeof(device_name));
61 	else
62 		snprintf(device_name, sizeof(device_name), "/dev/pwm/%s", name);
63 }
64 
65 static void
66 usage(void)
67 {
68 	fprintf(stderr, "Usage:\n");
69 	fprintf(stderr, "\tpwm [-f dev] -C\n");
70 	fprintf(stderr, "\tpwm [-f dev] [-D | -E] [-p period] [-d duty[%%]]\n");
71 	exit(1);
72 }
73 
74 int
75 main(int argc, char *argv[])
76 {
77 	struct pwm_state state;
78 	int fd;
79 	int period, duty;
80 	int action, ch;
81 	cap_rights_t right_ioctl;
82 	const unsigned long pwm_ioctls[] = {PWMGETSTATE, PWMSETSTATE};
83 	char *percent;
84 	bool setname;
85 
86 	action = 0;
87 	setname = false;
88 	fd = -1;
89 	period = duty = -1;
90 
91 	while ((ch = getopt(argc, argv, "f:EDCp:d:")) != -1) {
92 		switch (ch) {
93 		case 'E':
94 			if (action & (PWM_DISABLE | PWM_SHOW_CONFIG))
95 				usage();
96 			action |= PWM_ENABLE;
97 			break;
98 		case 'D':
99 			if (action & (PWM_ENABLE | PWM_SHOW_CONFIG))
100 				usage();
101 			action |= PWM_DISABLE;
102 			break;
103 		case 'C':
104 			if (action)
105 				usage();
106 			action = PWM_SHOW_CONFIG;
107 			break;
108 		case 'p':
109 			if (action & PWM_SHOW_CONFIG)
110 				usage();
111 			action |= PWM_PERIOD;
112 			period = strtol(optarg, NULL, 10);
113 			break;
114 		case 'd':
115 			if (action & PWM_SHOW_CONFIG)
116 				usage();
117 			action |= PWM_DUTY;
118 			duty = strtol(optarg, &percent, 10);
119 			if (*percent == '%') {
120 				if (duty < 0 || duty > 100) {
121 					fprintf(stderr,
122 					    "Invalid duty percentage\n");
123 					usage();
124 				}
125 			} else if (*percent != '\0')
126 				usage();
127 			break;
128 		case 'f':
129 			setname = true;
130 			set_device_name(optarg);
131 			break;
132 		case '?':
133 			usage();
134 			break;
135 		}
136 	}
137 
138 	if (action == 0)
139 		usage();
140 
141 	if ((fd = open(device_name, O_RDWR)) == -1) {
142 		fprintf(stderr, "pwm: cannot open %s: %s\n",
143 		    device_name, strerror(errno));
144 		if (setname)
145 			exit(1);
146 		else
147 			usage();
148 	}
149 
150 	if (caph_limit_stdio() < 0) {
151 		fprintf(stderr, "can't limit stdio rights");
152 		goto fail;
153 	}
154 	caph_cache_catpages();
155 	cap_rights_init(&right_ioctl, CAP_IOCTL);
156 	if (caph_rights_limit(fd, &right_ioctl) < 0) {
157 		fprintf(stderr, "cap_right_limit() failed\n");
158 		goto fail;
159 	}
160 	if (caph_ioctls_limit(fd, pwm_ioctls, nitems(pwm_ioctls)) < 0) {
161 		fprintf(stderr, "caph_ioctls_limit() failed\n");
162 		goto fail;
163 	}
164 	if (caph_enter() < 0) {
165 		fprintf(stderr, "failed to enter capability mode\n");
166 		goto fail;
167 	}
168 
169 	/* Fill the common args */
170 	if (ioctl(fd, PWMGETSTATE, &state) == -1) {
171 		fprintf(stderr, "Cannot get current state of the pwm controller\n");
172 		goto fail;
173 	}
174 
175 	if (action == PWM_SHOW_CONFIG) {
176 		printf("period: %u\nduty: %u\nenabled:%d\n",
177 		    state.period,
178 		    state.duty,
179 		    state.enable);
180 		goto fail;
181 	} else {
182 		if (action & PWM_ENABLE)
183 			state.enable = true;
184 		if (action & PWM_DISABLE)
185 			state.enable = false;
186 		if (action & PWM_PERIOD)
187 			state.period = period;
188 		if (action & PWM_DUTY) {
189 			if (*percent != '\0')
190 				state.duty = state.period * duty / 100;
191 			else
192 				state.duty = duty;
193 		}
194 
195 		if (ioctl(fd, PWMSETSTATE, &state) == -1) {
196 			fprintf(stderr,
197 			  "Cannot configure the pwm controller\n");
198 			goto fail;
199 		}
200 	}
201 
202 	close(fd);
203 	return (0);
204 
205 fail:
206 	close(fd);
207 	return (1);
208 }
209