Home | History | Annotate | Download | only in common
      1 /* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
      2  * Use of this source code is governed by a BSD-style license that can be
      3  * found in the LICENSE file.
      4  */
      5 
      6 #define _GNU_SOURCE /* For ppoll() */
      7 
      8 #include <errno.h>
      9 #include <fcntl.h>
     10 #include <pthread.h>
     11 #include <poll.h>
     12 #include <sched.h>
     13 #include <stdlib.h>
     14 #include <string.h>
     15 #include <syslog.h>
     16 #include <sys/param.h>
     17 #include <sys/resource.h>
     18 #include <sys/socket.h>
     19 #include <sys/syscall.h>
     20 #include <sys/types.h>
     21 #include <unistd.h>
     22 
     23 #include "cras_util.h"
     24 
     25 int cras_set_rt_scheduling(int rt_lim)
     26 {
     27 	struct rlimit rl;
     28 
     29 	rl.rlim_cur = rl.rlim_max = rt_lim;
     30 
     31 	if (setrlimit(RLIMIT_RTPRIO, &rl) < 0) {
     32 		syslog(LOG_WARNING, "setrlimit %u failed: %d\n",
     33 		       (unsigned) rt_lim, errno);
     34 		return -EACCES;
     35 	}
     36 	return 0;
     37 }
     38 
     39 int cras_set_thread_priority(int priority)
     40 {
     41 	struct sched_param sched_param;
     42 	int err;
     43 
     44 	memset(&sched_param, 0, sizeof(sched_param));
     45 	sched_param.sched_priority = priority;
     46 
     47 	err = pthread_setschedparam(pthread_self(), SCHED_RR, &sched_param);
     48 	if (err)
     49 		syslog(LOG_WARNING,
     50 		       "Failed to set thread sched params to priority %d"
     51 		       ", rc: %d\n", priority, err);
     52 
     53 	return err;
     54 }
     55 
     56 int cras_set_nice_level(int nice)
     57 {
     58 	int rc;
     59 
     60 	/* Linux isn't posix compliant with setpriority(2), it will set a thread
     61 	 * priority if it is passed a tid, not affecting the rest of the threads
     62 	 * in the process.  Setting this priority will only succeed if the user
     63 	 * has been granted permission to adjust nice values on the system.
     64 	 */
     65 	rc = setpriority(PRIO_PROCESS, syscall(__NR_gettid), nice);
     66 	if (rc)
     67 		syslog(LOG_WARNING, "Failed to set nice to %d, rc: %d",
     68 		       nice, rc);
     69 
     70 	return rc;
     71 }
     72 
     73 int cras_make_fd_nonblocking(int fd)
     74 {
     75 	int fl;
     76 
     77 	fl = fcntl(fd, F_GETFL);
     78 	if (fl < 0)
     79 		return fl;
     80 	if (fl & O_NONBLOCK)
     81 		return 0;
     82 	return fcntl(fd, F_SETFL, fl | O_NONBLOCK);
     83 }
     84 
     85 int cras_make_fd_blocking(int fd)
     86 {
     87 	int fl;
     88 
     89 	fl = fcntl(fd, F_GETFL);
     90 	if (fl < 0)
     91 		return fl;
     92 	if ((~fl) & O_NONBLOCK)
     93 		return 0;
     94 	return fcntl(fd, F_SETFL, fl & ~O_NONBLOCK);
     95 }
     96 
     97 int cras_send_with_fds(int sockfd, const void *buf, size_t len, int *fd,
     98 		       unsigned int num_fds)
     99 {
    100 	struct msghdr msg = {0};
    101 	struct iovec iov;
    102 	struct cmsghdr *cmsg;
    103 	char *control;
    104 	const unsigned int control_size = CMSG_SPACE(sizeof(*fd) * num_fds);
    105 	int rc;
    106 
    107 	control = calloc(control_size, 1);
    108 
    109 	msg.msg_iov = &iov;
    110 	msg.msg_iovlen = 1;
    111 	iov.iov_base = (void *)buf;
    112 	iov.iov_len = len;
    113 
    114 	msg.msg_control = control;
    115 	msg.msg_controllen = control_size;
    116 
    117 	cmsg = CMSG_FIRSTHDR(&msg);
    118 	cmsg->cmsg_level = SOL_SOCKET;
    119 	cmsg->cmsg_type = SCM_RIGHTS;
    120 	cmsg->cmsg_len = CMSG_LEN(sizeof(*fd) * num_fds);
    121 	memcpy(CMSG_DATA(cmsg), fd, sizeof(*fd) * num_fds);
    122 
    123 	rc = sendmsg(sockfd, &msg, 0);
    124 	free(control);
    125 	return rc;
    126 }
    127 
    128 int cras_recv_with_fds(int sockfd, void *buf, size_t len, int *fd,
    129 		       unsigned int *num_fds)
    130 {
    131 	struct msghdr msg = {0};
    132 	struct iovec iov;
    133 	struct cmsghdr *cmsg;
    134 	char *control;
    135 	const unsigned int control_size = CMSG_SPACE(sizeof(*fd) * *num_fds);
    136 	int rc;
    137 	int i;
    138 
    139 	control = calloc(control_size, 1);
    140 
    141 	for (i = 0; i < *num_fds; i++)
    142 		fd[i] = -1;
    143 
    144 	msg.msg_iov = &iov;
    145 	msg.msg_iovlen = 1;
    146 	iov.iov_base = buf;
    147 	iov.iov_len = len;
    148 	msg.msg_control = control;
    149 	msg.msg_controllen = control_size;
    150 
    151 	rc = recvmsg(sockfd, &msg, 0);
    152 	if (rc < 0) {
    153 		rc = -errno;
    154 		goto exit;
    155 	}
    156 
    157 	for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
    158 	     cmsg = CMSG_NXTHDR(&msg, cmsg)) {
    159 		if (cmsg->cmsg_level == SOL_SOCKET
    160 		    && cmsg->cmsg_type == SCM_RIGHTS) {
    161 			size_t fd_size = cmsg->cmsg_len - sizeof(*cmsg);
    162 			*num_fds = MIN(*num_fds, fd_size / sizeof(*fd));
    163 			memcpy(fd, CMSG_DATA(cmsg), *num_fds * sizeof(*fd));
    164 			break;
    165 		}
    166 	}
    167 
    168 exit:
    169 	free(control);
    170 	return rc;
    171 }
    172 
    173 int cras_poll(struct pollfd *fds, nfds_t nfds, struct timespec *timeout,
    174 	      const sigset_t *sigmask)
    175 {
    176 	struct timespec now;
    177 	struct timespec future;
    178 	struct pollfd *fd = fds;
    179 	nfds_t i;
    180 	int rc = 0;
    181 
    182 	if (timeout) {
    183 		/* Treat a negative timeout as valid (but timed-out) since
    184 		 * this function could update timeout to have negative tv_sec
    185 		 * or tv_nsec. */
    186 		if (timeout->tv_sec < 0 || timeout->tv_nsec < 0)
    187 			return -ETIMEDOUT;
    188 		rc = clock_gettime(CLOCK_MONOTONIC_RAW, &future);
    189 		if (rc < 0)
    190 			return -errno;
    191 		add_timespecs(&future, timeout);
    192 	}
    193 
    194 	for (i = 0; i < nfds; i++) {
    195 		fd->revents = 0;
    196 		fd++;
    197 	}
    198 
    199 	rc = ppoll(fds, nfds, timeout, sigmask);
    200 	if (rc == 0 && timeout) {
    201 		rc = -ETIMEDOUT;
    202 	}
    203 	else if (rc < 0) {
    204 		rc = -errno;
    205 	}
    206 
    207 	if (timeout) {
    208 		clock_gettime(CLOCK_MONOTONIC_RAW, &now);
    209 		subtract_timespecs(&future, &now, timeout);
    210 	}
    211 
    212 	return rc;
    213 }
    214 
    215 int wait_for_dev_input_access()
    216 {
    217 	/* Wait for /dev/input/event* files to become accessible by
    218 	 * having group 'input'.  Setting these files to have 'rw'
    219 	 * access to group 'input' is done through a udev rule
    220 	 * installed by adhd into /lib/udev/rules.d.
    221 	 *
    222 	 * Wait for up to 2 seconds for the /dev/input/event* files to be
    223 	 * readable by gavd.
    224 	 *
    225 	 * TODO(thutt): This could also be done with a udev enumerate
    226 	 *              and then a udev monitor.
    227 	 */
    228 	const unsigned max_iterations = 4;
    229 	unsigned i = 0;
    230 
    231 	while (i < max_iterations) {
    232 		int		   readable;
    233 		struct timeval	   timeout;
    234 		const char * const pathname = "/dev/input/event0";
    235 
    236 		timeout.tv_sec	= 0;
    237 		timeout.tv_usec = 500000;   /* 1/2 second. */
    238 		readable = access(pathname, R_OK);
    239 
    240 		/* If the file could be opened, then the udev rule has been
    241 		 * applied and gavd can read the event files.  If there are no
    242 		 * event files, then we don't need to wait.
    243 		 *
    244 		 * If access does not become available, then headphone &
    245 		 * microphone jack autoswitching will not function properly.
    246 		 */
    247 		if (readable == 0 || (readable == -1 && errno == ENOENT)) {
    248 			/* Access allowed, or file does not exist. */
    249 			break;
    250 		}
    251 		if (readable != -1 || errno != EACCES) {
    252 			syslog(LOG_ERR, "Bad access for input devs.");
    253 			return errno;
    254 		}
    255 		select(1, NULL, NULL, NULL, &timeout);
    256 		++i;
    257 	}
    258 
    259 	return 0;
    260 }
    261