22#include <freerdp/config.h> 
   29#include <winpr/synch.h> 
   30#include <winpr/string.h> 
   31#include <winpr/thread.h> 
   32#include <winpr/cmdline.h> 
   40#include <oss-includes.h> 
   43#include <freerdp/freerdp.h> 
   44#include <freerdp/addin.h> 
   45#include <freerdp/channels/rdpsnd.h> 
   47#include "audin_main.h" 
   57  UINT32 FramesPerPacket;
 
   63  rdpContext* rdpcontext;
 
   66static void OSS_LOG_ERR(
const char* _text, 
int _error)
 
   70    char buffer[256] = { 0 };
 
   71    WLog_ERR(TAG, 
"%s: %i - %s\n", (_text), (_error),
 
   72             winpr_strerror((_error), buffer, 
sizeof(buffer)));
 
   76static UINT32 audin_oss_get_format(
const AUDIO_FORMAT* format)
 
   78  switch (format->wFormatTag)
 
   81      switch (format->wBitsPerSample)
 
  101static BOOL audin_oss_format_supported(IAudinDevice* device, 
const AUDIO_FORMAT* format)
 
  103  if (device == NULL || format == NULL)
 
  106  switch (format->wFormatTag)
 
  108    case WAVE_FORMAT_PCM:
 
  109      if (format->cbSize != 0 || format->nSamplesPerSec > 48000 ||
 
  110          (format->wBitsPerSample != 8 && format->wBitsPerSample != 16) ||
 
  111          (format->nChannels != 1 && format->nChannels != 2))
 
  128static UINT audin_oss_set_format(IAudinDevice* device, 
const AUDIO_FORMAT* format,
 
  129                                 UINT32 FramesPerPacket)
 
  131  AudinOSSDevice* oss = (AudinOSSDevice*)device;
 
  133  if (device == NULL || format == NULL)
 
  134    return ERROR_INVALID_PARAMETER;
 
  136  oss->FramesPerPacket = FramesPerPacket;
 
  137  oss->format = *format;
 
  138  return CHANNEL_RC_OK;
 
  141static DWORD WINAPI audin_oss_thread_func(LPVOID arg)
 
  143  char dev_name[PATH_MAX] = 
