]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/pwm/pwm.c
Explain the relationship between PWM hardware channels being controlled and
[FreeBSD/FreeBSD.git] / usr.sbin / pwm / pwm.c
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] -E\n");
70         fprintf(stderr, "\tpwm [-f dev] -D\n");
71         fprintf(stderr, "\tpwm [-f dev] -C\n");
72         fprintf(stderr, "\tpwm [-f dev] -p period\n");
73         fprintf(stderr, "\tpwm [-f dev] -d duty\n");
74         exit(1);
75 }
76
77 int
78 main(int argc, char *argv[])
79 {
80         struct pwm_state state;
81         int fd;
82         int period, duty;
83         int action, ch;
84         cap_rights_t right_ioctl;
85         const unsigned long pwm_ioctls[] = {PWMGETSTATE, PWMSETSTATE};
86         char *percent;
87         bool setname;
88
89         action = 0;
90         setname = false;
91         fd = -1;
92         period = duty = -1;
93
94         while ((ch = getopt(argc, argv, "f:EDCp:d:")) != -1) {
95                 switch (ch) {
96                 case 'E':
97                         if (action)
98                                 usage();
99                         action = PWM_ENABLE;
100                         break;
101                 case 'D':
102                         if (action)
103                                 usage();
104                         action = PWM_DISABLE;
105                         break;
106                 case 'C':
107                         if (action)
108                                 usage();
109                         action = PWM_SHOW_CONFIG;
110                         break;
111                 case 'p':
112                         if (action & ~(PWM_PERIOD | PWM_DUTY))
113                                 usage();
114                         action = PWM_PERIOD;
115                         period = strtol(optarg, NULL, 10);
116                         break;
117                 case 'd':
118                         if (action & ~(PWM_PERIOD | PWM_DUTY))
119                                 usage();
120                         action = PWM_DUTY;
121                         duty = strtol(optarg, &percent, 10);
122                         if (*percent != '\0' && *percent != '%')
123                                 usage();
124                         break;
125                 case 'f':
126                         setname = true;
127                         set_device_name(optarg);
128                         break;
129                 case '?':
130                         usage();
131                         break;
132                 }
133         }
134
135         if (action == 0)
136                 usage();
137
138         if ((fd = open(device_name, O_RDWR)) == -1) {
139                 fprintf(stderr, "pwm: cannot open %s: %s\n",
140                     device_name, strerror(errno));
141                 if (setname)
142                         exit(1);
143                 else
144                         usage();
145         }
146
147         if (caph_limit_stdio() < 0) {
148                 fprintf(stderr, "can't limit stdio rights");
149                 goto fail;
150         }
151         caph_cache_catpages();
152         cap_rights_init(&right_ioctl, CAP_IOCTL);
153         if (caph_rights_limit(fd, &right_ioctl) < 0) {
154                 fprintf(stderr, "cap_right_limit() failed\n");
155                 goto fail;
156         }
157         if (caph_ioctls_limit(fd, pwm_ioctls, nitems(pwm_ioctls)) < 0) {
158                 fprintf(stderr, "caph_ioctls_limit() failed\n");
159                 goto fail;
160         }
161         if (caph_enter() < 0) {
162                 fprintf(stderr, "failed to enter capability mode\n");
163                 goto fail;
164         }
165
166         /* Fill the common args */
167         if (ioctl(fd, PWMGETSTATE, &state) == -1) {
168                 fprintf(stderr, "Cannot get current state of the pwm controller\n");
169                 goto fail;
170         }
171
172         switch (action) {
173         case PWM_ENABLE:
174                 if (state.enable == false) {
175                         state.enable = true;
176                         if (ioctl(fd, PWMSETSTATE, &state) == -1) {
177                                 fprintf(stderr,
178                                     "Cannot enable the pwm controller\n");
179                                 goto fail;
180                         }
181                 }
182                 break;
183         case PWM_DISABLE:
184                 if (state.enable == true) {
185                         state.enable = false;
186                         if (ioctl(fd, PWMSETSTATE, &state) == -1) {
187                                 fprintf(stderr,
188                                     "Cannot disable the pwm controller\n");
189                                 goto fail;
190                         }
191                 }
192                 break;
193         case PWM_SHOW_CONFIG:
194                 printf("period: %u\nduty: %u\nenabled:%d\n",
195                     state.period,
196                     state.duty,
197                     state.enable);
198                 break;
199         case PWM_PERIOD:
200         case PWM_DUTY:
201                 if (period != -1)
202                         state.period = period;
203                 if (duty != -1) {
204                         if (*percent != '\0')
205                                 state.duty = state.period * duty / 100;
206                         else
207                                 state.duty = duty;
208                 }
209                 if (ioctl(fd, PWMSETSTATE, &state) == -1) {
210                         fprintf(stderr,
211                           "Cannot configure the pwm controller\n");
212                         goto fail;
213                 }
214                 break;
215         }
216
217         close(fd);
218         return (0);
219
220 fail:
221         close(fd);
222         return (1);
223 }