1 /**
2  * OpenAL cross platform audio library
3  * Copyright (C) 1999-2007 by authors.
4  * This library is free software; you can redistribute it and/or
5  *  modify it under the terms of the GNU Library General Public
6  *  License as published by the Free Software Foundation; either
7  *  version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  *  Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  *  License along with this library; if not, write to the
16  *  Free Software Foundation, Inc.,
17  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  * Or go to http://www.gnu.org/copyleft/lgpl.html
19  */
20 
21 #include "config.h"
22 
23 #include "backends/oss.h"
24 
25 #include <fcntl.h>
26 #include <poll.h>
27 #include <sys/ioctl.h>
28 #include <sys/stat.h>
29 #include <unistd.h>
30 
31 #include <algorithm>
32 #include <atomic>
33 #include <cerrno>
34 #include <cstdio>
35 #include <cstring>
36 #include <exception>
37 #include <functional>
38 #include <memory>
39 #include <new>
40 #include <string>
41 #include <thread>
42 #include <utility>
43 
44 #include "alcmain.h"
45 #include "alconfig.h"
casecompare(char c1,char c2)46 #include "albyte.h"
47 #include "almalloc.h"
48 #include "alnumeric.h"
49 #include "aloptional.h"
50 #include "alu.h"
51 #include "core/logging.h"
52 #include "ringbuffer.h"
53 #include "threads.h"
54 #include "vector.h"
55 
56 #include <sys/soundcard.h>
57 
58 /*
59  * The OSS documentation talks about SOUND_MIXER_READ, but the header
60  * only contains MIXER_READ. Play safe. Same for WRITE.
61  */
62 #ifndef SOUND_MIXER_READ
63 #define SOUND_MIXER_READ MIXER_READ
64 #endif
65 #ifndef SOUND_MIXER_WRITE
66 #define SOUND_MIXER_WRITE MIXER_WRITE
67 #endif
68 
69 #if defined(SOUND_VERSION) && (SOUND_VERSION < 0x040000)
70 #define ALC_OSS_COMPAT
71 #endif
72 #ifndef SNDCTL_AUDIOINFO
73 #define ALC_OSS_COMPAT
74 #endif
75 
76 /*
77  * FreeBSD strongly discourages the use of specific devices,
78  * such as those returned in oss_audioinfo.devnode
79  */
80 #ifdef __FreeBSD__
81 #define ALC_OSS_DEVNODE_TRUC
82 #endif
83 
84 namespace {
85 
86 constexpr char DefaultName[] = "OSS Default";
87 std::string DefaultPlayback{"/dev/dsp"};
88 std::string DefaultCapture{"/dev/dsp"};
89 
90 struct DevMap {
91     std::string name;
92     std::string device_name;
93 };
94 
95 al::vector<DevMap> PlaybackDevices;
96 al::vector<DevMap> CaptureDevices;
97 
98 
99 #ifdef ALC_OSS_COMPAT
100 
101 #define DSP_CAP_OUTPUT 0x00020000
102 #define DSP_CAP_INPUT 0x00010000
103 void ALCossListPopulate(al::vector<DevMap> &devlist, int type)
104 {
105     devlist.emplace_back(DevMap{DefaultName, (type==DSP_CAP_INPUT) ? DefaultCapture : DefaultPlayback});
106 }
107 
108 #else
109 
110 void ALCossListAppend(al::vector<DevMap> &list, al::span<const char> handle, al::span<const char> path)
111 {
112 #ifdef ALC_OSS_DEVNODE_TRUC
113     for(size_t i{0};i < path.size();++i)
114     {
115         if(path[i] == '.' && handle.size() + i >= path.size())
116         {
117             const size_t hoffset{handle.size() + i - path.size()};
118             if(strncmp(path.data() + i, handle.data() + hoffset, path.size() - i) == 0)
119                 handle = handle.first(hoffset);
120             path = path.first(i);
121         }
122     }
123 #endif
124     if(handle.empty())
125         handle = path;
126 
127     std::string basename{handle.data(), handle.size()};
128     std::string devname{path.data(), path.size()};
129 
130     auto match_devname = [&devname](const DevMap &entry) -> bool
131     { return entry.device_name == devname; };
132     if(std::find_if(list.cbegin(), list.cend(), match_devname) != list.cend())
133         return;
134 
135     auto checkName = [&list](const std::string &name) -> bool
136     {
137         auto match_name = [&name](const DevMap &entry) -> bool { return entry.name == name; };
138         return std::find_if(list.cbegin(), list.cend(), match_name) != list.cend();
139     };
140     int count{1};
141     std::string newname{basename};
142     while(checkName(newname))
143     {
144         newname = basename;
145         newname += " #";
146         newname += std::to_string(++count);
147     }
148 
149     list.emplace_back(DevMap{std::move(newname), std::move(devname)});
150     const DevMap &entry = list.back();
151 
152     TRACE("Got device \"%s\", \"%s\"\n", entry.name.c_str(), entry.device_name.c_str());
153 }
154 
155 void ALCossListPopulate(al::vector<DevMap> &devlist, int type_flag)
156 {
157     int fd{open("/dev/mixer", O_RDONLY)};
158     if(fd < 0)
159     {
160         TRACE("Could not open /dev/mixer: %s\n", strerror(errno));
161         goto done;
162     }
163 
164     oss_sysinfo si;
165     if(ioctl(fd, SNDCTL_SYSINFO, &si) == -1)
166     {
167         TRACE("SNDCTL_SYSINFO failed: %s\n", strerror(errno));
168         goto done;
169     }
170 
171     for(int i{0};i < si.numaudios;i++)
172     {
173         oss_audioinfo ai;
174         ai.dev = i;
175         if(ioctl(fd, SNDCTL_AUDIOINFO, &ai) == -1)
176         {
177             ERR("SNDCTL_AUDIOINFO (%d) failed: %s\n", i, strerror(errno));
178             continue;
179         }
180         if(!(ai.caps&type_flag) || ai.devnode[0] == '\0')
181             continue;
182 
183         al::span<const char> handle;
184         if(ai.handle[0] != '\0')
185             handle = {ai.handle, strnlen(ai.handle, sizeof(ai.handle))};
186         else
187             handle = {ai.name, strnlen(ai.name, sizeof(ai.name))};
188         al::span<const char> devnode{ai.devnode, strnlen(ai.devnode, sizeof(ai.devnode))};
189 
190         ALCossListAppend(devlist, handle, devnode);
191     }
192 
193 done:
194     if(fd >= 0)
195         close(fd);
196     fd = -1;
197 
198     const char *defdev{((type_flag==DSP_CAP_INPUT) ? DefaultCapture : DefaultPlayback).c_str()};
199     auto iter = std::find_if(devlist.cbegin(), devlist.cend(),
200         [defdev](const DevMap &entry) -> bool
201         { return entry.device_name == defdev; }
202     );
203     if(iter == devlist.cend())
204         devlist.insert(devlist.begin(), DevMap{DefaultName, defdev});
205     else
206     {
207         DevMap entry{std::move(*iter)};
208         devlist.erase(iter);
209         devlist.insert(devlist.begin(), std::move(entry));
210     }
211     devlist.shrink_to_fit();
212 }
213 
214 #endif
215 
216 uint log2i(uint x)
217 {
218     uint y{0};
219     while(x > 1)
220     {
221         x >>= 1;
222         y++;
223     }
224     return y;
225 }
226 
227 
228 struct OSSPlayback final : public BackendBase {
229     OSSPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
230     ~OSSPlayback() override;
231 
232     int mixerProc();
233 
234     void open(const char *name) override;
235     bool reset() override;
236     void start() override;
237     void stop() override;
238 
239     int mFd{-1};
240 
241     al::vector<al::byte> mMixData;
242 
243     std::atomic<bool> mKillNow{true};
244     std::thread mThread;
245 
246     DEF_NEWDEL(OSSPlayback)
247 };
248 
249 OSSPlayback::~OSSPlayback()
250 {
251     if(mFd != -1)
252         close(mFd);
253     mFd = -1;
254 }
255 
256 
257 int OSSPlayback::mixerProc()
258 {
259     SetRTPriority();
260     althrd_setname(MIXER_THREAD_NAME);
261 
262     const size_t frame_step{mDevice->channelsFromFmt()};
263     const size_t frame_size{mDevice->frameSizeFromFmt()};
264 
265     while(!mKillNow.load(std::memory_order_acquire)
266         && mDevice->Connected.load(std::memory_order_acquire))
267     {
268         pollfd pollitem{};
269         pollitem.fd = mFd;
270         pollitem.events = POLLOUT;
271 
272         int pret{poll(&pollitem, 1, 1000)};
273         if(pret < 0)
274         {
275             if(errno == EINTR || errno == EAGAIN)
276                 continue;
277             ERR("poll failed: %s\n", strerror(errno));
278             mDevice->handleDisconnect("Failed waiting for playback buffer: %s", strerror(errno));
279             break;
280         }
281         else if(pret == 0)
282         {
283             WARN("poll timeout\n");
284             continue;
285         }
286 
287         al::byte *write_ptr{mMixData.data()};
288         size_t to_write{mMixData.size()};
289         mDevice->renderSamples(write_ptr, static_cast<uint>(to_write/frame_size), frame_step);
290         while(to_write > 0 && !mKillNow.load(std::memory_order_acquire))
291         {
292             ssize_t wrote{write(mFd, write_ptr, to_write)};
293             if(wrote < 0)
294             {
295                 if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
296                     continue;
297                 ERR("write failed: %s\n", strerror(errno));
298                 mDevice->handleDisconnect("Failed writing playback samples: %s", strerror(errno));
299                 break;
300             }
301 
302             to_write -= static_cast<size_t>(wrote);
303             write_ptr += wrote;
304         }
305     }
306 
307     return 0;
308 }
309 
310 
311 void OSSPlayback::open(const char *name)
312 {
313     const char *devname{DefaultPlayback.c_str()};
314     if(!name)
315         name = DefaultName;
316     else
317     {
318         if(PlaybackDevices.empty())
319             ALCossListPopulate(PlaybackDevices, DSP_CAP_OUTPUT);
320 
321         auto iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(),
322             [&name](const DevMap &entry) -> bool
323             { return entry.name == name; }
324         );
325         if(iter == PlaybackDevices.cend())
326             throw al::backend_exception{al::backend_error::NoDevice,
327                 "Device name \"%s\" not found", name};
328         devname = iter->device_name.c_str();
329     }
330 
331     mFd = ::open(devname, O_WRONLY);
332     if(mFd == -1)
333         throw al::backend_exception{al::backend_error::NoDevice, "Could not open %s: %s", devname,
334             strerror(errno)};
335 
336     mDevice->DeviceName = name;
337 }
338 
339 bool OSSPlayback::reset()
340 {
341     int ossFormat{};
342     switch(mDevice->FmtType)
343     {
344         case DevFmtByte:
345             ossFormat = AFMT_S8;
346             break;
347         case DevFmtUByte:
348             ossFormat = AFMT_U8;
349             break;
350         case DevFmtUShort:
351         case DevFmtInt:
352         case DevFmtUInt:
353         case DevFmtFloat:
354             mDevice->FmtType = DevFmtShort;
355             /* fall-through */
356         case DevFmtShort:
357             ossFormat = AFMT_S16_NE;
358             break;
359     }
360 
361     uint periods{mDevice->BufferSize / mDevice->UpdateSize};
362     uint numChannels{mDevice->channelsFromFmt()};
363     uint ossSpeed{mDevice->Frequency};
364     uint frameSize{numChannels * mDevice->bytesFromFmt()};
365     /* According to the OSS spec, 16 bytes (log2(16)) is the minimum. */
366     uint log2FragmentSize{maxu(log2i(mDevice->UpdateSize*frameSize), 4)};
367     uint numFragmentsLogSize{(periods << 16) | log2FragmentSize};
368 
369     audio_buf_info info{};
370     const char *err;
371 #define CHECKERR(func) if((func) < 0) {                                       \
372     err = #func;                                                              \
373     goto err;                                                                 \
374 }
375     /* Don't fail if SETFRAGMENT fails. We can handle just about anything
376      * that's reported back via GETOSPACE */
377     ioctl(mFd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize);
378     CHECKERR(ioctl(mFd, SNDCTL_DSP_SETFMT, &ossFormat));
379     CHECKERR(ioctl(mFd, SNDCTL_DSP_CHANNELS, &numChannels));
380     CHECKERR(ioctl(mFd, SNDCTL_DSP_SPEED, &ossSpeed));
381     CHECKERR(ioctl(mFd, SNDCTL_DSP_GETOSPACE, &info));
382     if(0)
383     {
384     err:
385         ERR("%s failed: %s\n", err, strerror(errno));
386         return false;
387     }
388 #undef CHECKERR
389 
390     if(mDevice->channelsFromFmt() != numChannels)
391     {
392         ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(mDevice->FmtChans),
393             numChannels);
394         return false;
395     }
396 
397     if(!((ossFormat == AFMT_S8 && mDevice->FmtType == DevFmtByte) ||
398          (ossFormat == AFMT_U8 && mDevice->FmtType == DevFmtUByte) ||
399          (ossFormat == AFMT_S16_NE && mDevice->FmtType == DevFmtShort)))
400     {
401         ERR("Failed to set %s samples, got OSS format %#x\n", DevFmtTypeString(mDevice->FmtType),
402             ossFormat);
403         return false;
404     }
405 
406     mDevice->Frequency = ossSpeed;
407     mDevice->UpdateSize = static_cast<uint>(info.fragsize) / frameSize;
408     mDevice->BufferSize = static_cast<uint>(info.fragments) * mDevice->UpdateSize;
409 
410     setDefaultChannelOrder();
411 
412     mMixData.resize(mDevice->UpdateSize * mDevice->frameSizeFromFmt());
413 
414     return true;
415 }
416 
417 void OSSPlayback::start()
418 {
419     try {
420         mKillNow.store(false, std::memory_order_release);
421         mThread = std::thread{std::mem_fn(&OSSPlayback::mixerProc), this};
422     }
423     catch(std::exception& e) {
424         throw al::backend_exception{al::backend_error::DeviceError,
425             "Failed to start mixing thread: %s", e.what()};
426     }
427 }
428 
429 void OSSPlayback::stop()
430 {
431     if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
432         return;
433     mThread.join();
434 
435     if(ioctl(mFd, SNDCTL_DSP_RESET) != 0)
436         ERR("Error resetting device: %s\n", strerror(errno));
437 }
438 
439 
440 struct OSScapture final : public BackendBase {
441     OSScapture(ALCdevice *device) noexcept : BackendBase{device} { }
442     ~OSScapture() override;
443 
444     int recordProc();
445 
446     void open(const char *name) override;
447     void start() override;
448     void stop() override;
449     void captureSamples(al::byte *buffer, uint samples) override;
450     uint availableSamples() override;
451 
452     int mFd{-1};
453 
454     RingBufferPtr mRing{nullptr};
455 
456     std::atomic<bool> mKillNow{true};
457     std::thread mThread;
458 
459     DEF_NEWDEL(OSScapture)
460 };
461 
462 OSScapture::~OSScapture()
463 {
464     if(mFd != -1)
465         close(mFd);
466     mFd = -1;
467 }
468 
469 
470 int OSScapture::recordProc()
471 {
472     SetRTPriority();
473     althrd_setname(RECORD_THREAD_NAME);
474 
475     const size_t frame_size{mDevice->frameSizeFromFmt()};
476     while(!mKillNow.load(std::memory_order_acquire))
477     {
478         pollfd pollitem{};
479         pollitem.fd = mFd;
480         pollitem.events = POLLIN;
481 
482         int sret{poll(&pollitem, 1, 1000)};
483         if(sret < 0)
484         {
485             if(errno == EINTR || errno == EAGAIN)
486                 continue;
487             ERR("poll failed: %s\n", strerror(errno));
488             mDevice->handleDisconnect("Failed to check capture samples: %s", strerror(errno));
489             break;
490         }
491         else if(sret == 0)
492         {
493             WARN("poll timeout\n");
494             continue;
495         }
496 
497         auto vec = mRing->getWriteVector();
498         if(vec.first.len > 0)
499         {
500             ssize_t amt{read(mFd, vec.first.buf, vec.first.len*frame_size)};
501             if(amt < 0)
502             {
503                 ERR("read failed: %s\n", strerror(errno));
504                 mDevice->handleDisconnect("Failed reading capture samples: %s", strerror(errno));
505                 break;
506             }
507             mRing->writeAdvance(static_cast<size_t>(amt)/frame_size);
508         }
509     }
510 
511     return 0;
512 }
513 
514 
515 void OSScapture::open(const char *name)
516 {
517     const char *devname{DefaultCapture.c_str()};
518     if(!name)
519         name = DefaultName;
520     else
521     {
522         if(CaptureDevices.empty())
523             ALCossListPopulate(CaptureDevices, DSP_CAP_INPUT);
524 
525         auto iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(),
526             [&name](const DevMap &entry) -> bool
527             { return entry.name == name; }
528         );
529         if(iter == CaptureDevices.cend())
530             throw al::backend_exception{al::backend_error::NoDevice,
531                 "Device name \"%s\" not found", name};
532         devname = iter->device_name.c_str();
533     }
534 
535     mFd = ::open(devname, O_RDONLY);
536     if(mFd == -1)
537         throw al::backend_exception{al::backend_error::NoDevice, "Could not open %s: %s", devname,
538             strerror(errno)};
539 
540     int ossFormat{};
541     switch(mDevice->FmtType)
542     {
543     case DevFmtByte:
544         ossFormat = AFMT_S8;
545         break;
546     case DevFmtUByte:
547         ossFormat = AFMT_U8;
548         break;
549     case DevFmtShort:
550         ossFormat = AFMT_S16_NE;
551         break;
552     case DevFmtUShort:
553     case DevFmtInt:
554     case DevFmtUInt:
555     case DevFmtFloat:
556         throw al::backend_exception{al::backend_error::DeviceError,
557             "%s capture samples not supported", DevFmtTypeString(mDevice->FmtType)};
558     }
559 
560     uint periods{4};
561     uint numChannels{mDevice->channelsFromFmt()};
562     uint frameSize{numChannels * mDevice->bytesFromFmt()};
563     uint ossSpeed{mDevice->Frequency};
564     /* according to the OSS spec, 16 bytes are the minimum */
565     uint log2FragmentSize{maxu(log2i(mDevice->BufferSize * frameSize / periods), 4)};
566     uint numFragmentsLogSize{(periods << 16) | log2FragmentSize};
567 
568     audio_buf_info info{};
569 #define CHECKERR(func) if((func) < 0) {                                       \
570     throw al::backend_exception{al::backend_error::DeviceError, #func " failed: %s", \
571         strerror(errno)};                                                     \
572 }
573     CHECKERR(ioctl(mFd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize));
574     CHECKERR(ioctl(mFd, SNDCTL_DSP_SETFMT, &ossFormat));
575     CHECKERR(ioctl(mFd, SNDCTL_DSP_CHANNELS, &numChannels));
576     CHECKERR(ioctl(mFd, SNDCTL_DSP_SPEED, &ossSpeed));
577     CHECKERR(ioctl(mFd, SNDCTL_DSP_GETISPACE, &info));
578 #undef CHECKERR
579 
580     if(mDevice->channelsFromFmt() != numChannels)
581         throw al::backend_exception{al::backend_error::DeviceError,
582             "Failed to set %s, got %d channels instead", DevFmtChannelsString(mDevice->FmtChans),
583             numChannels};
584 
585     if(!((ossFormat == AFMT_S8 && mDevice->FmtType == DevFmtByte)
586         || (ossFormat == AFMT_U8 && mDevice->FmtType == DevFmtUByte)
587         || (ossFormat == AFMT_S16_NE && mDevice->FmtType == DevFmtShort)))
588         throw al::backend_exception{al::backend_error::DeviceError,
589             "Failed to set %s samples, got OSS format %#x", DevFmtTypeString(mDevice->FmtType),
590             ossFormat};
591 
592     mRing = RingBuffer::Create(mDevice->BufferSize, frameSize, false);
593 
594     mDevice->DeviceName = name;
595 }
596 
597 void OSScapture::start()
598 {
599     try {
600         mKillNow.store(false, std::memory_order_release);
601         mThread = std::thread{std::mem_fn(&OSScapture::recordProc), this};
602     }
603     catch(std::exception& e) {
604         throw al::backend_exception{al::backend_error::DeviceError,
605             "Failed to start recording thread: %s", e.what()};
606     }
607 }
608 
609 void OSScapture::stop()
610 {
611     if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
612         return;
613     mThread.join();
614 
615     if(ioctl(mFd, SNDCTL_DSP_RESET) != 0)
616         ERR("Error resetting device: %s\n", strerror(errno));
617 }
618 
619 void OSScapture::captureSamples(al::byte *buffer, uint samples)
620 { mRing->read(buffer, samples); }
621 
622 uint OSScapture::availableSamples()
623 { return static_cast<uint>(mRing->readSpace()); }
624 
625 } // namespace
626 
627 
628 BackendFactory &OSSBackendFactory::getFactory()
629 {
630     static OSSBackendFactory factory{};
631     return factory;
632 }
633 
634 bool OSSBackendFactory::init()
635 {
636     if(auto devopt = ConfigValueStr(nullptr, "oss", "device"))
637         DefaultPlayback = std::move(*devopt);
638     if(auto capopt = ConfigValueStr(nullptr, "oss", "capture"))
639         DefaultCapture = std::move(*capopt);
640 
641     return true;
642 }
643 
644 bool OSSBackendFactory::querySupport(BackendType type)
645 { return (type == BackendType::Playback || type == BackendType::Capture); }
646 
647 std::string OSSBackendFactory::probe(BackendType type)
648 {
649     std::string outnames;
650 
651     auto add_device = [&outnames](const DevMap &entry) -> void
652     {
653         struct stat buf;
654         if(stat(entry.device_name.c_str(), &buf) == 0)
655         {
656             /* Includes null char. */
657             outnames.append(entry.name.c_str(), entry.name.length()+1);
658         }
659     };
660 
661     switch(type)
662     {
663     case BackendType::Playback:
664         PlaybackDevices.clear();
665         ALCossListPopulate(PlaybackDevices, DSP_CAP_OUTPUT);
666         std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
667         break;
668 
669     case BackendType::Capture:
670         CaptureDevices.clear();
671         ALCossListPopulate(CaptureDevices, DSP_CAP_INPUT);
672         std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
673         break;
674     }
675 
676     return outnames;
677 }
678 
679 BackendPtr OSSBackendFactory::createBackend(ALCdevice *device, BackendType type)
680 {
681     if(type == BackendType::Playback)
682         return BackendPtr{new OSSPlayback{device}};
683     if(type == BackendType::Capture)
684         return BackendPtr{new OSScapture{device}};
685     return nullptr;
686 }
687