• 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", priority, err);
52 
53 	return err;
54 }
55 
cras_set_nice_level(int nice)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 
cras_make_fd_nonblocking(int fd)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 
cras_make_fd_blocking(int fd)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 
cras_send_with_fds(int sockfd,const void * buf,size_t len,int * fd,unsigned int num_fds)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 
cras_recv_with_fds(int sockfd,void * buf,size_t len,int * fd,unsigned int * num_fds)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 
cras_poll(struct pollfd * fds,nfds_t nfds,struct timespec * timeout,const sigset_t * sigmask)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 
wait_for_dev_input_access()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