"/dev/dsp";
 
  146  unsigned long tmp = 0;
 
  147  size_t buffer_size = 0;
 
  148  AudinOSSDevice* oss = (AudinOSSDevice*)arg;
 
  154    error = ERROR_INVALID_PARAMETER;
 
  158  if (oss->dev_unit != -1)
 
  159    (void)sprintf_s(dev_name, (PATH_MAX - 1), 
"/dev/dsp%i", oss->dev_unit);
 
  161  WLog_INFO(TAG, 
"open: %s", dev_name);
 
  163  if ((pcm_handle = open(dev_name, O_RDONLY)) < 0)
 
  165    OSS_LOG_ERR(
"sound dev open failed", errno);
 
  166    error = ERROR_INTERNAL_ERROR;
 
  171  tmp = audin_oss_get_format(&oss->format);
 
  173  if (ioctl(pcm_handle, SNDCTL_DSP_SETFMT, &tmp) == -1)
 
  174    OSS_LOG_ERR(
"SNDCTL_DSP_SETFMT failed", errno);
 
  176  tmp = oss->format.nChannels;
 
  178  if (ioctl(pcm_handle, SNDCTL_DSP_CHANNELS, &tmp) == -1)
 
  179    OSS_LOG_ERR(
"SNDCTL_DSP_CHANNELS failed", errno);
 
  181  tmp = oss->format.nSamplesPerSec;
 
  183  if (ioctl(pcm_handle, SNDCTL_DSP_SPEED, &tmp) == -1)
 
  184    OSS_LOG_ERR(
"SNDCTL_DSP_SPEED failed", errno);
 
  186  tmp = oss->format.nBlockAlign;
 
  188  if (ioctl(pcm_handle, SNDCTL_DSP_SETFRAGMENT, &tmp) == -1)
 
  189    OSS_LOG_ERR(
"SNDCTL_DSP_SETFRAGMENT failed", errno);
 
  192      (1ull * oss->FramesPerPacket * oss->format.nChannels * (oss->format.wBitsPerSample / 8ull));
 
  193  buffer = (BYTE*)calloc((buffer_size + 
sizeof(
void*)), 
sizeof(BYTE));
 
  197    OSS_LOG_ERR(
"malloc() fail", errno);
 
  198    error = ERROR_NOT_ENOUGH_MEMORY;
 
  205    status = WaitForSingleObject(oss->stopEvent, 0);
 
  207    if (status == WAIT_FAILED)
 
  209      error = GetLastError();
 
  210      WLog_ERR(TAG, 
"WaitForSingleObject failed with error %" PRIu32 
"", error);
 
  214    if (status == WAIT_OBJECT_0)
 
  217    stmp = read(pcm_handle, buffer, buffer_size);
 
  222      OSS_LOG_ERR(
"read() error", errno);
 
  226    if ((
size_t)stmp < buffer_size) 
 
  229    if ((error = oss->receive(&oss->format, buffer, buffer_size, oss->user_data)))
 
  231      WLog_ERR(TAG, 
"oss->receive failed with error %" PRIu32 
"", error);
 
  238  if (error && oss && oss->rdpcontext)
 
  239    setChannelError(oss->rdpcontext, error, 
"audin_oss_thread_func reported an error");
 
  241  if (pcm_handle != -1)
 
  243    WLog_INFO(TAG, 
"close: %s", dev_name);
 
  257static UINT audin_oss_open(IAudinDevice* device, AudinReceive receive, 
void* user_data)
 
  259  AudinOSSDevice* oss = (AudinOSSDevice*)device;
 
  260  oss->receive = receive;
 
  261  oss->user_data = user_data;
 
  263  if (!(oss->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
 
  265    WLog_ERR(TAG, 
"CreateEvent failed!");
 
  266    return ERROR_INTERNAL_ERROR;
 
  269  if (!(oss->thread = CreateThread(NULL, 0, audin_oss_thread_func, oss, 0, NULL)))
 
  271    WLog_ERR(TAG, 
"CreateThread failed!");
 
  272    (void)CloseHandle(oss->stopEvent);
 
  273    oss->stopEvent = NULL;
 
  274    return ERROR_INTERNAL_ERROR;
 
  277  return CHANNEL_RC_OK;
 
  285static UINT audin_oss_close(IAudinDevice* device)
 
  288  AudinOSSDevice* oss = (AudinOSSDevice*)device;
 
  291    return ERROR_INVALID_PARAMETER;
 
  293  if (oss->stopEvent != NULL)
 
  295    (void)SetEvent(oss->stopEvent);
 
  297    if (WaitForSingleObject(oss->thread, INFINITE) == WAIT_FAILED)
 
  299      error = GetLastError();
 
  300      WLog_ERR(TAG, 
"WaitForSingleObject failed with error %" PRIu32 
"", error);
 
  304    (void)CloseHandle(oss->stopEvent);
 
  305    oss->stopEvent = NULL;
 
  306    (void)CloseHandle(oss->thread);
 
  311  oss->user_data = NULL;
 
  312  return CHANNEL_RC_OK;
 
  320static UINT audin_oss_free(IAudinDevice* device)
 
  322  AudinOSSDevice* oss = (AudinOSSDevice*)device;
 
  326    return ERROR_INVALID_PARAMETER;
 
  328  if ((error = audin_oss_close(device)))
 
  330    WLog_ERR(TAG, 
"audin_oss_close failed with error code %" PRIu32 
"!", error);
 
  334  return CHANNEL_RC_OK;
 
  342static UINT audin_oss_parse_addin_args(AudinOSSDevice* device, 
const ADDIN_ARGV* args)
 
  345  char* str_num = NULL;
 
  349  AudinOSSDevice* oss = device;
 
  351                                               NULL, NULL, -1, NULL, 
"audio device name" },
 
  352                                             { NULL, 0, NULL, NULL, NULL, -1, NULL, NULL } };
 
  355      COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
 
  357      CommandLineParseArgumentsA(args->argc, args->argv, audin_oss_args, flags, oss, NULL, NULL);
 
  360    return ERROR_INVALID_PARAMETER;
 
  362  arg = audin_oss_args;
 
  367    if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
 
  370    CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, 
"dev")
 
  372      str_num = _strdup(arg->Value);
 
  376        WLog_ERR(TAG, 
"_strdup failed!");
 
  377        return CHANNEL_RC_NO_MEMORY;
 
  381        long val = strtol(str_num, &eptr, 10);
 
  383        if ((errno != 0) || (val < INT32_MIN) || (val > INT32_MAX))
 
  386          return CHANNEL_RC_NULL_DATA;
 
  389        oss->dev_unit = (INT32)val;
 
  392      if (oss->dev_unit < 0 || *eptr != 
'\0')
 
  397    CommandLineSwitchEnd(arg)
 
  398  } 
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
 
  400  return CHANNEL_RC_OK;
 
  408FREERDP_ENTRY_POINT(UINT VCAPITYPE oss_freerdp_audin_client_subsystem_entry(
 
  412  AudinOSSDevice* oss = NULL;
 
  414  oss = (AudinOSSDevice*)calloc(1, 
sizeof(AudinOSSDevice));
 
  418    WLog_ERR(TAG, 
"calloc failed!");
 
  419    return CHANNEL_RC_NO_MEMORY;
 
  422  oss->iface.Open = audin_oss_open;
 
  423  oss->iface.FormatSupported = audin_oss_format_supported;
 
  424  oss->iface.SetFormat = audin_oss_set_format;
 
  425  oss->iface.Close = audin_oss_close;
 
  426  oss->iface.Free = audin_oss_free;
 
  427  oss->rdpcontext = pEntryPoints->rdpcontext;
 
  429  args = pEntryPoints->args;
 
  431  if ((error = audin_oss_parse_addin_args(oss, args)))
 
  433    WLog_ERR(TAG, 
"audin_oss_parse_addin_args failed with errorcode %" PRIu32 
"!", error);
 
  437  if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*)oss)))
 
  439    WLog_ERR(TAG, 
"RegisterAudinDevice failed with error %" PRIu32 
"!", error);
 
  443  return CHANNEL_RC_OK;