19312900fSEmmanuel Vadot /*- 29312900fSEmmanuel Vadot * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 39312900fSEmmanuel Vadot * 49312900fSEmmanuel Vadot * Copyright (c) 2018 Emmanuel Vadot <manu@FreeBSD.org> 59312900fSEmmanuel Vadot * All rights reserved. 69312900fSEmmanuel Vadot * 79312900fSEmmanuel Vadot * Redistribution and use in source and binary forms, with or without 89312900fSEmmanuel Vadot * modification, are permitted provided that the following conditions 99312900fSEmmanuel Vadot * are met: 109312900fSEmmanuel Vadot * 1. Redistributions of source code must retain the above copyright 119312900fSEmmanuel Vadot * notice, this list of conditions and the following disclaimer. 129312900fSEmmanuel Vadot * 2. Redistributions in binary form must reproduce the above copyright 139312900fSEmmanuel Vadot * notice, this list of conditions and the following disclaimer in the 149312900fSEmmanuel Vadot * documentation and/or other materials provided with the distribution. 159312900fSEmmanuel Vadot * 169312900fSEmmanuel Vadot * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 179312900fSEmmanuel Vadot * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 189312900fSEmmanuel Vadot * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 199312900fSEmmanuel Vadot * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 209312900fSEmmanuel Vadot * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 219312900fSEmmanuel Vadot * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 229312900fSEmmanuel Vadot * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 239312900fSEmmanuel Vadot * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 249312900fSEmmanuel Vadot * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 259312900fSEmmanuel Vadot * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 269312900fSEmmanuel Vadot * SUCH DAMAGE. 279312900fSEmmanuel Vadot * 289312900fSEmmanuel Vadot * $FreeBSD$ 299312900fSEmmanuel Vadot */ 309312900fSEmmanuel Vadot 319312900fSEmmanuel Vadot #include <sys/types.h> 329312900fSEmmanuel Vadot #include <sys/ioctl.h> 339312900fSEmmanuel Vadot #include <stdbool.h> 349312900fSEmmanuel Vadot #include <sys/pwm.h> 359312900fSEmmanuel Vadot #include <sys/capsicum.h> 369312900fSEmmanuel Vadot 379312900fSEmmanuel Vadot #include <err.h> 389312900fSEmmanuel Vadot #include <errno.h> 399312900fSEmmanuel Vadot #include <fcntl.h> 409312900fSEmmanuel Vadot #include <stdio.h> 419312900fSEmmanuel Vadot #include <stdlib.h> 429312900fSEmmanuel Vadot #include <string.h> 439312900fSEmmanuel Vadot #include <unistd.h> 449312900fSEmmanuel Vadot #include <capsicum_helpers.h> 459312900fSEmmanuel Vadot 469312900fSEmmanuel Vadot #define PWM_ENABLE 0x0001 479312900fSEmmanuel Vadot #define PWM_DISABLE 0x0002 489312900fSEmmanuel Vadot #define PWM_SHOW_CONFIG 0x0004 499312900fSEmmanuel Vadot #define PWM_PERIOD 0x0008 509312900fSEmmanuel Vadot #define PWM_DUTY 0x0010 519312900fSEmmanuel Vadot 529312900fSEmmanuel Vadot static void 539312900fSEmmanuel Vadot usage(void) 549312900fSEmmanuel Vadot { 559312900fSEmmanuel Vadot fprintf(stderr, "Usage:\n"); 569312900fSEmmanuel Vadot fprintf(stderr, "\tpwm [-f dev] -c channel -E\n"); 579312900fSEmmanuel Vadot fprintf(stderr, "\tpwm [-f dev] -c channel -D\n"); 589312900fSEmmanuel Vadot fprintf(stderr, "\tpwm [-f dev] -c channel -C\n"); 599312900fSEmmanuel Vadot fprintf(stderr, "\tpwm [-f dev] -c channel -p period\n"); 609312900fSEmmanuel Vadot fprintf(stderr, "\tpwm [-f dev] -c channel -d duty\n"); 619312900fSEmmanuel Vadot exit(1); 629312900fSEmmanuel Vadot } 639312900fSEmmanuel Vadot 649312900fSEmmanuel Vadot int 659312900fSEmmanuel Vadot main(int argc, char *argv[]) 669312900fSEmmanuel Vadot { 679312900fSEmmanuel Vadot struct pwm_state state; 689312900fSEmmanuel Vadot int fd; 699312900fSEmmanuel Vadot int channel, nchannels; 709312900fSEmmanuel Vadot int period, duty; 719312900fSEmmanuel Vadot int action, ch; 729312900fSEmmanuel Vadot cap_rights_t right_ioctl; 739312900fSEmmanuel Vadot const unsigned long pwm_ioctls[] = {PWMGETSTATE, PWMSETSTATE, PWMMAXCHANNEL}; 74*50a123aaSEmmanuel Vadot char *percent; 759312900fSEmmanuel Vadot 769312900fSEmmanuel Vadot action = 0; 779312900fSEmmanuel Vadot fd = -1; 789312900fSEmmanuel Vadot channel = -1; 799312900fSEmmanuel Vadot period = duty = -1; 809312900fSEmmanuel Vadot 819312900fSEmmanuel Vadot while ((ch = getopt(argc, argv, "f:c:EDCp:d:")) != -1) { 829312900fSEmmanuel Vadot switch (ch) { 839312900fSEmmanuel Vadot case 'E': 849312900fSEmmanuel Vadot if (action) 859312900fSEmmanuel Vadot usage(); 869312900fSEmmanuel Vadot action = PWM_ENABLE; 879312900fSEmmanuel Vadot break; 889312900fSEmmanuel Vadot case 'D': 899312900fSEmmanuel Vadot if (action) 909312900fSEmmanuel Vadot usage(); 919312900fSEmmanuel Vadot action = PWM_DISABLE; 929312900fSEmmanuel Vadot break; 939312900fSEmmanuel Vadot case 'C': 949312900fSEmmanuel Vadot if (action) 959312900fSEmmanuel Vadot usage(); 969312900fSEmmanuel Vadot action = PWM_SHOW_CONFIG; 979312900fSEmmanuel Vadot break; 989312900fSEmmanuel Vadot case 'p': 999312900fSEmmanuel Vadot if (action & ~(PWM_PERIOD | PWM_DUTY)) 1009312900fSEmmanuel Vadot usage(); 1019312900fSEmmanuel Vadot action = PWM_PERIOD; 1029312900fSEmmanuel Vadot period = strtol(optarg, NULL, 10); 1039312900fSEmmanuel Vadot break; 1049312900fSEmmanuel Vadot case 'd': 1059312900fSEmmanuel Vadot if (action & ~(PWM_PERIOD | PWM_DUTY)) 1069312900fSEmmanuel Vadot usage(); 1079312900fSEmmanuel Vadot action = PWM_DUTY; 108*50a123aaSEmmanuel Vadot duty = strtol(optarg, &percent, 10); 109*50a123aaSEmmanuel Vadot if (*percent != '\0' && *percent != '%') 110*50a123aaSEmmanuel Vadot usage(); 1119312900fSEmmanuel Vadot break; 1129312900fSEmmanuel Vadot case 'c': 1139312900fSEmmanuel Vadot if (channel != -1) 1149312900fSEmmanuel Vadot usage(); 1159312900fSEmmanuel Vadot channel = strtol(optarg, NULL, 10); 1169312900fSEmmanuel Vadot break; 1179312900fSEmmanuel Vadot case 'f': 1189312900fSEmmanuel Vadot if ((fd = open(optarg, O_RDWR)) < 0) { 1199312900fSEmmanuel Vadot fprintf(stderr, "pwm: cannot open %s %s\n", 1209312900fSEmmanuel Vadot optarg, strerror(errno)); 1219312900fSEmmanuel Vadot exit(1); 1229312900fSEmmanuel Vadot } 1239312900fSEmmanuel Vadot } 1249312900fSEmmanuel Vadot } 1259312900fSEmmanuel Vadot 1269312900fSEmmanuel Vadot if (fd == -1) { 1279312900fSEmmanuel Vadot if ((fd = open("/dev/pwmc0", O_RDWR)) < 0) { 1289312900fSEmmanuel Vadot fprintf(stderr, "pwm: cannot open %s %s\n", 1299312900fSEmmanuel Vadot optarg, strerror(errno)); 1309312900fSEmmanuel Vadot exit(1); 1319312900fSEmmanuel Vadot } 1329312900fSEmmanuel Vadot } 1339312900fSEmmanuel Vadot 1349312900fSEmmanuel Vadot if (action == 0 || fd == -1) 1359312900fSEmmanuel Vadot usage(); 1369312900fSEmmanuel Vadot 1379312900fSEmmanuel Vadot if (caph_limit_stdio() < 0) { 1389312900fSEmmanuel Vadot fprintf(stderr, "can't limit stdio rights"); 1399312900fSEmmanuel Vadot goto fail; 1409312900fSEmmanuel Vadot } 1419312900fSEmmanuel Vadot caph_cache_catpages(); 1429312900fSEmmanuel Vadot cap_rights_init(&right_ioctl, CAP_IOCTL); 1439312900fSEmmanuel Vadot if (caph_rights_limit(fd, &right_ioctl) < 0) { 1449312900fSEmmanuel Vadot fprintf(stderr, "cap_right_limit() failed\n"); 1459312900fSEmmanuel Vadot goto fail; 1469312900fSEmmanuel Vadot } 1479312900fSEmmanuel Vadot if (caph_ioctls_limit(fd, pwm_ioctls, nitems(pwm_ioctls)) < 0) { 1489312900fSEmmanuel Vadot fprintf(stderr, "caph_ioctls_limit() failed\n"); 1499312900fSEmmanuel Vadot goto fail; 1509312900fSEmmanuel Vadot } 1519312900fSEmmanuel Vadot if (caph_enter() < 0) { 1529312900fSEmmanuel Vadot fprintf(stderr, "failed to enter capability mode\n"); 1539312900fSEmmanuel Vadot goto fail; 1549312900fSEmmanuel Vadot } 1559312900fSEmmanuel Vadot 1569312900fSEmmanuel Vadot /* Check if the channel is correct */ 1579312900fSEmmanuel Vadot if (ioctl(fd, PWMMAXCHANNEL, &nchannels) == -1) { 1589312900fSEmmanuel Vadot fprintf(stderr, "ioctl: %s\n", strerror(errno)); 1599312900fSEmmanuel Vadot goto fail; 1609312900fSEmmanuel Vadot } 1619312900fSEmmanuel Vadot if (channel > nchannels) { 1629312900fSEmmanuel Vadot fprintf(stderr, "pwm controller only support %d channels\n", 1639312900fSEmmanuel Vadot nchannels); 1649312900fSEmmanuel Vadot goto fail; 1659312900fSEmmanuel Vadot } 1669312900fSEmmanuel Vadot 1679312900fSEmmanuel Vadot /* Fill the common args */ 1689312900fSEmmanuel Vadot state.channel = channel; 1699312900fSEmmanuel Vadot if (ioctl(fd, PWMGETSTATE, &state) == -1) { 1709312900fSEmmanuel Vadot fprintf(stderr, "Cannot get current state of the pwm controller\n"); 1719312900fSEmmanuel Vadot goto fail; 1729312900fSEmmanuel Vadot } 1739312900fSEmmanuel Vadot 1749312900fSEmmanuel Vadot switch (action) { 1759312900fSEmmanuel Vadot case PWM_ENABLE: 1769312900fSEmmanuel Vadot if (state.enable == false) { 1779312900fSEmmanuel Vadot state.enable = true; 1789312900fSEmmanuel Vadot if (ioctl(fd, PWMSETSTATE, &state) == -1) { 1799312900fSEmmanuel Vadot fprintf(stderr, 1809312900fSEmmanuel Vadot "Cannot enable the pwm controller\n"); 1819312900fSEmmanuel Vadot goto fail; 1829312900fSEmmanuel Vadot } 1839312900fSEmmanuel Vadot } 1849312900fSEmmanuel Vadot break; 1859312900fSEmmanuel Vadot case PWM_DISABLE: 1869312900fSEmmanuel Vadot if (state.enable == true) { 1879312900fSEmmanuel Vadot state.enable = false; 1889312900fSEmmanuel Vadot if (ioctl(fd, PWMSETSTATE, &state) == -1) { 1899312900fSEmmanuel Vadot fprintf(stderr, 1909312900fSEmmanuel Vadot "Cannot disable the pwm controller\n"); 1919312900fSEmmanuel Vadot goto fail; 1929312900fSEmmanuel Vadot } 1939312900fSEmmanuel Vadot } 1949312900fSEmmanuel Vadot break; 1959312900fSEmmanuel Vadot case PWM_SHOW_CONFIG: 1966a9997edSEmmanuel Vadot printf("period: %u\nduty: %u\nenabled:%d\n", 1976a9997edSEmmanuel Vadot state.period, 1986a9997edSEmmanuel Vadot state.duty, 1999312900fSEmmanuel Vadot state.enable); 2009312900fSEmmanuel Vadot break; 2019312900fSEmmanuel Vadot case PWM_PERIOD: 2029312900fSEmmanuel Vadot case PWM_DUTY: 2039312900fSEmmanuel Vadot if (period != -1) 2049312900fSEmmanuel Vadot state.period = period; 205*50a123aaSEmmanuel Vadot if (duty != -1) { 206*50a123aaSEmmanuel Vadot if (*percent != '\0') 207*50a123aaSEmmanuel Vadot state.duty = state.period * duty / 100; 208*50a123aaSEmmanuel Vadot else 2099312900fSEmmanuel Vadot state.duty = duty; 210*50a123aaSEmmanuel Vadot } 2119312900fSEmmanuel Vadot if (ioctl(fd, PWMSETSTATE, &state) == -1) { 2129312900fSEmmanuel Vadot fprintf(stderr, 2139312900fSEmmanuel Vadot "Cannot configure the pwm controller\n"); 2149312900fSEmmanuel Vadot goto fail; 2159312900fSEmmanuel Vadot } 2169312900fSEmmanuel Vadot break; 2179312900fSEmmanuel Vadot } 2189312900fSEmmanuel Vadot 2199312900fSEmmanuel Vadot close(fd); 2209312900fSEmmanuel Vadot return (0); 2219312900fSEmmanuel Vadot 2229312900fSEmmanuel Vadot fail: 2239312900fSEmmanuel Vadot close(fd); 2249312900fSEmmanuel Vadot return (1); 2259312900fSEmmanuel Vadot } 226