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/capsicum.h> 3571fb3739SIan Lepore #include <dev/pwm/pwmc.h> 369312900fSEmmanuel Vadot 379312900fSEmmanuel Vadot #include <err.h> 389312900fSEmmanuel Vadot #include <errno.h> 399312900fSEmmanuel Vadot #include <fcntl.h> 407d763870SIan Lepore #include <limits.h> 419312900fSEmmanuel Vadot #include <stdio.h> 429312900fSEmmanuel Vadot #include <stdlib.h> 439312900fSEmmanuel Vadot #include <string.h> 449312900fSEmmanuel Vadot #include <unistd.h> 459312900fSEmmanuel Vadot #include <capsicum_helpers.h> 469312900fSEmmanuel Vadot 479312900fSEmmanuel Vadot #define PWM_ENABLE 0x0001 489312900fSEmmanuel Vadot #define PWM_DISABLE 0x0002 499312900fSEmmanuel Vadot #define PWM_SHOW_CONFIG 0x0004 509312900fSEmmanuel Vadot #define PWM_PERIOD 0x0008 519312900fSEmmanuel Vadot #define PWM_DUTY 0x0010 529312900fSEmmanuel Vadot 537d763870SIan Lepore static char device_name[PATH_MAX] = "/dev/pwm/pwmc0.0"; 547d763870SIan Lepore 557d763870SIan Lepore static void 567d763870SIan Lepore set_device_name(const char *name) 577d763870SIan Lepore { 587d763870SIan Lepore 597d763870SIan Lepore if (name[0] == '/') 607d763870SIan Lepore strlcpy(device_name, name, sizeof(device_name)); 617d763870SIan Lepore else 627d763870SIan Lepore snprintf(device_name, sizeof(device_name), "/dev/pwm/%s", name); 637d763870SIan Lepore } 647d763870SIan Lepore 659312900fSEmmanuel Vadot static void 669312900fSEmmanuel Vadot usage(void) 679312900fSEmmanuel Vadot { 689312900fSEmmanuel Vadot fprintf(stderr, "Usage:\n"); 69780c3de8SIan Lepore fprintf(stderr, "\tpwm [-f dev] -C\n"); 70*26f3ca61SIan Lepore fprintf(stderr, "\tpwm [-f dev] [-D | -E] [-p period] [-d duty[%%]]\n"); 719312900fSEmmanuel Vadot exit(1); 729312900fSEmmanuel Vadot } 739312900fSEmmanuel Vadot 749312900fSEmmanuel Vadot int 759312900fSEmmanuel Vadot main(int argc, char *argv[]) 769312900fSEmmanuel Vadot { 779312900fSEmmanuel Vadot struct pwm_state state; 789312900fSEmmanuel Vadot int fd; 799312900fSEmmanuel Vadot int period, duty; 809312900fSEmmanuel Vadot int action, ch; 819312900fSEmmanuel Vadot cap_rights_t right_ioctl; 82780c3de8SIan Lepore const unsigned long pwm_ioctls[] = {PWMGETSTATE, PWMSETSTATE}; 8350a123aaSEmmanuel Vadot char *percent; 847d763870SIan Lepore bool setname; 859312900fSEmmanuel Vadot 869312900fSEmmanuel Vadot action = 0; 877d763870SIan Lepore setname = false; 889312900fSEmmanuel Vadot fd = -1; 899312900fSEmmanuel Vadot period = duty = -1; 909312900fSEmmanuel Vadot 91780c3de8SIan Lepore while ((ch = getopt(argc, argv, "f:EDCp:d:")) != -1) { 929312900fSEmmanuel Vadot switch (ch) { 939312900fSEmmanuel Vadot case 'E': 94*26f3ca61SIan Lepore if (action & (PWM_DISABLE | PWM_SHOW_CONFIG)) 959312900fSEmmanuel Vadot usage(); 96*26f3ca61SIan Lepore action |= PWM_ENABLE; 979312900fSEmmanuel Vadot break; 989312900fSEmmanuel Vadot case 'D': 99*26f3ca61SIan Lepore if (action & (PWM_ENABLE | PWM_SHOW_CONFIG)) 1009312900fSEmmanuel Vadot usage(); 101*26f3ca61SIan Lepore action |= PWM_DISABLE; 1029312900fSEmmanuel Vadot break; 1039312900fSEmmanuel Vadot case 'C': 1049312900fSEmmanuel Vadot if (action) 1059312900fSEmmanuel Vadot usage(); 1069312900fSEmmanuel Vadot action = PWM_SHOW_CONFIG; 1079312900fSEmmanuel Vadot break; 1089312900fSEmmanuel Vadot case 'p': 109*26f3ca61SIan Lepore if (action & PWM_SHOW_CONFIG) 1109312900fSEmmanuel Vadot usage(); 111*26f3ca61SIan Lepore action |= PWM_PERIOD; 1129312900fSEmmanuel Vadot period = strtol(optarg, NULL, 10); 1139312900fSEmmanuel Vadot break; 1149312900fSEmmanuel Vadot case 'd': 115*26f3ca61SIan Lepore if (action & PWM_SHOW_CONFIG) 1169312900fSEmmanuel Vadot usage(); 117*26f3ca61SIan Lepore action |= PWM_DUTY; 11850a123aaSEmmanuel Vadot duty = strtol(optarg, &percent, 10); 119*26f3ca61SIan Lepore if (*percent == '%') { 120*26f3ca61SIan Lepore if (duty < 0 || duty > 100) { 121*26f3ca61SIan Lepore fprintf(stderr, 122*26f3ca61SIan Lepore "Invalid duty percentage\n"); 123*26f3ca61SIan Lepore usage(); 124*26f3ca61SIan Lepore } 125*26f3ca61SIan Lepore } else if (*percent != '\0') 12650a123aaSEmmanuel Vadot usage(); 1279312900fSEmmanuel Vadot break; 1289312900fSEmmanuel Vadot case 'f': 1297d763870SIan Lepore setname = true; 1307d763870SIan Lepore set_device_name(optarg); 1317d763870SIan Lepore break; 132780c3de8SIan Lepore case '?': 133780c3de8SIan Lepore usage(); 134780c3de8SIan Lepore break; 1359312900fSEmmanuel Vadot } 1369312900fSEmmanuel Vadot } 1379312900fSEmmanuel Vadot 1387d763870SIan Lepore if (action == 0) 1399312900fSEmmanuel Vadot usage(); 1409312900fSEmmanuel Vadot 1417d763870SIan Lepore if ((fd = open(device_name, O_RDWR)) == -1) { 1427d763870SIan Lepore fprintf(stderr, "pwm: cannot open %s: %s\n", 1437d763870SIan Lepore device_name, strerror(errno)); 1447d763870SIan Lepore if (setname) 1457d763870SIan Lepore exit(1); 1467d763870SIan Lepore else 1477d763870SIan Lepore usage(); 1487d763870SIan Lepore } 1497d763870SIan Lepore 1509312900fSEmmanuel Vadot if (caph_limit_stdio() < 0) { 1519312900fSEmmanuel Vadot fprintf(stderr, "can't limit stdio rights"); 1529312900fSEmmanuel Vadot goto fail; 1539312900fSEmmanuel Vadot } 1549312900fSEmmanuel Vadot caph_cache_catpages(); 1559312900fSEmmanuel Vadot cap_rights_init(&right_ioctl, CAP_IOCTL); 1569312900fSEmmanuel Vadot if (caph_rights_limit(fd, &right_ioctl) < 0) { 1579312900fSEmmanuel Vadot fprintf(stderr, "cap_right_limit() failed\n"); 1589312900fSEmmanuel Vadot goto fail; 1599312900fSEmmanuel Vadot } 1609312900fSEmmanuel Vadot if (caph_ioctls_limit(fd, pwm_ioctls, nitems(pwm_ioctls)) < 0) { 1619312900fSEmmanuel Vadot fprintf(stderr, "caph_ioctls_limit() failed\n"); 1629312900fSEmmanuel Vadot goto fail; 1639312900fSEmmanuel Vadot } 1649312900fSEmmanuel Vadot if (caph_enter() < 0) { 1659312900fSEmmanuel Vadot fprintf(stderr, "failed to enter capability mode\n"); 1669312900fSEmmanuel Vadot goto fail; 1679312900fSEmmanuel Vadot } 1689312900fSEmmanuel Vadot 1699312900fSEmmanuel Vadot /* Fill the common args */ 1709312900fSEmmanuel Vadot if (ioctl(fd, PWMGETSTATE, &state) == -1) { 1719312900fSEmmanuel Vadot fprintf(stderr, "Cannot get current state of the pwm controller\n"); 1729312900fSEmmanuel Vadot goto fail; 1739312900fSEmmanuel Vadot } 1749312900fSEmmanuel Vadot 175*26f3ca61SIan Lepore if (action == PWM_SHOW_CONFIG) { 1766a9997edSEmmanuel Vadot printf("period: %u\nduty: %u\nenabled:%d\n", 1776a9997edSEmmanuel Vadot state.period, 1786a9997edSEmmanuel Vadot state.duty, 1799312900fSEmmanuel Vadot state.enable); 180*26f3ca61SIan Lepore goto fail; 181*26f3ca61SIan Lepore } else { 182*26f3ca61SIan Lepore if (action & PWM_ENABLE) 183*26f3ca61SIan Lepore state.enable = true; 184*26f3ca61SIan Lepore if (action & PWM_DISABLE) 185*26f3ca61SIan Lepore state.enable = false; 186*26f3ca61SIan Lepore if (action & PWM_PERIOD) 1879312900fSEmmanuel Vadot state.period = period; 188*26f3ca61SIan Lepore if (action & PWM_DUTY) { 18950a123aaSEmmanuel Vadot if (*percent != '\0') 19050a123aaSEmmanuel Vadot state.duty = state.period * duty / 100; 19150a123aaSEmmanuel Vadot else 1929312900fSEmmanuel Vadot state.duty = duty; 19350a123aaSEmmanuel Vadot } 194*26f3ca61SIan Lepore 1959312900fSEmmanuel Vadot if (ioctl(fd, PWMSETSTATE, &state) == -1) { 1969312900fSEmmanuel Vadot fprintf(stderr, 1979312900fSEmmanuel Vadot "Cannot configure the pwm controller\n"); 1989312900fSEmmanuel Vadot goto fail; 1999312900fSEmmanuel Vadot } 2009312900fSEmmanuel Vadot } 2019312900fSEmmanuel Vadot 2029312900fSEmmanuel Vadot close(fd); 2039312900fSEmmanuel Vadot return (0); 2049312900fSEmmanuel Vadot 2059312900fSEmmanuel Vadot fail: 2069312900fSEmmanuel Vadot close(fd); 2079312900fSEmmanuel Vadot return (1); 2089312900fSEmmanuel Vadot } 209