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