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