Commit | Line | Data |
---|---|---|
e99d42b1 | 1 | /* |
2 | * This file is part of DisOrder | |
a35bb7cc | 3 | * Copyright (C) 2007, 2008 Richard Kettlewell |
313acc77 | 4 | * Portions copyright (C) 2007 Ross Younger |
e99d42b1 | 5 | * |
e7eb3a27 | 6 | * This program is free software: you can redistribute it and/or modify |
e99d42b1 | 7 | * it under the terms of the GNU General Public License as published by |
e7eb3a27 | 8 | * the Free Software Foundation, either version 3 of the License, or |
e99d42b1 | 9 | * (at your option) any later version. |
e7eb3a27 RK |
10 | * |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
e99d42b1 | 16 | * You should have received a copy of the GNU General Public License |
e7eb3a27 | 17 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
e99d42b1 | 18 | */ |
19 | /** @file server/speaker-oss.c | |
20 | * @brief Support for @ref BACKEND_OSS */ | |
21 | ||
05b75f8d | 22 | #include "common.h" |
e99d42b1 | 23 | |
24 | #if HAVE_SYS_SOUNDCARD_H | |
25 | ||
e99d42b1 | 26 | #include <unistd.h> |
27 | #include <poll.h> | |
e99d42b1 | 28 | #include <fcntl.h> |
29 | #include <unistd.h> | |
30 | #include <errno.h> | |
31 | #include <sys/ioctl.h> | |
32 | #include <sys/soundcard.h> | |
33 | ||
34 | #include "configuration.h" | |
35 | #include "syscalls.h" | |
36 | #include "log.h" | |
37 | #include "speaker-protocol.h" | |
38 | #include "speaker.h" | |
39 | ||
40 | /** @brief Current device */ | |
41 | static int ossfd = -1; | |
42 | ||
43 | /** @brief OSS backend initialization */ | |
44 | static void oss_init(void) { | |
45 | info("selected OSS backend"); | |
46 | } | |
47 | ||
48 | /** @brief OSS deactivation */ | |
49 | static void oss_deactivate(void) { | |
50 | if(ossfd != -1) { | |
51 | xclose(ossfd); | |
52 | ossfd = -1; | |
53 | device_state = device_closed; | |
54 | D(("released audio device")); | |
55 | } | |
56 | } | |
57 | ||
58 | /** @brief OSS backend activation */ | |
59 | static void oss_activate(void) { | |
60 | int stereo, format, rate; | |
61 | const char *device; | |
62 | ||
63 | if(ossfd == -1) { | |
64 | /* Try to pick a device */ | |
65 | if(!strcmp(config->device, "default")) { | |
66 | if(access("/dev/dsp", W_OK) == 0) | |
67 | device = "/dev/dsp"; | |
68 | else if(access("/dev/audio", W_OK) == 0) | |
69 | device = "/dev/audio"; | |
c62ae947 | 70 | else { |
a35bb7cc RK |
71 | static int reported; |
72 | ||
73 | if(!reported) { | |
74 | error(0, "cannot determine default OSS device"); | |
75 | reported = 1; | |
76 | } | |
c62ae947 RK |
77 | goto failed; |
78 | } | |
e99d42b1 | 79 | } else |
80 | device = config->device; /* just believe the user */ | |
81 | /* Open the device */ | |
82 | if((ossfd = open(device, O_WRONLY, 0)) < 0) { | |
83 | error(errno, "error opening %s", device); | |
84 | goto failed; | |
85 | } | |
86 | /* Set the audio format */ | |
87 | stereo = (config->sample_format.channels == 2); | |
88 | if(ioctl(ossfd, SNDCTL_DSP_STEREO, &stereo) < 0) { | |
89 | error(errno, "error calling ioctl SNDCTL_DSP_STEREO %d", stereo); | |
90 | goto failed; | |
91 | } | |
92 | if(config->sample_format.bits == 8) | |
93 | format = AFMT_U8; | |
94 | else if(config->sample_format.bits == 16) | |
95 | format = (config->sample_format.endian == ENDIAN_LITTLE | |
96 | ? AFMT_S16_LE : AFMT_S16_BE); | |
97 | else { | |
98 | error(0, "unsupported sample_format for oss backend"); | |
99 | goto failed; | |
100 | } | |
101 | if(ioctl(ossfd, SNDCTL_DSP_SETFMT, &format) < 0) { | |
102 | error(errno, "error calling ioctl SNDCTL_DSP_SETFMT %#x", format); | |
103 | goto failed; | |
104 | } | |
105 | rate = config->sample_format.rate; | |
106 | if(ioctl(ossfd, SNDCTL_DSP_SPEED, &rate) < 0) { | |
107 | error(errno, "error calling ioctl SNDCTL_DSP_SPEED %d", rate); | |
108 | goto failed; | |
109 | } | |
110 | if((unsigned)rate != config->sample_format.rate) | |
111 | error(0, "asked for %luHz, got %dHz", | |
112 | (unsigned long)config->sample_format.rate, rate); | |
113 | nonblock(ossfd); | |
114 | device_state = device_open; | |
115 | } | |
116 | return; | |
117 | failed: | |
118 | device_state = device_error; | |
c62ae947 | 119 | if(ossfd >= 0) { |
e99d42b1 | 120 | xclose(ossfd); |
c62ae947 RK |
121 | ossfd = -1; |
122 | } | |
e99d42b1 | 123 | } |
124 | ||
125 | /** @brief Play via OSS */ | |
126 | static size_t oss_play(size_t frames) { | |
127 | const size_t bytes_to_play = frames * bpf; | |
128 | ssize_t bytes_written; | |
129 | ||
130 | bytes_written = write(ossfd, playing->buffer + playing->start, | |
131 | bytes_to_play); | |
132 | if(bytes_written < 0) | |
133 | switch(errno) { | |
134 | case EINTR: /* interruped */ | |
135 | case EAGAIN: /* overrun */ | |
136 | return 0; /* try again later */ | |
137 | default: | |
138 | fatal(errno, "error writing to audio device"); | |
139 | } | |
140 | return bytes_written / bpf; | |
141 | } | |
142 | ||
143 | static int oss_slot; | |
144 | ||
145 | /** @brief Fill in poll fd array for OSS */ | |
e84fb5f0 | 146 | static void oss_beforepoll(int attribute((unused)) *timeoutp) { |
e99d42b1 | 147 | oss_slot = addfd(ossfd, POLLOUT|POLLERR); |
148 | } | |
149 | ||
150 | /** @brief Process poll() results for OSS */ | |
151 | static int oss_ready(void) { | |
152 | return !!(fds[oss_slot].revents & (POLLOUT|POLLERR)); | |
153 | } | |
154 | ||
155 | const struct speaker_backend oss_backend = { | |
156 | BACKEND_OSS, | |
157 | 0, | |
158 | oss_init, | |
159 | oss_activate, | |
160 | oss_play, | |
161 | oss_deactivate, | |
162 | oss_beforepoll, | |
163 | oss_ready | |
164 | }; | |
165 | ||
166 | #endif | |
167 | ||
168 | /* | |
169 | Local Variables: | |
170 | c-basic-offset:2 | |
171 | comment-column:40 | |
172 | fill-column:79 | |
173 | indent-tabs-mode:nil | |
174 | End: | |
175 | */ |