xref: /freebsd/usr.sbin/pwm/pwm.c (revision 0957b409)
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/pwm.h>
35 #include <sys/capsicum.h>
36 
37 #include <err.h>
38 #include <errno.h>
39 #include <fcntl.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44 #include <capsicum_helpers.h>
45 
46 #define	PWM_ENABLE	0x0001
47 #define	PWM_DISABLE	0x0002
48 #define	PWM_SHOW_CONFIG	0x0004
49 #define	PWM_PERIOD	0x0008
50 #define	PWM_DUTY	0x0010
51 
52 static void
53 usage(void)
54 {
55 	fprintf(stderr, "Usage:\n");
56 	fprintf(stderr, "\tpwm [-f dev] -c channel -E\n");
57 	fprintf(stderr, "\tpwm [-f dev] -c channel -D\n");
58 	fprintf(stderr, "\tpwm [-f dev] -c channel -C\n");
59 	fprintf(stderr, "\tpwm [-f dev] -c channel -p period\n");
60 	fprintf(stderr, "\tpwm [-f dev] -c channel -d duty\n");
61 	exit(1);
62 }
63 
64 int
65 main(int argc, char *argv[])
66 {
67 	struct pwm_state state;
68 	int fd;
69 	int channel, nchannels;
70 	int period, duty;
71 	int action, ch;
72 	cap_rights_t right_ioctl;
73 	const unsigned long pwm_ioctls[] = {PWMGETSTATE, PWMSETSTATE, PWMMAXCHANNEL};
74 	char *percent;
75 
76 	action = 0;
77 	fd = -1;
78 	channel = -1;
79 	period = duty = -1;
80 
81 	while ((ch = getopt(argc, argv, "f:c:EDCp:d:")) != -1) {
82 		switch (ch) {
83 		case 'E':
84 			if (action)
85 				usage();
86 			action = PWM_ENABLE;
87 			break;
88 		case 'D':
89 			if (action)
90 				usage();
91 			action = PWM_DISABLE;
92 			break;
93 		case 'C':
94 			if (action)
95 				usage();
96 			action = PWM_SHOW_CONFIG;
97 			break;
98 		case 'p':
99 			if (action & ~(PWM_PERIOD | PWM_DUTY))
100 				usage();
101 			action = PWM_PERIOD;
102 			period = strtol(optarg, NULL, 10);
103 			break;
104 		case 'd':
105 			if (action & ~(PWM_PERIOD | PWM_DUTY))
106 				usage();
107 			action = PWM_DUTY;
108 			duty = strtol(optarg, &percent, 10);
109 			if (*percent != '\0' && *percent != '%')
110 				usage();
111 			break;
112 		case 'c':
113 			if (channel != -1)
114 				usage();
115 			channel = strtol(optarg, NULL, 10);
116 			break;
117 		case 'f':
118 			if ((fd = open(optarg, O_RDWR)) < 0) {
119 				fprintf(stderr, "pwm: cannot open %s %s\n",
120 				  optarg, strerror(errno));
121 				exit(1);
122 			}
123 		}
124 	}
125 
126 	if (fd == -1) {
127 		if ((fd = open("/dev/pwmc0", O_RDWR)) < 0) {
128 			fprintf(stderr, "pwm: cannot open %s %s\n",
129 			    optarg, strerror(errno));
130 			exit(1);
131 		}
132 	}
133 
134 	if (action == 0 || fd == -1)
135 		usage();
136 
137 	if (caph_limit_stdio() < 0) {
138 		fprintf(stderr, "can't limit stdio rights");
139 		goto fail;
140 	}
141 	caph_cache_catpages();
142 	cap_rights_init(&right_ioctl, CAP_IOCTL);
143 	if (caph_rights_limit(fd, &right_ioctl) < 0) {
144 		fprintf(stderr, "cap_right_limit() failed\n");
145 		goto fail;
146 	}
147 	if (caph_ioctls_limit(fd, pwm_ioctls, nitems(pwm_ioctls)) < 0) {
148 		fprintf(stderr, "caph_ioctls_limit() failed\n");
149 		goto fail;
150 	}
151 	if (caph_enter() < 0) {
152 		fprintf(stderr, "failed to enter capability mode\n");
153 		goto fail;
154 	}
155 
156 	/* Check if the channel is correct */
157 	if (ioctl(fd, PWMMAXCHANNEL, &nchannels) == -1) {
158 		fprintf(stderr, "ioctl: %s\n", strerror(errno));
159 		goto fail;
160 	}
161 	if (channel > nchannels) {
162 		fprintf(stderr, "pwm controller only support %d channels\n",
163 		    nchannels);
164 		goto fail;
165 	}
166 
167 	/* Fill the common args */
168 	state.channel = channel;
169 	if (ioctl(fd, PWMGETSTATE, &state) == -1) {
170 		fprintf(stderr, "Cannot get current state of the pwm controller\n");
171 		goto fail;
172 	}
173 
174 	switch (action) {
175 	case PWM_ENABLE:
176 		if (state.enable == false) {
177 			state.enable = true;
178 			if (ioctl(fd, PWMSETSTATE, &state) == -1) {
179 				fprintf(stderr,
180 				    "Cannot enable the pwm controller\n");
181 				goto fail;
182 			}
183 		}
184 		break;
185 	case PWM_DISABLE:
186 		if (state.enable == true) {
187 			state.enable = false;
188 			if (ioctl(fd, PWMSETSTATE, &state) == -1) {
189 				fprintf(stderr,
190 				    "Cannot disable the pwm controller\n");
191 				goto fail;
192 			}
193 		}
194 		break;
195 	case PWM_SHOW_CONFIG:
196 		printf("period: %u\nduty: %u\nenabled:%d\n",
197 		    state.period,
198 		    state.duty,
199 		    state.enable);
200 		break;
201 	case PWM_PERIOD:
202 	case PWM_DUTY:
203 		if (period != -1)
204 			state.period = period;
205 		if (duty != -1) {
206 			if (*percent != '\0')
207 				state.duty = state.period * duty / 100;
208 			else
209 				state.duty = duty;
210 		}
211 		if (ioctl(fd, PWMSETSTATE, &state) == -1) {
212 			fprintf(stderr,
213 			  "Cannot configure the pwm controller\n");
214 			goto fail;
215 		}
216 		break;
217 	}
218 
219 	close(fd);
220 	return (0);
221 
222 fail:
223 	close(fd);
224 	return (1);
225 }
226