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