ioctl.c revision 9acbbeaf2a1ffe5c14b244867d427714fab43c5c
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <assert.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include <limits.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stropts.h>
#include <strings.h>
#include <thread.h>
#include <errno.h>
#include <libintl.h>
#include <sys/lx_autofs.h>
#include <sys/lx_socket.h>
#include <sys/lx_audio.h>
#include <sys/lx_ioctl.h>
#include <sys/lx_debug.h>
/* Define _KERNEL to get the devt manipulation macros. */
#define _KERNEL
#include <sys/sysmacros.h>
/* Maximum number of modules on a stream that we can handle. */
#define MAX_STRMODS 10
/* Maximum buffer size for debugging messages. */
#define MSGBUF 1024
/* Structure used to define an ioctl translator. */
typedef struct ioc_cmd_translator {
int ict_lx_cmd;
char *ict_lx_cmd_str;
int ict_cmd;
char *ict_cmd_str;
/*
* Structures used to associate a group of ioctl translators with
* a specific device.
*/
typedef struct ioc_dev_translator {
char *idt_driver;
/* Array of command translators. */
/*
* Structures used to associate a group of ioctl translators with
* a specific filesystem.
*/
typedef struct ioc_fs_translator {
char *ift_filesystem;
/* Array of command translators. */
/* Structure used to define a unsupported ioctl error codes. */
typedef struct ioc_errno_translator {
int iet_lx_cmd;
char *iet_lx_cmd_str;
int iet_errno;
/* Structure used to convert oss format flags into Solaris options. */
typedef struct oss_fmt_translator {
int oft_oss_fmt;
int oft_encoding;
int oft_precision;
/* Translator forward declerations. */
static oss_fmt_translator_t oft_table[];
static ioc_cmd_translator_t ioc_translators_file[];
static ioc_cmd_translator_t ioc_translators_fifo[];
static ioc_cmd_translator_t ioc_translators_sock[];
static ioc_dev_translator_t *ioc_translators_dev[];
static ioc_fs_translator_t *ioc_translators_fs[];
static ioc_errno_translator_t ioc_translators_errno[];
/*
* Interface name table.
*/
typedef struct ifname_map {
char im_solaris[IFNAMSIZ];
struct ifname_map *im_next;
} ifname_map_t;
static ifname_map_t *ifname_map;
static mutex_t ifname_mtx;
/*
* Macros and structures to help convert integers to string
* values that they represent (for displaying in debug output).
*/
#define I2S_ENTRY(x) { x, #x },
typedef struct int2str {
int i2s_int;
char *i2s_str;
} int2str_t;
static int2str_t st_mode_strings[] = {
};
static int2str_t oss_fmt_str[] = {
};
static void
{
int errno_backup = errno;
if (lx_debug_enabled == 0)
return;
path = "?";
if (lx_cmd_str == NULL)
lx_cmd_str = "?";
/* Display the initial error message and extended ioctl information. */
lx_debug("\tlx_ioctl(): cmd = 0x%x - %s, fd = %d - %s",
/* Display information about the target file, if it's available. */
char *fd_mode_str = "unknown";
char buf[LX_MSG_MAXLEN];
int i;
/* Translate the file type bits into a string. */
continue;
break;
}
"\tlx_ioctl(): mode = %s", fd_mode_str);
int i;
/* This is a device so display the devt. */
/* Try to display the drivers name. */
if (modctl(MODGETNAME,
"; driver = %s", fd_driver);
}
}
/* Restore errno. */
}
static int
{
int i;
/* Get the number of modules on the stream. */
lx_debug("\tioctl(%d, 0x%x - %s, ...)",
lx_debug("\tldlinux_check(): unable to count stream modules");
return (-errno);
}
/* Sanity check the number of modules on the stream. */
assert(i <= MAX_STRMODS);
/* Get the list of modules on the stream. */
lx_debug("\tioctl(%d, 0x%x - %s, ...)",
lx_debug("\tldlinux_check(): unable to list stream modules");
return (-errno);
}
return (1);
return (0);
}
static int
{
return (-1);
return (0);
}
/*
* Add an interface name mapping if it doesn't already exist.
*
* Interfaces with IFF_LOOPBACK flag get renamed to loXXX.
* Interfaces with IFF_BROADCAST flag get renamed to ethXXX.
*
* Caller locks the name table.
*/
static int
{
static int eth_index = 0;
static int lo_index = 0;
return (0);
return (-1);
if (if_flags & IFF_LOOPBACK) {
/* Loopback */
if (lo_index == 0)
else
"lo:%d", lo_index);
lo_index++;
} else if (if_flags & IFF_BROADCAST) {
/* Assume ether if it has a broadcast address */
"eth%d", eth_index);
eth_index++;
} else {
/* Do not translate unknown interfaces */
}
return (0);
}
static int
{
}
/*
* (Re-)scan all interfaces and add them to the name table.
* Caller locks the name table.
*/
static int
ifname_scan(void)
{
goto fail;
lx_debug("\tifname_scan(): unable to get number of interfaces");
goto fail;
}
goto fail;
lx_debug("\tifname_scan(): unable to get interfaces");
goto fail;
}
/* Get the interface flags */
for (i = 0; i < ifcount; i++) {
lx_debug("\tioctl(%d, 0x%x - %s, ...)",
lx_debug("\tifname_scan(): unable to get flags for %s",
}
}
/*
* Sort the interfaces by name to preserve the order
* across reboots of this zone. Note that the order of
* interface names won't be consistent across network
* configuration changes. ie. If network interfaces
* are added or removed from a zone (either dynamically
* or statically) the network interfaces names to physical
* network interface mappings that linux apps see may
* change.
*/
/* Add to the name table */
for (i = 0; i < ifcount; i++)
goto fail;
return (0);
fail:
if (fd >= 0)
return (-1);
}
static int
ifname_from_linux(char *name)
{
int pass;
(void) mutex_lock(&ifname_mtx);
break;
break;
}
(void) mutex_unlock(&ifname_mtx);
if (im_p) {
return (0);
}
return (-1);
}
static int
ifname_from_solaris(char *name)
{
int pass;
(void) mutex_lock(&ifname_mtx);
break;
break;
}
(void) mutex_unlock(&ifname_mtx);
if (im_p) {
return (0);
}
return (-1);
}
/*
* Called to initialize the ioctl translation subsystem.
*/
int
{
int i, ret;
/* Figure out the major numbers for our devices translators. */
for (i = 0; ioc_translators_dev[i] != NULL; i++) {
if (ret != 0) {
"lx_ioctl_init(): modctl(MODGETMAJBIND, ",
}
}
/* Create the interface name table */
if (ifname_scan() != 0)
lx_err("lx_ioctl_init(): ifname_scan() failed\n");
return (0);
}
static ioc_cmd_translator_t *
{
return (ict);
ict++;
}
return (NULL);
}
/*
* Main entry point for the ioctl translater.
*/
int
{
int i, ret;
"lx_ioctl(): fstat() failed");
/*
* Linux ioctl(2) is only documented to return EBADF, EFAULT,
* EINVAL or ENOTTY.
*
* EINVAL is documented to be "Request or argp is not valid",
* so it's reasonable to force any errno that's not EBADF,
* EFAULT or ENOTTY to be EINVAL.
*/
return (-errno); /* errno already set. */
}
default:
break;
case S_IFREG:
/* Use file translators. */
break;
case S_IFSOCK:
/* Use socket translators. */
break;
case S_IFIFO:
/* Use fifo translators. */
break;
case S_IFCHR:
/*
* Look through all the device translators to see if there
* is one for this device.
*/
for (i = 0; ioc_translators_dev[i] != NULL; i++) {
continue;
/* We found a translator for this device. */
break;
}
break;
}
/*
* Search the selected translator group to see if we have a
* translator for this specific command.
*/
/* We found a translator for this command, invoke it. */
"lx_ioctl(): emulating ioctl");
arg);
return (ret);
}
/*
* If we didn't find a file or device translator for this
* command then try to find a filesystem translator for
* this command.
*/
for (i = 0; ioc_translators_fs[i] != NULL; i++) {
ioc_translators_fs[i]->ift_filesystem) != 0)
continue;
/* We found a translator for this filesystem. */
break;
}
/*
* Search the selected translator group to see if we have a
* translator for this specific command.
*/
/* We found a translator for this command, invoke it. */
"lx_ioctl(): emulating ioctl");
arg);
return (ret);
}
/*
* No translator for this ioctl was found.
* Check if there is an errno translator.
*/
continue;
/* We found a an errno translator for this ioctl. */
"lx_ioctl(): emulating errno");
return (ret);
}
"lx_ioctl(): unsupported linux ioctl");
cmd);
return (-EINVAL);
}
/*
* Ioctl translator functions.
*/
/*
* Used by translators that want to explicitly return EINVAL for an
* ioctl(2) instead of having the translation framework do it implicitly.
* This allows us to indicate which unsupported ioctl(2)s should not
* trigger a SIGSYS when running in LX_STRICT mode.
*/
/* ARGSUSED */
static int
{
return (-EINVAL);
}
static int
/*ARGSUSED*/
{
int ret;
lx_debug("\tioctl(%d, 0x%x - %s, ...)",
}
static int
/*ARGSUSED*/
{
lx_debug("\tioctl(%d, 0x%x - %s, ...)",
}
static int
/*ARGSUSED*/
{
return (-errno);
/*
* Linux expects a SIOCATMARK of a UDP socket to return EINVAL, while
* Solaris allows it.
*/
return (-EINVAL);
}
return (-EINVAL);
return (-errno);
return (-errno);
return (0);
}
static int
/*ARGSUSED*/
{
/* Copy in the data */
return (-errno);
return (-EINVAL);
lx_debug("\tioctl(%d, 0x%x - %s, %.14s",
return (-errno);
return (-EINVAL);
/* Copy out the data */
return (-errno);
return (0);
}
static int
/*ARGSUSED*/
{
/* Copy in the data. */
return (-errno);
/* They want to know how many interfaces there are. */
lx_debug("\tioctl(%d, 0x%x - %s, ...)",
return (-errno);
/* Check if we're done. */
/* Copy out the data. */
return (-errno);
return (0);
}
}
/* Get interface configuration list. */
if (ret < 0)
return (-errno);
/* Rename interfaces to linux */
return (-EINVAL);
/* Copy out the data */
return (-errno);
return (0);
}
static int
/*ARGSUSED*/
{
/* Copy in the data */
return (-errno);
lx_debug("\tioctl(%d, 0x%x - %s, lx %.14s)",
/*
* We're not going to support SIOCSIFHWADDR, but we need to be
* able to check the result of the uucopy first to see if the command
* should have returned EFAULT.
*/
if (cmd == LX_SIOCSIFHWADDR) {
"lx_ioctl(): unsupported linux ioctl: %s"),
"SIOCSIFHWADDR");
return (-EINVAL);
}
/* Abuse ifr_addr for linux ifr_hwaddr */
/* Copy out the data */
return (-errno);
return (0);
}
return (-EINVAL);
lx_debug("\tioctl(%d, 0x%x - %s, %.14s)",
return (-errno);
return (-errno);
return (-EINVAL);
/* Abuse ifr_addr for linux ifr_hwaddr */
else
/* Copy out the data */
return (-errno);
return (0);
}
static void
{
} else {
}
}
static void
{
} else {
}
}
static void
{
}
static void
{
}
static void
{
} else {
}
}
static void
{
} else {
}
}
static int
/*ARGSUSED*/
{
/* Copy in the data. */
return (-errno);
/*
* ldlinux strmod. So make sure the module exists on the
* target stream before we invoke the ioctl.
*/
return (ldlinux);
if (ldlinux == 1) {
return (-errno);
}
lx_debug("\tioctl(%d, 0x%x - %s, ...)",
}
static int
/*ARGSUSED*/
{
/* Copy in the data. */
return (-errno);
/*
* ldlinux strmod. So make sure the module exists on the
* target stream before we invoke the ioctl.
*/
return (ldlinux);
if (ldlinux == 1) {
return (-errno);
}
lx_debug("\tioctl(%d, 0x%x - %s, ...)",
}
/*
* The Solaris TIOCGPGRP ioctl does not have exactly the same semantics as
* the Linux one. To mimic Linux semantics we have to do some extra work
* normally done by the Solaris version of tcgetpgrp().
*/
static int
/*ARGSUSED*/
{
int ret;
lx_debug("\tioctl(%d, 0x%x - %s, ...)",
return (-errno);
return (-errno);
return (-ENOTTY);
lx_debug("\tioctl(%d, 0x%x - %s, ...)",
}
static int
/*ARGSUSED*/
{
/*
* and Solaris. Linux expects 0 or -1. Solaris can return
* positive number on success.
*/
return (-errno);
return (0);
}
static int
/*ARGSUSED*/
{
/* This operation is only valid for the lx_ptm device. */
/* Copy out the data. */
return (-errno);
return (0);
}
static int
/*ARGSUSED*/
{
return (0);
return (-errno);
return (-errno);
return (0);
}
static int
/*ARGSUSED*/
{
return (-errno);
/* Now munge the data to how Linux wants it. */
return (-errno);
return (0);
}
static int
/*ARGSUSED*/
{
int ldlinux;
return (ldlinux);
lx_debug("\tioctl(%d, 0x%x - %s, ...)",
return (-errno);
/* Now munge the data to how Linux wants it. */
/* Copy out the data. */
return (-errno);
/*
* ldlinux strmod. So make sure the module exists on the
* target stream before we invoke the ioctl.
*/
if (ldlinux == 0)
return (0);
return (-errno);
/* Copy out the data. */
return (-errno);
return (0);
}
static int
/*ARGSUSED*/
{
int ldlinux;
return (ldlinux);
lx_debug("\tioctl(%d, 0x%x - %s, ...)",
return (-errno);
/* Now munge the data to how Linux wants it. */
/* Copy out the data. */
return (-errno);
/*
* ldlinux strmod. So make sure the module exists on the
* target stream before we invoke the ioctl.
*/
if (ldlinux == 0)
return (0);
return (-errno);
/* Copy out the data. */
return (-errno);
return (0);
}
static int
/*ARGSUSED*/
{
return (-errno);
/* Check if this fd is already our ctty. */
lx_debug("\tioctl(%d, 0x%x - %s, ...)",
return (0);
/*
* Need to make sure we're a session leader, otherwise the
* TIOCSCTTY ioctl will fail.
*/
(void) setpgrp();
lx_debug("\tioctl(%d, 0x%x - %s, ...)",
return (-errno);
return (0);
}
/*
*/
static int
i_is_dsp_dev(int fd)
{
int minor;
/*
* This is a cloning device so we have to ask the driver
* what kind of minor node this is.
*/
lx_debug("\tioctl(%d, 0x%x - %s, ...)",
return (-EINVAL);
if (minor != LXA_MINORNUM_DSP)
return (-EINVAL);
return (0);
}
static int
/*ARGSUSED*/
{
int err;
/* Ioctl is only supported on dsp audio devices. */
return (err);
/* Nothing to really do on Solaris. */
return (0);
}
static void
{
int i, first = 1;
buf[0] = '\0';
continue;
if (first)
first = 0;
else
}
}
static int
/*ARGSUSED*/
{
/* Ioctl is only supported on dsp audio devices. */
return (err);
/* We need to know the access mode for the file. */
return (-EINVAL);
/* Test to see what Linux oss formats the target device supports. */
for (i = 0; oft_table[i].oft_oss_fmt != 0; i++) {
/* Initialize the mode request. */
/* Translate a Linux oss format into Solaris settings. */
}
}
/* Send the request. */
lx_debug("\tioctl(%d, 0x%x - %s, ...)",
continue;
/* This Linux oss format is supported. */
}
if (lx_debug_enabled != 0) {
}
return (-errno);
return (0);
}
static int
/*ARGSUSED*/
{
/* Ioctl is only supported on dsp audio devices. */
return (err);
return (-errno);
if (lx_debug_enabled != 0) {
}
lx_debug("\tXXX: possible oss formats query?");
return (-EINVAL);
}
/* Check if multiple format bits were specified. */
if (!BIT_ONLYONESET(mask))
return (-EINVAL);
/* Decode the oss format request into a native format. */
for (i = 0; oft_table[i].oft_oss_fmt != 0; i++) {
break;
}
if (oft_table[i].oft_oss_fmt == 0)
return (-EINVAL);
/* We need to know the access mode for the file. */
return (-EINVAL);
/* Initialize the mode request. */
/* Translate the Linux oss request into a Solaris request. */
}
}
/* Send the request. */
lx_debug("\tioctl(%d, 0x%x - %s, ...)",
}
static int
/*ARGSUSED*/
{
(cmd == LX_OSS_SNDCTL_DSP_STEREO));
/* Ioctl is only supported on dsp audio devices. */
return (err);
return (-errno);
lx_debug("\toss %s request = 0x%x (%u)",
return (-EINVAL);
}
if (cmd == LX_OSS_SNDCTL_DSP_STEREO) {
/*
* There doesn't seem to be any documentation for
* SNDCTL_DSP_STEREO. Looking at source that uses or
* used this ioctl seems to indicate that the
* functionality provided by this ioctl has been
* subsumed by the SNDCTL_DSP_CHANNELS ioctl. It
* seems that the only arguments ever passed to
* the SNDCTL_DSP_STEREO. Ioctl are boolean values
* of '0' or '1'. Hence we'll start out strict and
* only support those values.
*
* Some online forum discussions about this ioctl
* seemed to indicate that in case of success it
* returns the "stereo" setting (ie, either
* '0' for mono or '1' for stereo).
*/
lx_debug("\tinvalid stereo request");
return (-EINVAL);
}
channels += 1;
} else {
/* Limit the system to one or two channels. */
lx_debug("\tinvalid channel request");
return (-EINVAL);
}
}
/* We need to know the access mode for the file. */
return (-EINVAL);
/* Initialize the channel request. */
/* Translate the Linux oss request into a Solaris request. */
/* Send the request. */
lx_debug("\tioctl(%d, 0x%x - %s, ...)",
return (-errno);
if (cmd == LX_OSS_SNDCTL_DSP_STEREO)
return (channels - 1);
return (0);
}
static int
/*ARGSUSED*/
{
/* Ioctl is only supported on dsp audio devices. */
return (err);
return (-errno);
lx_debug("\tXXX: possible oss speed query?");
return (-EINVAL);
}
/* We need to know the access mode for the file. */
return (-EINVAL);
/* Initialize the speed request. */
/* Translate the Linux oss request into a Solaris request. */
/* Send the request. */
lx_debug("\tioctl(%d, 0x%x - %s, ...)",
}
static int
/*ARGSUSED*/
{
int err;
/* Ioctl is only supported on dsp audio devices. */
return (err);
/* Query the current fragment count and size. */
lx_debug("\tioctl(%d, 0x%x - %s, ...)",
return (-errno);
return (-errno);
return (0);
}
static int
/*ARGSUSED*/
{
int err;
(cmd == LX_OSS_SNDCTL_DSP_GETISPACE));
/* Ioctl is only supported on dsp audio devices. */
return (err);
/* Query the current fragment count and size. */
lx_debug("\tioctl(%d, 0x%x - %s, ...)",
return (-errno);
/* Return the current fragment count and size. */
/*
* We'll lie and tell applications that they can always write
* out at least one fragment without blocking.
*/
if (cmd == LX_OSS_SNDCTL_DSP_GETOSPACE)
lx_debug("\toss get output space result = ");
if (cmd == LX_OSS_SNDCTL_DSP_GETISPACE)
lx_debug("\toss get input space result = ");
lx_debug("\t\tbytes = 0x%x (%u), fragments = 0x%x (%u)",
lx_debug("\t\tfragtotal = 0x%x (%u), fragsize = 0x%x (%u)",
return (-errno);
return (0);
}
static int
/*ARGSUSED*/
{
int err;
/* Ioctl is only supported on dsp audio devices. */
return (err);
return (-errno);
/*
* The argument to this ioctl is a 32-bit integer of the
* format 0x MMMM SSSS where:
* SSSS - requests a fragment size of 2^SSSS
* MMMM - requests a maximum fragment count of 2^MMMM
* if MMMM is 0x7fff then the application is requesting
* no limits on the number of fragments.
*/
lx_debug("\toss fragment request: "
"power size = 0x%x (%u), power cnt = 0x%x (%u)",
/* Limit the supported fragment size from 2^4 to 2^31. */
return (-EINVAL);
/* Limit the number of fragments from 2^1 to 2^32. */
return (-EINVAL);
/* Expand the fragment values. */
} else {
}
lx_debug("\toss fragment request: "
"translated size = 0x%x (%u), translated cnt = 0x%x (%u)",
/* Set the current fragment count and size. */
lx_debug("\tioctl(%d, 0x%x - %s, ...)",
}
static int
/*ARGSUSED*/
{
int err;
/* Ioctl is only supported on dsp audio devices. */
return (err);
/*
* Report that we support mmap access
* this is where things start to get fun.
*/
return (-errno);
return (0);
}
static int
/*ARGSUSED*/
{
int err;
/* Ioctl is only supported on dsp audio devices. */
return (err);
return (-errno);
lx_debug("\toss set trigger request = 0x%x (%u)",
/* We only support two types of trigger requests. */
if ((trigger != LX_OSS_PCM_DISABLE_OUTPUT) &&
return (-EINVAL);
/*
* We only support triggers on devices open for write access,
* but we don't need to check for that here since the driver will
* verify this for us.
*/
/* Send the trigger command to the audio device. */
lx_debug("\tioctl(%d, 0x%x - %s, ...)",
}
static int
/*ARGSUSED*/
{
/* Ioctl is only supported on dsp audio devices. */
return (err);
/* Query the current fragment size. */
lx_debug("\tioctl(%d, 0x%x - %s, ...)",
return (-errno);
/* Figure out how many samples have been played. */
lx_debug("\tioctl(%d, 0x%x - %s, ...)",
return (-errno);
/*
* Figure out how many fragments of audio have gone out since
* the last call to this ioctl.
*/
/* Figure out the current fragment offset for mmap audio output. */
lx_debug("\tioctl(%d, 0x%x - %s, ...)",
/*
* We really should return an error here, but some
* application (*cough* *cough* flash) expect this
* ioctl to work even if they haven't mmaped the
* device.
*/
} else {
}
lx_debug("\toss get output ptr result = ");
lx_debug("\t\t"
"bytes = 0x%x (%u), blocks = 0x%x (%u), ptr = 0x%x (%u)",
return (-errno);
return (0);
}
static int
/*ARGSUSED*/
{
/* Ioctl is only supported on dsp audio devices. */
return (err);
/* We need to know the access mode for the file. */
return (-EINVAL);
/*
* A sync is basically a noop for record only device.
* We check for this here because on Linux a sync on a record
* only device returns success immediately. But the Solaris
* equivalent to a drain operation is a AUDIO_DRAIN, and if
* it's issued to a record only device it will fail and return
* EINVAL.
*/
return (0);
/* Drain any pending output. */
lx_debug("\tioctl(%d, 0x%x - %s, ...)",
}
/*
*
* There are some interesting things to take note of for supporting
*
* 1) We report support for the following mixer resources:
* VOLUME, PCM, MIC
*
* 2) We assume the following number of channels for each mixer resource:
* VOLUME: 2 channels
* PCM: 2 channels
* MIC: 1 channel
*
* 3) OSS sets the gain on each channel independently but on Solaris
* there is only one gain value and a balance value. So we need
* to do some translation back and forth.
*
* 4) OSS assumes direct access to hardware but Solaris provides
* get a virtualized audio channel stream, all of which are merged
* together by a software mixer before reaching the hardware). Hence
* mapping OSS mixer resources to Solaris mixer resources takes some
* work. VOLUME and Mic resources are mapped to the actual underlying
* audio hardware resources. PCM resource are mapped to the virtual
* audio channel output level. This mapping becomes more complicated
* if there are no open audio output channels. In this case the
* lx_audio device caches the PCM channels setting for us and applies
* them to any new audio output channels that get opened. (This
* is the reason that we don't use AUDIO_SETINFO ioctls directly
* but instead the lx_audio driver custom LXA_IOC_MIXER_SET_*
* and LXA_IOC_MIXER_GET_* ioctls.) For more information see
* the comments in lx_audio.c.
*/
static int
i_is_mixer_dev(int fd)
{
int minor;
/*
* This is a cloning device so we have to ask the driver
* what kind of minor node this is.
*/
lx_debug("\tioctl(%d, 0x%x - %s, ...)",
return (-EINVAL);
if (minor != LXA_MINORNUM_MIXER)
return (-EINVAL);
return (0);
}
static int
{
/* Deal with the other easy case, both channels have the same level. */
*val = LX_OSS_MIXER_ENC2(
return (0);
}
} else {
}
return (0);
}
static int
{
if (!LX_OSS_MIXER_2CH_OK(val))
return (-EINVAL);
/*
* Deal with the easy case.
* Both channels have the same non-zero level.
*/
return (0);
}
/* If both levels are zero, preserve the current balance setting. */
ml->lxa_ml_gain = 0;
return (0);
}
/*
* First set the gain to match the highest channel value volume.
* Then use the balance to simulate lower volume on the second
* channel.
*/
ml->lxa_ml_balance = 0;
} else {
}
return (0);
}
static int
/*ARGSUSED*/
{
char *cmd_txt;
(cmd == LX_OSS_SOUND_MIXER_READ_PCM));
/* Ioctl is only supported on mixer audio devices. */
return (err);
if (cmd == LX_OSS_SOUND_MIXER_READ_VOLUME) {
cmd_txt = "LXA_IOC_MIXER_GET_VOL";
}
if (cmd == LX_OSS_SOUND_MIXER_READ_PCM) {
cmd_txt = "LXA_IOC_MIXER_GET_PCM";
}
/* Attempt to set the device output gain. */
return (-errno);
lx_debug("\tlx_audio mixer results, "
"gain = 0x%x (%u), balance = 0x%x (%u)",
/* Translate the mixer levels struct to an OSS mixer value. */
return (err);
lx_debug("\toss get mixer %s result = 0x%x (%u)",
return (-errno);
return (0);
}
static int
/*ARGSUSED*/
{
char *cmd_txt;
(cmd == LX_OSS_SOUND_MIXER_WRITE_PCM));
/* Ioctl is only supported on mixer audio devices. */
return (err);
return (-errno);
if (cmd == LX_OSS_SOUND_MIXER_WRITE_VOLUME) {
cmd_txt = "LXA_IOC_MIXER_SET_VOL";
/* Attempt to get the device output gain. */
LXA_IOC_MIXER_GET_VOL, "LXA_IOC_MIXER_GET_VOL");
return (-errno);
}
if (cmd == LX_OSS_SOUND_MIXER_WRITE_PCM) {
cmd_txt = "LXA_IOC_MIXER_SET_PCM";
/* Attempt to get the device output gain. */
LXA_IOC_MIXER_GET_PCM, "LXA_IOC_MIXER_GET_PCM");
return (-errno);
}
lx_debug("\toss set mixer %s request = 0x%x (%u)",
/* Translate an OSS mixer value to mixer levels. */
return (err);
lx_debug("\tlx_audio mixer request, "
"gain = 0x%x (%u), balance = 0x%x (%u)",
/* Attempt to set the device output gain. */
return (-errno);
return (0);
}
static int
/*ARGSUSED*/
{
int err;
(cmd == LX_OSS_SOUND_MIXER_READ_IGAIN));
/* Ioctl is only supported on mixer audio devices. */
return (err);
/* Attempt to get the device input gain. */
lx_debug("\tioctl(%d, 0x%x - %s, ...)",
return (-errno);
/* Report the mixer as having two channels. */
if (cmd == LX_OSS_SOUND_MIXER_READ_MIC)
if (cmd == LX_OSS_SOUND_MIXER_READ_IGAIN)
return (-errno);
return (0);
}
static int
/*ARGSUSED*/
{
int err;
/* Ioctl is only supported on mixer audio devices. */
return (err);
return (-errno);
if (cmd == LX_OSS_SOUND_MIXER_WRITE_MIC)
if (cmd == LX_OSS_SOUND_MIXER_WRITE_IGAIN)
/* The mic only supports one channel. */
/* Attempt to set the device input gain. */
lx_debug("\tioctl(%d, 0x%x - %s, ...)",
return (-errno);
return (0);
}
static int
/*ARGSUSED*/
{
int err;
if (cmd == LX_OSS_SOUND_MIXER_READ_DEVMASK) {
/* Bitmap of all the mixer channels we supposedly support. */
(1 << LX_OSS_SM_MIC) |
(1 << LX_OSS_SM_VOLUME));
}
if (cmd == LX_OSS_SOUND_MIXER_READ_STEREODEVS) {
/* Bitmap of the stereo mixer channels we supposedly support. */
(1 << LX_OSS_SM_VOLUME));
}
if ((cmd == LX_OSS_SOUND_MIXER_READ_RECMASK) ||
(cmd == LX_OSS_SOUND_MIXER_READ_RECSRC)) {
/* Bitmap of the mixer input channels we supposedly support. */
}
/* Ioctl is only supported on mixer audio devices. */
return (err);
return (-errno);
return (0);
}
/*
* Audio ioctl conversion support structures.
*/
static oss_fmt_translator_t oft_table[] = {
{ 0, 0, 0 }
};
/*
* Ioctl translator definitions.
*/
/*
* Defines to help with creating ioctl translators.
*
* IOC_CMD_TRANSLATOR_NONE - Ioctl has the same semantics and argument
* values on Solaris and Linux but may have different command values.
* (Macro assumes the symbolic Linux name assigned to the ioctl command
* value is the same as the Solaris symbol but pre-pended with an "LX_")
*
* IOC_CMD_TRANSLATOR_PASS - Ioctl is a Linux specific ioctl and should
* be passed through unmodified.
*
* IOC_CMD_TRANSLATOR_FILTER - Ioctl has the same command name on
* Solaris and Linux and needs a translation function that is common to
* more than one ioctl. (Macro assumes the symbolic Linux name assigned
* to the ioctl command value is the same as the Solaris symbol but
* pre-pended with an "LX_")
*
* IOC_CMD_TRANSLATOR_CUSTOM - Ioctl needs special handling via a
* translation function.
*/
#define IOC_CMD_TRANSLATOR_NONE(ioc_cmd_sym) \
#define IOC_CMD_TRANSLATOR_PASS(ioc_cmd_sym) \
{ (int)ioc_cmd_sym, #ioc_cmd_sym, \
{ (int)ioc_cmd_sym, #ioc_cmd_sym, \
#define IOC_CMD_TRANSLATOR_END \
/* All files will need to support these ioctls. */
#define IOC_CMD_TRANSLATORS_ALL \
/* Any files supporting streams semantics will need these ioctls. */
#define IOC_CMD_TRANSLATORS_STREAMS \
\
\
/*
* Translators for non-device files.
*/
static ioc_cmd_translator_t ioc_translators_file[] = {
};
static ioc_cmd_translator_t ioc_translators_fifo[] = {
};
static ioc_cmd_translator_t ioc_translators_sock[] = {
};
/*
* Translators for devices.
*/
static ioc_cmd_translator_t ioc_cmd_translators_ptm[] = {
};
static ioc_dev_translator_t ioc_translator_ptm = {
LX_PTM_DRV, /* idt_driver */
0, /* idt_major */
};
static ioc_cmd_translator_t ioc_cmd_translators_pts[] = {
};
static ioc_dev_translator_t ioc_translator_pts = {
"pts", /* idt_driver */
0, /* idt_major */
};
static ioc_dev_translator_t ioc_translator_sy = {
"sy", /* idt_driver */
0, /* idt_major */
/*
* a layered driver that passes on requests to the ctty for the
* current process. Since ctty's are currently always implemented
* via the pts driver, we should make sure to support all the
* same ioctls on the sy driver as we do on the pts driver.
*/
};
static ioc_cmd_translator_t ioc_cmd_translators_zcons[] = {
};
static ioc_dev_translator_t ioc_translator_zcons = {
"zcons", /* idt_driver */
0, /* idt_major */
};
static ioc_cmd_translator_t ioc_cmd_translators_lx_audio[] = {
};
static ioc_dev_translator_t ioc_translator_lx_audio = {
"lx_audio", /* idt_driver */
0, /* idt_major */
};
/*
* An array of all the device translators.
*/
static ioc_dev_translator_t *ioc_translators_dev[] = {
};
/*
* Translators for filesystems.
*/
static ioc_cmd_translator_t ioc_cmd_translators_autofs[] = {
};
static ioc_fs_translator_t ioc_translator_autofs = {
LX_AUTOFS_NAME, /* ift_filesystem */
};
/*
* An array of all the filesystem translators.
*/
static ioc_fs_translator_t *ioc_translators_fs[] = {
};
/*
* Ioctl error translator definitions.
*/
#define IOC_ERRNO_TRANSLATOR_END \
{ 0, NULL, 0 }
static ioc_errno_translator_t ioc_translators_errno[] = {
};
int
lx_vhangup(void)
{
if (geteuid() != 0)
return (-EPERM);
vhangup();
return (0);
}