/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #define _GNU_SOURCE /* For ppoll() */ #include <errno.h> #include <fcntl.h> #include <pthread.h> #include <poll.h> #include <sched.h> #include <stdlib.h> #include <string.h> #include <syslog.h> #include <sys/param.h> #include <sys/resource.h> #include <sys/socket.h> #include <sys/syscall.h> #include <sys/types.h> #include <unistd.h> #include "cras_util.h" int cras_set_rt_scheduling(int rt_lim) { struct rlimit rl; rl.rlim_cur = rl.rlim_max = rt_lim; if (setrlimit(RLIMIT_RTPRIO, &rl) < 0) { syslog(LOG_WARNING, "setrlimit %u failed: %d\n", (unsigned) rt_lim, errno); return -EACCES; } return 0; } int cras_set_thread_priority(int priority) { struct sched_param sched_param; int err; memset(&sched_param, 0, sizeof(sched_param)); sched_param.sched_priority = priority; err = pthread_setschedparam(pthread_self(), SCHED_RR, &sched_param); if (err) syslog(LOG_WARNING, "Failed to set thread sched params to priority %d" ", rc: %d\n", priority, err); return err; } int cras_set_nice_level(int nice) { int rc; /* Linux isn't posix compliant with setpriority(2), it will set a thread * priority if it is passed a tid, not affecting the rest of the threads * in the process. Setting this priority will only succeed if the user * has been granted permission to adjust nice values on the system. */ rc = setpriority(PRIO_PROCESS, syscall(__NR_gettid), nice); if (rc) syslog(LOG_WARNING, "Failed to set nice to %d, rc: %d", nice, rc); return rc; } int cras_make_fd_nonblocking(int fd) { int fl; fl = fcntl(fd, F_GETFL); if (fl < 0) return fl; if (fl & O_NONBLOCK) return 0; return fcntl(fd, F_SETFL, fl | O_NONBLOCK); } int cras_make_fd_blocking(int fd) { int fl; fl = fcntl(fd, F_GETFL); if (fl < 0) return fl; if ((~fl) & O_NONBLOCK) return 0; return fcntl(fd, F_SETFL, fl & ~O_NONBLOCK); } int cras_send_with_fds(int sockfd, const void *buf, size_t len, int *fd, unsigned int num_fds) { struct msghdr msg = {0}; struct iovec iov; struct cmsghdr *cmsg; char *control; const unsigned int control_size = CMSG_SPACE(sizeof(*fd) * num_fds); int rc; control = calloc(control_size, 1); msg.msg_iov = &iov; msg.msg_iovlen = 1; iov.iov_base = (void *)buf; iov.iov_len = len; msg.msg_control = control; msg.msg_controllen = control_size; cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; cmsg->cmsg_len = CMSG_LEN(sizeof(*fd) * num_fds); memcpy(CMSG_DATA(cmsg), fd, sizeof(*fd) * num_fds); rc = sendmsg(sockfd, &msg, 0); free(control); return rc; } int cras_recv_with_fds(int sockfd, void *buf, size_t len, int *fd, unsigned int *num_fds) { struct msghdr msg = {0}; struct iovec iov; struct cmsghdr *cmsg; char *control; const unsigned int control_size = CMSG_SPACE(sizeof(*fd) * *num_fds); int rc; int i; control = calloc(control_size, 1); for (i = 0; i < *num_fds; i++) fd[i] = -1; msg.msg_iov = &iov; msg.msg_iovlen = 1; iov.iov_base = buf; iov.iov_len = len; msg.msg_control = control; msg.msg_controllen = control_size; rc = recvmsg(sockfd, &msg, 0); if (rc < 0) { rc = -errno; goto exit; } for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { size_t fd_size = cmsg->cmsg_len - sizeof(*cmsg); *num_fds = MIN(*num_fds, fd_size / sizeof(*fd)); memcpy(fd, CMSG_DATA(cmsg), *num_fds * sizeof(*fd)); break; } } exit: free(control); return rc; } int cras_poll(struct pollfd *fds, nfds_t nfds, struct timespec *timeout, const sigset_t *sigmask) { struct timespec now; struct timespec future; struct pollfd *fd = fds; nfds_t i; int rc = 0; if (timeout) { /* Treat a negative timeout as valid (but timed-out) since * this function could update timeout to have negative tv_sec * or tv_nsec. */ if (timeout->tv_sec < 0 || timeout->tv_nsec < 0) return -ETIMEDOUT; rc = clock_gettime(CLOCK_MONOTONIC_RAW, &future); if (rc < 0) return -errno; add_timespecs(&future, timeout); } for (i = 0; i < nfds; i++) { fd->revents = 0; fd++; } rc = ppoll(fds, nfds, timeout, sigmask); if (rc == 0 && timeout) { rc = -ETIMEDOUT; } else if (rc < 0) { rc = -errno; } if (timeout) { clock_gettime(CLOCK_MONOTONIC_RAW, &now); subtract_timespecs(&future, &now, timeout); } return rc; } int wait_for_dev_input_access() { /* Wait for /dev/input/event* files to become accessible by * having group 'input'. Setting these files to have 'rw' * access to group 'input' is done through a udev rule * installed by adhd into /lib/udev/rules.d. * * Wait for up to 2 seconds for the /dev/input/event* files to be * readable by gavd. * * TODO(thutt): This could also be done with a udev enumerate * and then a udev monitor. */ const unsigned max_iterations = 4; unsigned i = 0; while (i < max_iterations) { int readable; struct timeval timeout; const char * const pathname = "/dev/input/event0"; timeout.tv_sec = 0; timeout.tv_usec = 500000; /* 1/2 second. */ readable = access(pathname, R_OK); /* If the file could be opened, then the udev rule has been * applied and gavd can read the event files. If there are no * event files, then we don't need to wait. * * If access does not become available, then headphone & * microphone jack autoswitching will not function properly. */ if (readable == 0 || (readable == -1 && errno == ENOENT)) { /* Access allowed, or file does not exist. */ break; } if (readable != -1 || errno != EACCES) { syslog(LOG_ERR, "Bad access for input devs."); return errno; } select(1, NULL, NULL, NULL, &timeout); ++i; } return 0; }