• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
cras_set_rt_scheduling(int rt_lim)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 
cras_set_thread_priority(int priority)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",
52 		       priority, err);
53 
54 	return err;
55 }
56 
cras_set_nice_level(int nice)57 int cras_set_nice_level(int nice)
58 {
59 	int rc;
60 
61 	/* Linux isn't posix compliant with setpriority(2), it will set a thread
62 	 * priority if it is passed a tid, not affecting the rest of the threads
63 	 * in the process.  Setting this priority will only succeed if the user
64 	 * has been granted permission to adjust nice values on the system.
65 	 */
66 	rc = setpriority(PRIO_PROCESS, syscall(__NR_gettid), nice);
67 	if (rc)
68 		syslog(LOG_WARNING, "Failed to set nice to %d, rc: %d", nice,
69 		       rc);
70 
71 	return rc;
72 }
73 
cras_make_fd_nonblocking(int fd)74 int cras_make_fd_nonblocking(int fd)
75 {
76 	int fl;
77 
78 	fl = fcntl(fd, F_GETFL);
79 	if (fl < 0)
80 		return fl;
81 	if (fl & O_NONBLOCK)
82 		return 0;
83 	return fcntl(fd, F_SETFL, fl | O_NONBLOCK);
84 }
85 
cras_make_fd_blocking(int fd)86 int cras_make_fd_blocking(int fd)
87 {
88 	int fl;
89 
90 	fl = fcntl(fd, F_GETFL);
91 	if (fl < 0)
92 		return fl;
93 	if ((~fl) & O_NONBLOCK)
94 		return 0;
95 	return fcntl(fd, F_SETFL, fl & ~O_NONBLOCK);
96 }
97 
cras_send_with_fds(int sockfd,const void * buf,size_t len,int * fd,unsigned int num_fds)98 int cras_send_with_fds(int sockfd, const void *buf, size_t len, int *fd,
99 		       unsigned int num_fds)
100 {
101 	struct msghdr msg = { 0 };
102 	struct iovec iov;
103 	struct cmsghdr *cmsg;
104 	char *control;
105 	const unsigned int control_size = CMSG_SPACE(sizeof(*fd) * num_fds);
106 	int rc;
107 
108 	control = calloc(control_size, 1);
109 
110 	msg.msg_iov = &iov;
111 	msg.msg_iovlen = 1;
112 	iov.iov_base = (void *)buf;
113 	iov.iov_len = len;
114 
115 	msg.msg_control = control;
116 	msg.msg_controllen = control_size;
117 
118 	cmsg = CMSG_FIRSTHDR(&msg);
119 	cmsg->cmsg_level = SOL_SOCKET;
120 	cmsg->cmsg_type = SCM_RIGHTS;
121 	cmsg->cmsg_len = CMSG_LEN(sizeof(*fd) * num_fds);
122 	memcpy(CMSG_DATA(cmsg), fd, sizeof(*fd) * num_fds);
123 
124 	rc = sendmsg(sockfd, &msg, 0);
125 	if (rc == -1)
126 		rc = -errno;
127 	free(control);
128 	return rc;
129 }
130 
cras_recv_with_fds(int sockfd,void * buf,size_t len,int * fd,unsigned int * num_fds)131 int cras_recv_with_fds(int sockfd, void *buf, size_t len, int *fd,
132 		       unsigned int *num_fds)
133 {
134 	struct msghdr msg = { 0 };
135 	struct iovec iov;
136 	struct cmsghdr *cmsg;
137 	char *control;
138 	const unsigned int control_size = CMSG_SPACE(sizeof(*fd) * *num_fds);
139 	int rc;
140 	int i;
141 
142 	control = calloc(control_size, 1);
143 
144 	for (i = 0; i < *num_fds; i++)
145 		fd[i] = -1;
146 
147 	msg.msg_iov = &iov;
148 	msg.msg_iovlen = 1;
149 	iov.iov_base = buf;
150 	iov.iov_len = len;
151 	msg.msg_control = control;
152 	msg.msg_controllen = control_size;
153 
154 	rc = recvmsg(sockfd, &msg, 0);
155 	if (rc < 0) {
156 		rc = -errno;
157 		goto exit;
158 	}
159 
160 	for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
161 	     cmsg = CMSG_NXTHDR(&msg, cmsg)) {
162 		if (cmsg->cmsg_level == SOL_SOCKET &&
163 		    cmsg->cmsg_type == SCM_RIGHTS) {
164 			size_t fd_size = cmsg->cmsg_len - sizeof(*cmsg);
165 			*num_fds = MIN(*num_fds, fd_size / sizeof(*fd));
166 			memcpy(fd, CMSG_DATA(cmsg), *num_fds * sizeof(*fd));
167 			goto exit;
168 		}
169 	}
170 
171 	// If we reach here, we did not find any file descriptors.
172 	*num_fds = 0;
173 exit:
174 	free(control);
175 	return rc;
176 }
177 
cras_poll(struct pollfd * fds,nfds_t nfds,struct timespec * timeout,const sigset_t * sigmask)178 int cras_poll(struct pollfd *fds, nfds_t nfds, struct timespec *timeout,
179 	      const sigset_t *sigmask)
180 {
181 	struct timespec now;
182 	struct timespec future;
183 	struct pollfd *fd = fds;
184 	nfds_t i;
185 	int rc = 0;
186 
187 	if (timeout) {
188 		/* Treat a negative timeout as valid (but timed-out) since
189 		 * this function could update timeout to have negative tv_sec
190 		 * or tv_nsec. */
191 		if (timeout->tv_sec < 0 || timeout->tv_nsec < 0)
192 			return -ETIMEDOUT;
193 		rc = clock_gettime(CLOCK_MONOTONIC_RAW, &future);
194 		if (rc < 0)
195 			return -errno;
196 		add_timespecs(&future, timeout);
197 	}
198 
199 	for (i = 0; i < nfds; i++) {
200 		fd->revents = 0;
201 		fd++;
202 	}
203 
204 	rc = ppoll(fds, nfds, timeout, sigmask);
205 	if (rc == 0 && timeout) {
206 		rc = -ETIMEDOUT;
207 	} else if (rc < 0) {
208 		rc = -errno;
209 	}
210 
211 	if (timeout) {
212 		clock_gettime(CLOCK_MONOTONIC_RAW, &now);
213 		subtract_timespecs(&future, &now, timeout);
214 	}
215 
216 	return rc;
217 }
218 
wait_for_dev_input_access()219 int wait_for_dev_input_access()
220 {
221 	/* Wait for /dev/input/event* files to become accessible by
222 	 * having group 'input'.  Setting these files to have 'rw'
223 	 * access to group 'input' is done through a udev rule
224 	 * installed by adhd into /lib/udev/rules.d.
225 	 *
226 	 * Wait for up to 2 seconds for the /dev/input/event* files to be
227 	 * readable by gavd.
228 	 *
229 	 * TODO(thutt): This could also be done with a udev enumerate
230 	 *              and then a udev monitor.
231 	 */
232 	const unsigned max_iterations = 4;
233 	unsigned i = 0;
234 
235 	while (i < max_iterations) {
236 		int readable;
237 		struct timeval timeout;
238 		const char *const pathname = "/dev/input/event0";
239 
240 		timeout.tv_sec = 0;
241 		timeout.tv_usec = 500000; /* 1/2 second. */
242 		readable = access(pathname, R_OK);
243 
244 		/* If the file could be opened, then the udev rule has been
245 		 * applied and gavd can read the event files.  If there are no
246 		 * event files, then we don't need to wait.
247 		 *
248 		 * If access does not become available, then headphone &
249 		 * microphone jack autoswitching will not function properly.
250 		 */
251 		if (readable == 0 || (readable == -1 && errno == ENOENT)) {
252 			/* Access allowed, or file does not exist. */
253 			break;
254 		}
255 		if (readable != -1 || errno != EACCES) {
256 			syslog(LOG_ERR, "Bad access for input devs.");
257 			return errno;
258 		}
259 		select(1, NULL, NULL, NULL, &timeout);
260 		++i;
261 	}
262 
263 	return 0;
264 }
265