2000-01-06 12:33:15 +00:00
/*
2005-09-14 20:46:50 +00:00
* Asterisk - - An open source telephony toolkit .
2000-01-06 12:33:15 +00:00
*
2005-01-21 07:06:25 +00:00
* Copyright ( C ) 1999 - 2005 , Digium , Inc .
2000-01-06 12:33:15 +00:00
*
2004-10-02 00:58:31 +00:00
* Mark Spencer < markster @ digium . com >
2000-01-06 12:33:15 +00:00
*
2005-08-03 04:11:52 +00:00
* FreeBSD changes and multiple device support by Luigi Rizzo , 2005.05 .25
* note - this code best seen with ts = 8 ( 8 - spaces tabs ) in the editor
2005-09-14 20:46:50 +00:00
*
* See http : //www.asterisk.org for more information about
* the Asterisk project . Please do not directly contact
* any of the maintainers of this project for assistance ;
* the project provides a web site , mailing lists and IRC
* channels for your use .
*
* This program is free software , distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree .
*/
2005-10-24 20:12:06 +00:00
/*! \file
2005-09-14 20:46:50 +00:00
*
2005-10-24 20:12:06 +00:00
* \ brief Channel driver for OSS sound cards
2005-09-14 20:46:50 +00:00
*
2005-12-30 21:18:06 +00:00
* \ author Mark Spencer < markster @ digium . com >
* \ author Luigi Rizzo
*
2005-11-06 15:09:47 +00:00
* \ par See also
* \ arg \ ref Config_oss
*
* \ ingroup channel_drivers
2000-01-06 12:33:15 +00:00
*/
2006-04-24 17:11:45 +00:00
/*** MODULEINFO
2006-04-30 17:49:50 +00:00
< depend > ossaudio < / depend >
2006-04-24 17:11:45 +00:00
* * */
2006-06-07 18:54:56 +00:00
# include "asterisk.h"
ASTERISK_FILE_VERSION ( __FILE__ , " $Revision$ " )
2005-08-03 04:11:52 +00:00
# include <stdio.h>
2006-06-07 18:54:56 +00:00
# include <ctype.h>
# include <math.h>
2005-08-03 04:11:52 +00:00
# include <string.h>
2000-01-06 12:33:15 +00:00
# include <unistd.h>
# include <sys/ioctl.h>
2005-08-03 04:11:52 +00:00
# include <fcntl.h>
2000-01-06 12:33:15 +00:00
# include <sys/time.h>
# include <stdlib.h>
2005-08-03 04:11:52 +00:00
# include <errno.h>
2003-04-23 20:23:30 +00:00
# ifdef __linux
2000-01-06 12:33:15 +00:00
# include <linux/soundcard.h>
2003-10-21 02:57:29 +00:00
# elif defined(__FreeBSD__)
2004-05-27 22:12:55 +00:00
# include <sys/soundcard.h>
2003-04-23 20:23:30 +00:00
# else
# include <soundcard.h>
# endif
2005-06-06 21:09:59 +00:00
# include "asterisk/lock.h"
# include "asterisk/frame.h"
# include "asterisk/logger.h"
2006-06-07 18:54:56 +00:00
# include "asterisk/callerid.h"
2005-06-06 21:09:59 +00:00
# include "asterisk/channel.h"
# include "asterisk/module.h"
# include "asterisk/options.h"
# include "asterisk/pbx.h"
# include "asterisk/config.h"
# include "asterisk/cli.h"
# include "asterisk/utils.h"
# include "asterisk/causes.h"
# include "asterisk/endian.h"
2006-02-01 23:05:28 +00:00
# include "asterisk/stringfields.h"
2006-05-31 17:29:12 +00:00
# include "asterisk/abstract_jb.h"
2006-07-19 20:44:39 +00:00
# include "asterisk/musiconhold.h"
2005-06-06 21:09:59 +00:00
2005-08-03 04:11:52 +00:00
/* ringtones we use */
2001-05-03 04:32:56 +00:00
# include "busy.h"
# include "ringtone.h"
# include "ring10.h"
# include "answer.h"
2000-01-06 12:33:15 +00:00
2006-05-31 17:29:12 +00:00
/*! Global jitterbuffer configuration - by default, jb is disabled */
2006-05-31 16:56:50 +00:00
static struct ast_jb_conf default_jbconf =
{
. flags = 0 ,
. max_size = - 1 ,
. resync_threshold = - 1 ,
2006-09-08 03:51:26 +00:00
. impl = " " ,
2006-05-31 16:56:50 +00:00
} ;
static struct ast_jb_conf global_jbconf ;
2005-08-03 04:11:52 +00:00
/*
* Basic mode of operation :
*
* we have one keyboard ( which receives commands from the keyboard )
* and multiple headset ' s connected to audio cards .
* Cards / Headsets are named as the sections of oss . conf .
* The section called [ general ] contains the default parameters .
*
* At any time , the keyboard is attached to one card , and you
* can switch among them using the command ' console foo '
* where ' foo ' is the name of the card you want .
*
* oss . conf parameters are
2006-02-10 23:19:05 +00:00
START_CONFIG
2005-08-03 04:11:52 +00:00
[ general ]
2006-03-28 13:23:05 +00:00
; General config options , with default values shown .
; You should use one section per device , with [ general ] being used
; for the first device and also as a template for other devices .
;
; All but ' debug ' can go also in the device - specific sections .
;
; debug = 0x0 ; misc debug flags , default is 0
; Set the device to use for I / O
; device = / dev / dsp
; Optional mixer command to run upon startup ( e . g . to set
; volume levels , mutes , etc .
; mixer =
; Software mic volume booster ( or attenuator ) , useful for sound
; cards or microphones with poor sensitivity . The volume level
; is in dB , ranging from - 20.0 to + 20.0
; boost = n ; mic volume boost in dB
; Set the callerid for outgoing calls
; callerid = John Doe < 555 - 1234 >
; autoanswer = no ; no autoanswer on call
; autohangup = yes ; hangup when other party closes
; extension = s ; default extension to call
; context = default ; default context for outgoing calls
; language = " " ; default language
2006-07-19 20:44:39 +00:00
; Default Music on Hold class to use when this channel is placed on hold in
; the case that the music class is not set on the channel with
; Set ( CHANNEL ( musicclass ) = whatever ) in the dialplan and the peer channel
; putting this one on hold did not suggest a class to use .
;
; mohinterpret = default
2006-03-28 13:23:05 +00:00
; If you set overridecontext to ' yes ' , then the whole dial string
; will be interpreted as an extension , which is extremely useful
; to dial SIP , IAX and other extensions which use the ' @ ' character .
; The default is ' no ' just for backward compatibility , but the
; suggestion is to change it .
; overridecontext = no ; if ' no ' , the last @ will start the context
; if ' yes ' the whole string is an extension .
; low level device parameters in case you have problems with the
; device driver on your operating system . You should not touch these
; unless you know what you are doing .
; queuesize = 10 ; frames in device driver
; frags = 8 ; argument to SETFRAGMENT
2005-08-03 04:11:52 +00:00
2006-05-31 16:56:50 +00:00
; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - JITTER BUFFER CONFIGURATION - - - - - - - - - - - - - - - - - - - - - - - - - -
; jbenable = yes ; Enables the use of a jitterbuffer on the receiving side of an
; OSS channel . Defaults to " no " . An enabled jitterbuffer will
; be used only if the sending side can create and the receiving
2006-06-01 16:47:28 +00:00
; side can not accept jitter . The OSS channel can ' t accept jitter ,
; thus an enabled jitterbuffer on the receive OSS side will always
; be used if the sending side can create jitter .
2006-05-31 16:56:50 +00:00
; jbmaxsize = 200 ; Max length of the jitterbuffer in milliseconds .
; jbresyncthreshold = 1000 ; Jump in the frame timestamps over which the jitterbuffer is
; resynchronized . Useful to improve the quality of the voice , with
; big jumps in / broken timestamps , usualy sent from exotic devices
; and programs . Defaults to 1000.
2006-06-01 16:47:28 +00:00
; jbimpl = fixed ; Jitterbuffer implementation , used on the receiving side of an OSS
; channel . Two implementations are currenlty available - " fixed "
2006-05-31 16:56:50 +00:00
; ( with size always equals to jbmax - size ) and " adaptive " ( with
; variable size , actually the new jb of IAX2 ) . Defaults to fixed .
; jblog = no ; Enables jitterbuffer frame logging . Defaults to " no " .
; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2005-08-03 04:11:52 +00:00
[ card1 ]
2006-03-28 13:23:05 +00:00
; device = / dev / dsp1 ; alternate device
2006-02-10 23:19:05 +00:00
END_CONFIG
2005-08-03 04:11:52 +00:00
. . and so on for the other cards .
*/
/*
* Helper macros to parse config arguments . They will go in a common
* header file if their usage is globally accepted . In the meantime ,
* we define them here . Typical usage is as below .
* Remember to open a block right before M_START ( as it declares
* some variables ) and use the M_ * macros WITHOUT A SEMICOLON :
*
* {
* M_START ( v - > name , v - > value )
*
* M_BOOL ( " dothis " , x - > flag1 )
* M_STR ( " name " , x - > somestring )
* M_F ( " bar " , some_c_code )
* M_END ( some_final_statement )
* . . . other code in the block
* }
*
* XXX NOTE these macros should NOT be replicated in other parts of asterisk .
* Likely we will come up with a better way of doing config file parsing .
*/
# define M_START(var, val) \
char * __s = var ; char * __val = val ;
# define M_END(x) x;
# define M_F(tag, f) if (!strcasecmp((__s), tag)) { f; } else
# define M_BOOL(tag, dst) M_F(tag, (dst) = ast_true(__val) )
# define M_UINT(tag, dst) M_F(tag, (dst) = strtoul(__val, NULL, 0) )
# define M_STR(tag, dst) M_F(tag, ast_copy_string(dst, __val, sizeof(dst)))
/*
* The following parameters are used in the driver :
*
* FRAME_SIZE the size of an audio frame , in samples .
* 160 is used almost universally , so you should not change it .
*
* FRAGS the argument for the SETFRAGMENT ioctl .
* Overridden by the ' frags ' parameter in oss . conf
*
* Bits 0 - 7 are the base - 2 log of the device ' s block size ,
* bits 16 - 31 are the number of blocks in the driver ' s queue .
* There are a lot of differences in the way this parameter
* is supported by different drivers , so you may need to
* experiment a bit with the value .
* A good default for linux is 30 blocks of 64 bytes , which
* results in 6 frames of 320 bytes ( 160 samples ) .
* FreeBSD works decently with blocks of 256 or 512 bytes ,
* leaving the number unspecified .
* Note that this only refers to the device buffer size ,
* this module will then try to keep the lenght of audio
* buffered within small constraints .
*
* QUEUE_SIZE The max number of blocks actually allowed in the device
* driver ' s buffer , irrespective of the available number .
* Overridden by the ' queuesize ' parameter in oss . conf
*
* Should be > = 2 , and at most as large as the hw queue above
* ( otherwise it will never be full ) .
*/
# define FRAME_SIZE 160
# define QUEUE_SIZE 10
# if defined(__FreeBSD__)
# define FRAGS 0x8
# else
# define FRAGS ( ( (6 * 5) << 16 ) | 0x6 )
# endif
/*
* XXX text message sizes are probably 256 chars , but i am
* not sure if there is a suitable definition anywhere .
*/
# define TEXT_SIZE 256
#if 0
2006-09-08 03:51:26 +00:00
# define TRYOPEN 1 /* try to open on startup */
2005-08-03 04:11:52 +00:00
# endif
2006-09-08 03:51:26 +00:00
# define O_CLOSE 0x444 /* special 'close' mode for device */
2000-01-06 12:33:15 +00:00
/* Which device to use */
2004-08-31 13:32:11 +00:00
# if defined( __OpenBSD__ ) || defined( __NetBSD__ )
2003-10-22 03:10:34 +00:00
# define DEV_DSP " / dev / audio"
# else
2000-01-06 12:33:15 +00:00
# define DEV_DSP " / dev / dsp"
2003-10-22 03:10:34 +00:00
# endif
2000-01-06 12:33:15 +00:00
2005-08-03 04:11:52 +00:00
# ifndef MIN
# define MIN(a,b) ((a) < (b) ? (a) : (b))
# endif
# ifndef MAX
# define MAX(a,b) ((a) > (b) ? (a) : (b))
# endif
2000-01-06 12:33:15 +00:00
static int usecnt ;
2004-06-09 01:45:08 +00:00
AST_MUTEX_DEFINE_STATIC ( usecnt_lock ) ;
2000-01-06 12:33:15 +00:00
2005-08-03 04:11:52 +00:00
static char * config = " oss.conf " ; /* default config file */
2000-01-06 12:33:15 +00:00
2005-08-03 04:11:52 +00:00
static int oss_debug ;
2001-05-03 04:32:56 +00:00
2005-08-03 04:11:52 +00:00
/*
* Each sound is made of ' datalen ' samples of sound , repeated as needed to
* generate ' samplen ' samples of data , then followed by ' silencelen ' samples
* of silence . The loop is repeated if ' repeat ' is set .
*/
2001-05-03 04:32:56 +00:00
struct sound {
int ind ;
2005-08-03 04:11:52 +00:00
char * desc ;
2001-05-03 04:32:56 +00:00
short * data ;
int datalen ;
int samplen ;
int silencelen ;
int repeat ;
} ;
static struct sound sounds [ ] = {
2005-08-03 04:11:52 +00:00
{ AST_CONTROL_RINGING , " RINGING " , ringtone , sizeof ( ringtone ) / 2 , 16000 , 32000 , 1 } ,
{ AST_CONTROL_BUSY , " BUSY " , busy , sizeof ( busy ) / 2 , 4000 , 4000 , 1 } ,
{ AST_CONTROL_CONGESTION , " CONGESTION " , busy , sizeof ( busy ) / 2 , 2000 , 2000 , 1 } ,
{ AST_CONTROL_RING , " RING10 " , ring10 , sizeof ( ring10 ) / 2 , 16000 , 32000 , 1 } ,
{ AST_CONTROL_ANSWER , " ANSWER " , answer , sizeof ( answer ) / 2 , 2200 , 0 , 0 } ,
{ - 1 , NULL , 0 , 0 , 0 , 0 } , /* end marker */
2001-05-03 04:32:56 +00:00
} ;
2000-01-06 12:33:15 +00:00
2005-08-03 04:11:52 +00:00
/*
* descriptor for one of our channels .
* There is one used for ' default ' values ( from the [ general ] entry in
* the configuration file ) , and then one instance for each device
* ( the default is cloned from [ general ] , others are only created
* if the relevant section exists ) .
*/
struct chan_oss_pvt {
struct chan_oss_pvt * next ;
char * name ;
/*
* cursound indicates which in struct sound we play . - 1 means nothing ,
* any other value is a valid sound , in which case sampsent indicates
* the next sample to send in [ 0. . samplen + silencelen ]
* nosound is set to disable the audio data from the channel
* ( so we can play the tones etc . ) .
*/
2006-09-08 03:51:26 +00:00
int sndcmd [ 2 ] ; /* Sound command pipe */
int cursound ; /* index of sound to send */
int sampsent ; /* # of sound samples sent */
int nosound ; /* set to block audio from the PBX */
2005-08-03 04:11:52 +00:00
2006-09-08 03:51:26 +00:00
int total_blocks ; /* total blocks in the output device */
2005-08-03 04:11:52 +00:00
int sounddev ;
enum { M_UNSET , M_FULL , M_READ , M_WRITE } duplex ;
int autoanswer ;
int autohangup ;
int hookstate ;
2006-09-08 03:51:26 +00:00
char * mixer_cmd ; /* initial command to issue to the mixer */
unsigned int queuesize ; /* max fragments in queue */
unsigned int frags ; /* parameter for SETFRAGMENT */
2005-08-03 04:11:52 +00:00
2006-09-08 03:51:26 +00:00
int warned ; /* various flags used for warnings */
2005-08-03 04:11:52 +00:00
# define WARN_used_blocks 1
# define WARN_speed 2
# define WARN_frag 4
2006-09-08 03:51:26 +00:00
int w_errors ; /* overfull in the write path */
2005-08-03 04:11:52 +00:00
struct timeval lastopen ;
int overridecontext ;
int mute ;
2006-02-10 23:19:05 +00:00
/* boost support. BOOST_SCALE * 10 ^(BOOST_MAX/20) must
* be representable in 16 bits to avoid overflows .
*/
# define BOOST_SCALE (1<<9)
2006-09-08 03:51:26 +00:00
# define BOOST_MAX 40 /* slightly less than 7 bits */
int boost ; /* input boost, scaled by BOOST_SCALE */
char device [ 64 ] ; /* device to open */
2005-08-03 04:11:52 +00:00
pthread_t sthread ;
2000-01-06 12:33:15 +00:00
struct ast_channel * owner ;
2005-08-03 04:11:52 +00:00
char ext [ AST_MAX_EXTENSION ] ;
char ctx [ AST_MAX_CONTEXT ] ;
char language [ MAX_LANGUAGE ] ;
2006-09-08 03:51:26 +00:00
char cid_name [ 256 ] ; /*XXX */
char cid_num [ 256 ] ; /*XXX */
2006-07-19 20:44:39 +00:00
char mohinterpret [ MAX_MUSICCLASS ] ;
2005-08-03 04:11:52 +00:00
/* buffers used in oss_write */
2006-09-08 03:51:26 +00:00
char oss_write_buf [ FRAME_SIZE * 2 ] ;
2005-08-03 04:11:52 +00:00
int oss_write_dst ;
/* buffers used in oss_read - AST_FRIENDLY_OFFSET space for headers
* plus enough room for a full frame
*/
char oss_read_buf [ FRAME_SIZE * 2 + AST_FRIENDLY_OFFSET ] ;
2006-09-08 03:51:26 +00:00
int readpos ; /* read position above */
2005-08-03 04:11:52 +00:00
struct ast_frame read_f ; /* returned by oss_read */
} ;
static struct chan_oss_pvt oss_default = {
. cursound = - 1 ,
. sounddev = - 1 ,
2006-09-08 03:51:26 +00:00
. duplex = M_UNSET , /* XXX check this */
2005-08-03 04:11:52 +00:00
. autoanswer = 1 ,
. autohangup = 1 ,
. queuesize = QUEUE_SIZE ,
. frags = FRAGS ,
. ext = " s " ,
. ctx = " default " ,
. readpos = AST_FRIENDLY_OFFSET , /* start here on reads */
. lastopen = { 0 , 0 } ,
2006-02-10 23:19:05 +00:00
. boost = BOOST_SCALE ,
2005-08-03 04:11:52 +00:00
} ;
2000-01-06 12:33:15 +00:00
2005-08-03 04:11:52 +00:00
static char * oss_active ; /* the active device */
static int setformat ( struct chan_oss_pvt * o , int mode ) ;
static struct ast_channel * oss_request ( const char * type , int format , void * data
, int * cause ) ;
2006-08-31 01:59:02 +00:00
static int oss_digit_begin ( struct ast_channel * c , char digit ) ;
static int oss_digit_end ( struct ast_channel * c , char digit ) ;
2005-04-21 06:30:23 +00:00
static int oss_text ( struct ast_channel * c , const char * text ) ;
2005-03-04 06:47:24 +00:00
static int oss_hangup ( struct ast_channel * c ) ;
static int oss_answer ( struct ast_channel * c ) ;
static struct ast_frame * oss_read ( struct ast_channel * chan ) ;
static int oss_call ( struct ast_channel * c , char * dest , int timeout ) ;
static int oss_write ( struct ast_channel * chan , struct ast_frame * f ) ;
2006-05-10 12:24:11 +00:00
static int oss_indicate ( struct ast_channel * chan , int cond , const void * data , size_t datalen ) ;
2005-03-04 06:47:24 +00:00
static int oss_fixup ( struct ast_channel * oldchan , struct ast_channel * newchan ) ;
2006-05-09 08:44:50 +00:00
static char tdesc [ ] = " OSS Console Channel Driver " ;
2005-03-04 06:47:24 +00:00
static const struct ast_channel_tech oss_tech = {
2006-09-08 03:51:26 +00:00
. type = " Console " ,
. description = tdesc ,
. capabilities = AST_FORMAT_SLINEAR ,
2006-02-01 23:05:28 +00:00
. requester = oss_request ,
2006-08-31 01:59:02 +00:00
. send_digit_begin = oss_digit_begin ,
. send_digit_end = oss_digit_end ,
2006-02-01 23:05:28 +00:00
. send_text = oss_text ,
. hangup = oss_hangup ,
. answer = oss_answer ,
. read = oss_read ,
. call = oss_call ,
. write = oss_write ,
. indicate = oss_indicate ,
. fixup = oss_fixup ,
2005-03-04 06:47:24 +00:00
} ;
2005-08-03 04:11:52 +00:00
/*
* returns a pointer to the descriptor with the given name
*/
static struct chan_oss_pvt * find_desc ( char * dev )
2000-01-06 12:33:15 +00:00
{
2006-09-08 03:51:26 +00:00
struct chan_oss_pvt * o = NULL ;
if ( ! dev )
2006-02-10 23:19:05 +00:00
ast_log ( LOG_WARNING , " null dev \n " ) ;
2000-01-06 12:33:15 +00:00
2006-09-08 03:51:26 +00:00
for ( o = oss_default . next ; o & & o - > name & & dev & & strcmp ( o - > name , dev ) ! = 0 ; o = o - > next ) ;
if ( ! o )
2006-02-10 23:19:05 +00:00
ast_log ( LOG_WARNING , " could not find <%s> \n " , dev ? dev : " --no-device-- " ) ;
2006-09-08 03:51:26 +00:00
2005-08-03 04:11:52 +00:00
return o ;
}
2000-01-06 12:33:15 +00:00
2005-08-03 04:11:52 +00:00
/*
* split a string in extension - context , returns pointers to malloc ' ed
* strings .
2005-08-03 19:58:34 +00:00
* If we do not have ' overridecontext ' then the last @ is considered as
2005-08-03 04:11:52 +00:00
* a context separator , and the context is overridden .
* This is usually not very necessary as you can play with the dialplan ,
* and it is nice not to need it because you have ' @ ' in SIP addresses .
* Return value is the buffer address .
*/
static char * ast_ext_ctx ( const char * src , char * * ext , char * * ctx )
{
struct chan_oss_pvt * o = find_desc ( oss_active ) ;
if ( ext = = NULL | | ctx = = NULL )
2006-09-08 03:51:26 +00:00
return NULL ; /* error */
2005-08-03 04:11:52 +00:00
* ext = * ctx = NULL ;
2006-09-08 03:51:26 +00:00
2005-08-03 04:11:52 +00:00
if ( src & & * src ! = ' \0 ' )
2006-01-11 01:20:29 +00:00
* ext = ast_strdup ( src ) ;
2006-09-08 03:51:26 +00:00
2005-08-03 04:11:52 +00:00
if ( * ext = = NULL )
return NULL ;
2006-09-08 03:51:26 +00:00
2005-08-03 04:11:52 +00:00
if ( ! o - > overridecontext ) {
/* parse from the right */
* ctx = strrchr ( * ext , ' @ ' ) ;
if ( * ctx )
* ( * ctx ) + + = ' \0 ' ;
}
2006-09-08 03:51:26 +00:00
2005-08-03 04:11:52 +00:00
return * ext ;
}
2000-01-06 12:33:15 +00:00
2005-08-03 04:11:52 +00:00
/*
* Returns the number of blocks used in the audio output channel
*/
static int used_blocks ( struct chan_oss_pvt * o )
{
struct audio_buf_info info ;
2000-01-06 12:33:15 +00:00
2005-08-03 04:11:52 +00:00
if ( ioctl ( o - > sounddev , SNDCTL_DSP_GETOSPACE , & info ) ) {
2006-09-08 03:51:26 +00:00
if ( ! ( o - > warned & WARN_used_blocks ) ) {
2005-08-03 04:11:52 +00:00
ast_log ( LOG_WARNING , " Error reading output space \n " ) ;
o - > warned | = WARN_used_blocks ;
}
return 1 ;
}
2006-09-08 03:51:26 +00:00
2005-08-03 04:11:52 +00:00
if ( o - > total_blocks = = 0 ) {
2006-09-08 03:51:26 +00:00
if ( 0 ) /* debugging */
ast_log ( LOG_WARNING , " fragtotal %d size %d avail %d \n " , info . fragstotal , info . fragsize , info . fragments ) ;
2005-08-03 04:11:52 +00:00
o - > total_blocks = info . fragments ;
}
2006-09-08 03:51:26 +00:00
2005-08-03 04:11:52 +00:00
return o - > total_blocks - info . fragments ;
}
2000-01-06 12:33:15 +00:00
2005-08-03 04:11:52 +00:00
/* Write an exactly FRAME_SIZE sized frame */
static int soundcard_writeframe ( struct chan_oss_pvt * o , short * data )
2006-09-08 03:51:26 +00:00
{
2005-08-03 04:11:52 +00:00
int res ;
2000-01-06 12:33:15 +00:00
2005-08-03 04:11:52 +00:00
if ( o - > sounddev < 0 )
setformat ( o , O_RDWR ) ;
if ( o - > sounddev < 0 )
2006-09-08 03:51:26 +00:00
return 0 ; /* not fatal */
2005-08-03 04:11:52 +00:00
/*
* Nothing complex to manage the audio device queue .
* If the buffer is full just drop the extra , otherwise write .
* XXX in some cases it might be useful to write anyways after
* a number of failures , to restart the output chain .
*/
res = used_blocks ( o ) ;
if ( res > o - > queuesize ) { /* no room to write a block */
if ( o - > w_errors + + = = 0 & & ( oss_debug & 0x4 ) )
2006-09-08 03:51:26 +00:00
ast_log ( LOG_WARNING , " write: used %d blocks (%d) \n " , res , o - > w_errors ) ;
2005-08-03 04:11:52 +00:00
return 0 ;
2000-01-06 12:33:15 +00:00
}
2005-08-03 04:11:52 +00:00
o - > w_errors = 0 ;
2006-09-08 03:51:26 +00:00
return write ( o - > sounddev , ( ( void * ) data ) , FRAME_SIZE * 2 ) ;
2000-01-06 12:33:15 +00:00
}
2001-05-03 04:32:56 +00:00
2005-08-03 04:11:52 +00:00
/*
* Handler for ' sound writable ' events from the sound thread .
* Builds a frame from the high level description of the sounds ,
* and passes it to the audio device .
* The actual sound is made of 1 or more sequences of sound samples
* ( s - > datalen , repeated to make s - > samplen samples ) followed by
* s - > silencelen samples of silence . The position in the sequence is stored
* in o - > sampsent , which goes between 0 . . s - > samplen + s - > silencelen .
* In case we fail to write a frame , don ' t update o - > sampsent .
*/
static void send_sound ( struct chan_oss_pvt * o )
2001-05-03 04:32:56 +00:00
{
short myframe [ FRAME_SIZE ] ;
2005-08-03 04:11:52 +00:00
int ofs , l , start ;
int l_sampsent = o - > sampsent ;
struct sound * s ;
2006-09-08 03:51:26 +00:00
if ( o - > cursound < 0 ) /* no sound to send */
2005-08-03 04:11:52 +00:00
return ;
2006-09-08 03:51:26 +00:00
2005-08-03 04:11:52 +00:00
s = & sounds [ o - > cursound ] ;
2006-09-08 03:51:26 +00:00
2005-08-03 04:11:52 +00:00
for ( ofs = 0 ; ofs < FRAME_SIZE ; ofs + = l ) {
l = s - > samplen - l_sampsent ; /* # of available samples */
if ( l > 0 ) {
2006-09-08 03:51:26 +00:00
start = l_sampsent % s - > datalen ; /* source offset */
2005-08-03 04:11:52 +00:00
if ( l > FRAME_SIZE - ofs ) /* don't overflow the frame */
l = FRAME_SIZE - ofs ;
if ( l > s - > datalen - start ) /* don't overflow the source */
l = s - > datalen - start ;
2006-09-08 03:51:26 +00:00
bcopy ( s - > data + start , myframe + ofs , l * 2 ) ;
2005-08-03 04:11:52 +00:00
if ( 0 )
2006-09-08 03:51:26 +00:00
ast_log ( LOG_WARNING , " send_sound sound %d/%d of %d into %d \n " , l_sampsent , l , s - > samplen , ofs ) ;
2005-08-03 04:11:52 +00:00
l_sampsent + = l ;
2006-09-08 03:51:26 +00:00
} else { /* end of samples, maybe some silence */
static const short silence [ FRAME_SIZE ] = { 0 , } ;
2005-08-03 04:11:52 +00:00
l + = s - > silencelen ;
if ( l > 0 ) {
if ( l > FRAME_SIZE - ofs )
l = FRAME_SIZE - ofs ;
2006-09-08 03:51:26 +00:00
bcopy ( silence , myframe + ofs , l * 2 ) ;
2005-08-03 04:11:52 +00:00
l_sampsent + = l ;
2006-09-08 03:51:26 +00:00
} else { /* silence is over, restart sound if loop */
2005-08-03 04:11:52 +00:00
if ( s - > repeat = = 0 ) { /* last block */
o - > cursound = - 1 ;
o - > nosound = 0 ; /* allow audio data */
if ( ofs < FRAME_SIZE ) /* pad with silence */
2006-09-08 03:51:26 +00:00
bcopy ( silence , myframe + ofs , ( FRAME_SIZE - ofs ) * 2 ) ;
2001-05-03 04:32:56 +00:00
}
2005-08-03 04:11:52 +00:00
l_sampsent = 0 ;
2001-05-03 04:32:56 +00:00
}
}
}
2005-08-03 04:11:52 +00:00
l = soundcard_writeframe ( o , myframe ) ;
if ( l > 0 )
o - > sampsent = l_sampsent ; /* update status */
2001-05-03 04:32:56 +00:00
}
2005-08-03 04:11:52 +00:00
static void * sound_thread ( void * arg )
2001-05-03 04:32:56 +00:00
{
2002-12-30 16:50:12 +00:00
char ign [ 4096 ] ;
2006-09-08 03:51:26 +00:00
struct chan_oss_pvt * o = ( struct chan_oss_pvt * ) arg ;
2005-08-03 04:11:52 +00:00
/*
* Just in case , kick the driver by trying to read from it .
* Ignore errors - this read is almost guaranteed to fail .
*/
read ( o - > sounddev , ign , sizeof ( ign ) ) ;
for ( ; ; ) {
fd_set rfds , wfds ;
int maxfd , res ;
2001-05-03 04:32:56 +00:00
FD_ZERO ( & rfds ) ;
FD_ZERO ( & wfds ) ;
2005-08-03 04:11:52 +00:00
FD_SET ( o - > sndcmd [ 0 ] , & rfds ) ;
maxfd = o - > sndcmd [ 0 ] ; /* pipe from the main process */
if ( o - > cursound > - 1 & & o - > sounddev < 0 )
2006-09-08 03:51:26 +00:00
setformat ( o , O_RDWR ) ; /* need the channel, try to reopen */
2005-08-03 04:11:52 +00:00
else if ( o - > cursound = = - 1 & & o - > owner = = NULL )
2006-09-08 03:51:26 +00:00
setformat ( o , O_CLOSE ) ; /* can close */
2005-08-03 04:11:52 +00:00
if ( o - > sounddev > - 1 ) {
2006-09-08 03:51:26 +00:00
if ( ! o - > owner ) { /* no one owns the audio, so we must drain it */
2005-08-03 04:11:52 +00:00
FD_SET ( o - > sounddev , & rfds ) ;
maxfd = MAX ( o - > sounddev , maxfd ) ;
}
if ( o - > cursound > - 1 ) {
FD_SET ( o - > sounddev , & wfds ) ;
maxfd = MAX ( o - > sounddev , maxfd ) ;
}
2001-05-03 04:32:56 +00:00
}
2005-08-03 04:11:52 +00:00
/* ast_select emulates linux behaviour in terms of timeout handling */
res = ast_select ( maxfd + 1 , & rfds , & wfds , NULL , NULL ) ;
2001-05-03 04:32:56 +00:00
if ( res < 1 ) {
ast_log ( LOG_WARNING , " select failed: %s \n " , strerror ( errno ) ) ;
2005-08-03 04:11:52 +00:00
sleep ( 1 ) ;
2001-05-03 04:32:56 +00:00
continue ;
}
2005-08-03 04:11:52 +00:00
if ( FD_ISSET ( o - > sndcmd [ 0 ] , & rfds ) ) {
/* read which sound to play from the pipe */
int i , what = - 1 ;
read ( o - > sndcmd [ 0 ] , & what , sizeof ( what ) ) ;
for ( i = 0 ; sounds [ i ] . ind ! = - 1 ; i + + ) {
if ( sounds [ i ] . ind = = what ) {
o - > cursound = i ;
o - > sampsent = 0 ;
2006-09-08 03:51:26 +00:00
o - > nosound = 1 ; /* block audio from pbx */
2005-08-03 04:11:52 +00:00
break ;
}
}
if ( sounds [ i ] . ind = = - 1 )
ast_log ( LOG_WARNING , " invalid sound index: %d \n " , what ) ;
2001-05-03 04:32:56 +00:00
}
2005-08-03 04:11:52 +00:00
if ( o - > sounddev > - 1 ) {
2006-09-08 03:51:26 +00:00
if ( FD_ISSET ( o - > sounddev , & rfds ) ) /* read and ignore errors */
2005-08-03 04:11:52 +00:00
read ( o - > sounddev , ign , sizeof ( ign ) ) ;
if ( FD_ISSET ( o - > sounddev , & wfds ) )
send_sound ( o ) ;
2002-12-30 16:50:12 +00:00
}
2001-05-03 04:32:56 +00:00
}
2006-09-08 03:51:26 +00:00
return NULL ; /* Never reached */
2001-05-03 04:32:56 +00:00
}
2005-08-03 04:11:52 +00:00
/*
* reset and close the device if opened ,
* then open and initialize it in the desired mode ,
* trigger reads and writes so we can start using it .
*/
static int setformat ( struct chan_oss_pvt * o , int mode )
2000-01-06 12:33:15 +00:00
{
2005-08-03 04:11:52 +00:00
int fmt , desired , res , fd ;
if ( o - > sounddev > = 0 ) {
ioctl ( o - > sounddev , SNDCTL_DSP_RESET , 0 ) ;
close ( o - > sounddev ) ;
o - > duplex = M_UNSET ;
o - > sounddev = - 1 ;
}
2006-09-08 03:51:26 +00:00
if ( mode = = O_CLOSE ) /* we are done */
2000-01-06 12:33:15 +00:00
return 0 ;
2005-08-03 04:11:52 +00:00
if ( ast_tvdiff_ms ( ast_tvnow ( ) , o - > lastopen ) < 1000 )
2006-09-08 03:51:26 +00:00
return - 1 ; /* don't open too often */
2005-08-03 04:11:52 +00:00
o - > lastopen = ast_tvnow ( ) ;
2006-09-08 03:51:26 +00:00
fd = o - > sounddev = open ( o - > device , mode | O_NONBLOCK ) ;
2005-08-03 04:11:52 +00:00
if ( fd < 0 ) {
2006-09-08 03:51:26 +00:00
ast_log ( LOG_WARNING , " Unable to re-open DSP device %s: %s \n " , o - > device , strerror ( errno ) ) ;
2005-08-03 04:11:52 +00:00
return - 1 ;
2000-01-06 12:33:15 +00:00
}
2005-08-03 04:11:52 +00:00
if ( o - > owner )
o - > owner - > fds [ 0 ] = fd ;
2005-03-24 00:55:40 +00:00
# if __BYTE_ORDER == __LITTLE_ENDIAN
2000-01-06 12:33:15 +00:00
fmt = AFMT_S16_LE ;
2005-03-24 00:55:40 +00:00
# else
fmt = AFMT_S16_BE ;
# endif
2000-01-06 12:33:15 +00:00
res = ioctl ( fd , SNDCTL_DSP_SETFMT , & fmt ) ;
if ( res < 0 ) {
ast_log ( LOG_WARNING , " Unable to set format to 16-bit signed \n " ) ;
return - 1 ;
}
2005-08-03 04:11:52 +00:00
switch ( mode ) {
2006-09-08 03:51:26 +00:00
case O_RDWR :
res = ioctl ( fd , SNDCTL_DSP_SETDUPLEX , 0 ) ;
/* Check to see if duplex set (FreeBSD Bug) */
res = ioctl ( fd , SNDCTL_DSP_GETCAPS , & fmt ) ;
if ( res = = 0 & & ( fmt & DSP_CAP_DUPLEX ) ) {
if ( option_verbose > 1 )
ast_verbose ( VERBOSE_PREFIX_2 " Console is full duplex \n " ) ;
o - > duplex = M_FULL ;
} ;
break ;
case O_WRONLY :
o - > duplex = M_WRITE ;
break ;
case O_RDONLY :
o - > duplex = M_READ ;
break ;
2000-01-06 12:33:15 +00:00
}
2005-08-03 04:11:52 +00:00
2000-01-06 12:33:15 +00:00
fmt = 0 ;
res = ioctl ( fd , SNDCTL_DSP_STEREO , & fmt ) ;
if ( res < 0 ) {
ast_log ( LOG_WARNING , " Failed to set audio device to mono \n " ) ;
return - 1 ;
}
2006-09-08 03:51:26 +00:00
fmt = desired = DEFAULT_SAMPLE_RATE ; /* 8000 Hz desired */
2000-01-06 12:33:15 +00:00
res = ioctl ( fd , SNDCTL_DSP_SPEED , & fmt ) ;
2005-08-03 04:11:52 +00:00
2000-01-06 12:33:15 +00:00
if ( res < 0 ) {
ast_log ( LOG_WARNING , " Failed to set audio device to mono \n " ) ;
return - 1 ;
}
if ( fmt ! = desired ) {
2005-08-03 04:11:52 +00:00
if ( ! ( o - > warned & WARN_speed ) ) {
ast_log ( LOG_WARNING ,
" Requested %d Hz, got %d Hz -- sound may be choppy \n " ,
desired , fmt ) ;
o - > warned | = WARN_speed ;
2000-01-06 12:33:15 +00:00
}
}
2005-08-03 04:11:52 +00:00
/*
* on Freebsd , SETFRAGMENT does not work very well on some cards .
* Default to use 256 bytes , let the user override
*/
if ( o - > frags ) {
fmt = o - > frags ;
res = ioctl ( fd , SNDCTL_DSP_SETFRAGMENT , & fmt ) ;
if ( res < 0 ) {
if ( ! ( o - > warned & WARN_frag ) ) {
ast_log ( LOG_WARNING ,
" Unable to set fragment size -- sound may be choppy \n " ) ;
o - > warned | = WARN_frag ;
}
2000-01-06 12:33:15 +00:00
}
}
2005-08-03 04:11:52 +00:00
/* on some cards, we need SNDCTL_DSP_SETTRIGGER to start outputting */
res = PCM_ENABLE_INPUT | PCM_ENABLE_OUTPUT ;
res = ioctl ( fd , SNDCTL_DSP_SETTRIGGER , & res ) ;
/* it may fail if we are in half duplex, never mind */
return 0 ;
2000-01-06 12:33:15 +00:00
}
2005-08-03 04:11:52 +00:00
/*
* some of the standard methods supported by channels .
*/
2006-08-31 01:59:02 +00:00
static int oss_digit_begin ( struct ast_channel * c , char digit )
{
return 0 ;
}
static int oss_digit_end ( struct ast_channel * c , char digit )
2000-01-06 12:33:15 +00:00
{
2005-08-03 04:11:52 +00:00
/* no better use for received digits than print them */
2006-09-08 03:51:26 +00:00
ast_verbose ( " << Console Received digit %c >> \n " , digit ) ;
2000-01-06 12:33:15 +00:00
return 0 ;
}
2005-04-21 06:30:23 +00:00
static int oss_text ( struct ast_channel * c , const char * text )
2000-03-26 01:59:06 +00:00
{
2005-08-03 04:11:52 +00:00
/* print received messages */
2006-09-08 03:51:26 +00:00
ast_verbose ( " << Console Received text %s >> \n " , text ) ;
2000-03-26 01:59:06 +00:00
return 0 ;
}
2005-08-03 04:11:52 +00:00
/* Play ringtone 'x' on device 'o' */
static void ring ( struct chan_oss_pvt * o , int x )
{
write ( o - > sndcmd [ 1 ] , & x , sizeof ( x ) ) ;
}
/*
* handler for incoming calls . Either autoanswer , or start ringing
*/
2000-01-06 12:33:15 +00:00
static int oss_call ( struct ast_channel * c , char * dest , int timeout )
{
2005-08-03 04:11:52 +00:00
struct chan_oss_pvt * o = c - > tech_pvt ;
2002-05-30 01:34:15 +00:00
struct ast_frame f = { 0 , } ;
2005-08-03 04:11:52 +00:00
2006-09-08 03:51:26 +00:00
ast_verbose ( " << Call to device '%s' dnid '%s' rdnis '%s' on console from '%s' <%s> >> \n " , dest , c - > cid . cid_dnid , c - > cid . cid_rdnis , c - > cid . cid_name , c - > cid . cid_num ) ;
2005-08-03 04:11:52 +00:00
if ( o - > autoanswer ) {
2006-09-08 03:51:26 +00:00
ast_verbose ( " << Auto-answered >> \n " ) ;
2002-05-30 01:34:15 +00:00
f . frametype = AST_FRAME_CONTROL ;
f . subclass = AST_CONTROL_ANSWER ;
2004-04-07 15:59:14 +00:00
ast_queue_frame ( c , & f ) ;
2000-01-06 12:33:15 +00:00
} else {
2005-08-03 04:11:52 +00:00
ast_verbose ( " << Type 'answer' to answer, or use 'autoanswer' for future calls >> \n " ) ;
2002-05-30 01:34:15 +00:00
f . frametype = AST_FRAME_CONTROL ;
f . subclass = AST_CONTROL_RINGING ;
2004-04-07 15:59:14 +00:00
ast_queue_frame ( c , & f ) ;
2005-08-03 04:11:52 +00:00
ring ( o , AST_CONTROL_RING ) ;
2000-01-06 12:33:15 +00:00
}
return 0 ;
}
2005-08-03 04:11:52 +00:00
/*
* remote side answered the phone
*/
2000-01-06 12:33:15 +00:00
static int oss_answer ( struct ast_channel * c )
{
2005-08-03 04:11:52 +00:00
struct chan_oss_pvt * o = c - > tech_pvt ;
2006-09-08 03:51:26 +00:00
ast_verbose ( " << Console call has been answered >> \n " ) ;
2005-08-03 04:11:52 +00:00
#if 0
/* play an answer tone (XXX do we really need it ?) */
ring ( o , AST_CONTROL_ANSWER ) ;
# endif
2002-08-28 04:07:50 +00:00
ast_setstate ( c , AST_STATE_UP ) ;
2005-08-03 04:11:52 +00:00
o - > cursound = - 1 ;
2006-09-08 03:51:26 +00:00
o - > nosound = 0 ;
2000-01-06 12:33:15 +00:00
return 0 ;
}
static int oss_hangup ( struct ast_channel * c )
{
2005-08-03 04:11:52 +00:00
struct chan_oss_pvt * o = c - > tech_pvt ;
o - > cursound = - 1 ;
o - > nosound = 0 ;
2005-03-04 06:47:24 +00:00
c - > tech_pvt = NULL ;
2005-08-03 04:11:52 +00:00
o - > owner = NULL ;
2006-09-08 03:51:26 +00:00
ast_verbose ( " << Hangup on console >> \n " ) ;
2005-08-03 04:11:52 +00:00
ast_mutex_lock ( & usecnt_lock ) ; /* XXX not sure why */
2000-01-06 12:33:15 +00:00
usecnt - - ;
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & usecnt_lock ) ;
2005-08-03 04:11:52 +00:00
if ( o - > hookstate ) {
if ( o - > autoanswer | | o - > autohangup ) {
2002-01-13 18:51:27 +00:00
/* Assume auto-hangup too */
2005-08-03 04:11:52 +00:00
o - > hookstate = 0 ;
setformat ( o , O_CLOSE ) ;
2002-01-13 18:51:27 +00:00
} else {
/* Make congestion noise */
2005-08-03 04:11:52 +00:00
ring ( o , AST_CONTROL_CONGESTION ) ;
2002-01-13 18:51:27 +00:00
}
2001-05-03 04:32:56 +00:00
}
2000-01-06 12:33:15 +00:00
return 0 ;
}
2005-08-03 04:11:52 +00:00
/* used for data coming from the network */
static int oss_write ( struct ast_channel * c , struct ast_frame * f )
2000-01-06 12:33:15 +00:00
{
2005-08-03 04:11:52 +00:00
int src ;
struct chan_oss_pvt * o = c - > tech_pvt ;
2001-05-03 04:32:56 +00:00
/* Immediately return if no sound is enabled */
2005-08-03 04:11:52 +00:00
if ( o - > nosound )
2001-05-03 04:32:56 +00:00
return 0 ;
/* Stop any currently playing sound */
2005-08-03 04:11:52 +00:00
o - > cursound = - 1 ;
/*
* we could receive a block which is not a multiple of our
* FRAME_SIZE , so buffer it locally and write to the device
* in FRAME_SIZE chunks .
* Keep the residue stored for future use .
*/
2006-09-08 03:51:26 +00:00
src = 0 ; /* read position into f->data */
while ( src < f - > datalen ) {
2005-08-03 04:11:52 +00:00
/* Compute spare room in the buffer */
int l = sizeof ( o - > oss_write_buf ) - o - > oss_write_dst ;
if ( f - > datalen - src > = l ) { /* enough to fill a frame */
2006-09-08 03:51:26 +00:00
memcpy ( o - > oss_write_buf + o - > oss_write_dst , f - > data + src , l ) ;
soundcard_writeframe ( o , ( short * ) o - > oss_write_buf ) ;
2005-08-03 04:11:52 +00:00
src + = l ;
o - > oss_write_dst = 0 ;
2006-09-08 03:51:26 +00:00
} else { /* copy residue */
2005-08-03 04:11:52 +00:00
l = f - > datalen - src ;
2006-09-08 03:51:26 +00:00
memcpy ( o - > oss_write_buf + o - > oss_write_dst , f - > data + src , l ) ;
src + = l ; /* but really, we are done */
2005-08-03 04:11:52 +00:00
o - > oss_write_dst + = l ;
2000-01-06 12:33:15 +00:00
}
}
return 0 ;
}
2005-08-03 04:11:52 +00:00
static struct ast_frame * oss_read ( struct ast_channel * c )
2000-01-06 12:33:15 +00:00
{
int res ;
2005-08-03 04:11:52 +00:00
struct chan_oss_pvt * o = c - > tech_pvt ;
struct ast_frame * f = & o - > read_f ;
2006-02-10 23:19:05 +00:00
/* XXX can be simplified returning &ast_null_frame */
2005-08-03 04:11:52 +00:00
/* prepare a NULL frame in case we don't have enough data to return */
bzero ( f , sizeof ( struct ast_frame ) ) ;
f - > frametype = AST_FRAME_NULL ;
2006-02-01 23:05:28 +00:00
f - > src = oss_tech . type ;
2005-08-03 04:11:52 +00:00
2006-09-08 03:51:26 +00:00
res = read ( o - > sounddev , o - > oss_read_buf + o - > readpos , sizeof ( o - > oss_read_buf ) - o - > readpos ) ;
if ( res < 0 ) /* audio data not ready, return a NULL frame */
2005-08-03 04:11:52 +00:00
return f ;
o - > readpos + = res ;
if ( o - > readpos < sizeof ( o - > oss_read_buf ) ) /* not enough samples */
return f ;
if ( o - > mute )
return f ;
o - > readpos = AST_FRIENDLY_OFFSET ; /* reset read pointer for next frame */
if ( c - > _state ! = AST_STATE_UP ) /* drop data if frame is not up */
return f ;
/* ok we can build and deliver the frame to the caller */
f - > frametype = AST_FRAME_VOICE ;
f - > subclass = AST_FORMAT_SLINEAR ;
f - > samples = FRAME_SIZE ;
f - > datalen = FRAME_SIZE * 2 ;
f - > data = o - > oss_read_buf + AST_FRIENDLY_OFFSET ;
2006-09-08 03:51:26 +00:00
if ( o - > boost ! = BOOST_SCALE ) { /* scale and clip values */
2006-02-10 23:19:05 +00:00
int i , x ;
2006-09-08 03:51:26 +00:00
int16_t * p = ( int16_t * ) f - > data ;
2006-02-10 23:19:05 +00:00
for ( i = 0 ; i < f - > samples ; i + + ) {
x = ( p [ i ] * o - > boost ) / BOOST_SCALE ;
if ( x > 32767 )
x = 32767 ;
else if ( x < - 32768 )
x = - 32768 ;
p [ i ] = x ;
}
}
2006-09-08 03:51:26 +00:00
2005-08-03 04:11:52 +00:00
f - > offset = AST_FRIENDLY_OFFSET ;
return f ;
2000-01-06 12:33:15 +00:00
}
2004-04-07 16:17:31 +00:00
static int oss_fixup ( struct ast_channel * oldchan , struct ast_channel * newchan )
2001-05-03 04:32:56 +00:00
{
2005-08-03 04:11:52 +00:00
struct chan_oss_pvt * o = newchan - > tech_pvt ;
o - > owner = newchan ;
2001-05-03 04:32:56 +00:00
return 0 ;
}
2006-05-10 12:24:11 +00:00
static int oss_indicate ( struct ast_channel * c , int cond , const void * data , size_t datalen )
2001-05-03 04:32:56 +00:00
{
2005-08-03 04:11:52 +00:00
struct chan_oss_pvt * o = c - > tech_pvt ;
2006-07-24 11:45:06 +00:00
int res = - 1 ;
2005-08-03 04:11:52 +00:00
2006-09-08 03:51:26 +00:00
switch ( cond ) {
case AST_CONTROL_BUSY :
case AST_CONTROL_CONGESTION :
case AST_CONTROL_RINGING :
res = cond ;
break ;
case - 1 :
o - > cursound = - 1 ;
o - > nosound = 0 ; /* when cursound is -1 nosound must be 0 */
return 0 ;
case AST_CONTROL_VIDUPDATE :
res = - 1 ;
break ;
case AST_CONTROL_HOLD :
ast_verbose ( " << Console Has Been Placed on Hold >> \n " ) ;
ast_moh_start ( c , data , o - > mohinterpret ) ;
break ;
case AST_CONTROL_UNHOLD :
ast_verbose ( " << Console Has Been Retrieved from Hold >> \n " ) ;
ast_moh_stop ( c ) ;
break ;
default :
ast_log ( LOG_WARNING , " Don't know how to display condition %d on %s \n " , cond , c - > name ) ;
return - 1 ;
2001-05-03 04:32:56 +00:00
}
2006-07-19 20:44:39 +00:00
2005-08-03 04:11:52 +00:00
if ( res > - 1 )
ring ( o , res ) ;
2006-09-08 03:51:26 +00:00
return 0 ;
2001-05-03 04:32:56 +00:00
}
2005-08-03 04:11:52 +00:00
/*
* allocate a new channel .
*/
2006-09-08 03:51:26 +00:00
static struct ast_channel * oss_new ( struct chan_oss_pvt * o , char * ext , char * ctx , int state )
2000-01-06 12:33:15 +00:00
{
2005-08-03 04:11:52 +00:00
struct ast_channel * c ;
c = ast_channel_alloc ( 1 ) ;
if ( c = = NULL )
return NULL ;
c - > tech = & oss_tech ;
2006-02-01 23:05:28 +00:00
ast_string_field_build ( c , name , " OSS/%s " , o - > device + 5 ) ;
2006-02-10 23:19:05 +00:00
if ( o - > sounddev < 0 )
setformat ( o , O_RDWR ) ;
2006-09-08 03:51:26 +00:00
c - > fds [ 0 ] = o - > sounddev ; /* -1 if device closed, override later */
2005-08-03 04:11:52 +00:00
c - > nativeformats = AST_FORMAT_SLINEAR ;
c - > readformat = AST_FORMAT_SLINEAR ;
c - > writeformat = AST_FORMAT_SLINEAR ;
c - > tech_pvt = o ;
2005-10-27 02:19:37 +00:00
if ( ! ast_strlen_zero ( ctx ) )
2005-08-03 04:11:52 +00:00
ast_copy_string ( c - > context , ctx , sizeof ( c - > context ) ) ;
2005-10-27 02:19:37 +00:00
if ( ! ast_strlen_zero ( ext ) )
2005-08-03 04:11:52 +00:00
ast_copy_string ( c - > exten , ext , sizeof ( c - > exten ) ) ;
2005-10-27 02:19:37 +00:00
if ( ! ast_strlen_zero ( o - > language ) )
2006-02-01 23:05:28 +00:00
ast_string_field_set ( c , language , o - > language ) ;
2006-07-03 04:25:21 +00:00
ast_set_callerid ( c , o - > cid_num , o - > cid_name , o - > cid_num ) ;
if ( ! ast_strlen_zero ( ext ) )
c - > cid . cid_dnid = ast_strdup ( ext ) ;
2005-08-03 04:11:52 +00:00
o - > owner = c ;
ast_setstate ( c , state ) ;
2006-06-23 16:49:12 +00:00
ast_mutex_lock ( & usecnt_lock ) ;
usecnt + + ;
ast_mutex_unlock ( & usecnt_lock ) ;
ast_update_use_count ( ) ;
2006-08-16 03:43:47 +00:00
ast_jb_configure ( c , & global_jbconf ) ;
2005-08-03 04:11:52 +00:00
if ( state ! = AST_STATE_DOWN ) {
if ( ast_pbx_start ( c ) ) {
ast_log ( LOG_WARNING , " Unable to start PBX on %s \n " , c - > name ) ;
ast_hangup ( c ) ;
2006-06-23 16:49:12 +00:00
o - > owner = c = NULL ;
/* XXX what about the channel itself ? */
/* XXX what about usecnt ? */
2000-01-06 12:33:15 +00:00
}
}
2006-05-31 16:56:50 +00:00
2005-08-03 04:11:52 +00:00
return c ;
2000-01-06 12:33:15 +00:00
}
2006-09-08 03:51:26 +00:00
static struct ast_channel * oss_request ( const char * type , int format , void * data , int * cause )
2000-01-06 12:33:15 +00:00
{
2005-08-03 04:11:52 +00:00
struct ast_channel * c ;
struct chan_oss_pvt * o = find_desc ( data ) ;
2006-09-08 03:51:26 +00:00
ast_log ( LOG_WARNING , " oss_request ty <%s> data 0x%p <%s> \n " , type , data , ( char * ) data ) ;
2005-08-03 04:11:52 +00:00
if ( o = = NULL ) {
2006-09-08 03:51:26 +00:00
ast_log ( LOG_NOTICE , " Device %s not found \n " , ( char * ) data ) ;
2005-08-03 04:11:52 +00:00
/* XXX we could default to 'dsp' perhaps ? */
2000-01-06 12:33:15 +00:00
return NULL ;
}
2005-08-03 04:11:52 +00:00
if ( ( format & AST_FORMAT_SLINEAR ) = = 0 ) {
ast_log ( LOG_NOTICE , " Format 0x%x unsupported \n " , format ) ;
return NULL ;
}
if ( o - > owner ) {
ast_log ( LOG_NOTICE , " Already have a call (chan %p) on the OSS channel \n " , o - > owner ) ;
2004-10-26 22:25:43 +00:00
* cause = AST_CAUSE_BUSY ;
2000-01-06 12:33:15 +00:00
return NULL ;
}
2006-09-08 03:51:26 +00:00
c = oss_new ( o , NULL , NULL , AST_STATE_DOWN ) ;
2005-08-03 04:11:52 +00:00
if ( c = = NULL ) {
2000-03-26 01:59:06 +00:00
ast_log ( LOG_WARNING , " Unable to create new OSS channel \n " ) ;
2005-08-03 04:11:52 +00:00
return NULL ;
2000-03-26 01:59:06 +00:00
}
2005-08-03 04:11:52 +00:00
return c ;
2000-01-06 12:33:15 +00:00
}
static int console_autoanswer ( int fd , int argc , char * argv [ ] )
{
2005-08-03 04:11:52 +00:00
struct chan_oss_pvt * o = find_desc ( oss_active ) ;
2000-01-06 12:33:15 +00:00
if ( argc = = 1 ) {
2005-08-03 04:11:52 +00:00
ast_cli ( fd , " Auto answer is %s. \n " , o - > autoanswer ? " on " : " off " ) ;
2000-01-06 12:33:15 +00:00
return RESULT_SUCCESS ;
}
2005-08-03 04:11:52 +00:00
if ( argc ! = 2 )
return RESULT_SHOWUSAGE ;
if ( o = = NULL ) {
2006-09-08 03:51:26 +00:00
ast_log ( LOG_WARNING , " Cannot find device %s (should not happen!) \n " , oss_active ) ;
2005-08-03 04:11:52 +00:00
return RESULT_FAILURE ;
}
if ( ! strcasecmp ( argv [ 1 ] , " on " ) )
o - > autoanswer = - 1 ;
else if ( ! strcasecmp ( argv [ 1 ] , " off " ) )
o - > autoanswer = 0 ;
else
return RESULT_SHOWUSAGE ;
2000-01-06 12:33:15 +00:00
return RESULT_SUCCESS ;
}
2006-01-18 22:17:31 +00:00
static char * autoanswer_complete ( const char * line , const char * word , int pos , int state )
2000-01-06 12:33:15 +00:00
{
2006-03-28 23:33:02 +00:00
static char * choices [ ] = { " on " , " off " , NULL } ;
return ( pos ! = 1 ) ? NULL : ast_cli_complete ( word , choices , state ) ;
2000-01-06 12:33:15 +00:00
}
static char autoanswer_usage [ ] =
2006-09-08 03:51:26 +00:00
" Usage: autoanswer [on|off] \n "
" Enables or disables autoanswer feature. If used without \n "
" argument, displays the current on/off status of autoanswer. \n " " The default value of autoanswer is in 'oss.conf'. \n " ;
2000-01-06 12:33:15 +00:00
2005-08-03 04:11:52 +00:00
/*
* answer command from the console
*/
2000-01-06 12:33:15 +00:00
static int console_answer ( int fd , int argc , char * argv [ ] )
{
2002-05-30 01:34:15 +00:00
struct ast_frame f = { AST_FRAME_CONTROL , AST_CONTROL_ANSWER } ;
2005-08-03 04:11:52 +00:00
struct chan_oss_pvt * o = find_desc ( oss_active ) ;
2000-01-06 12:33:15 +00:00
if ( argc ! = 1 )
return RESULT_SHOWUSAGE ;
2005-08-03 04:11:52 +00:00
if ( ! o - > owner ) {
2000-01-06 12:33:15 +00:00
ast_cli ( fd , " No one is calling us \n " ) ;
return RESULT_FAILURE ;
}
2005-08-03 04:11:52 +00:00
o - > hookstate = 1 ;
o - > cursound = - 1 ;
o - > nosound = 0 ;
ast_queue_frame ( o - > owner , & f ) ;
#if 0
/* XXX do we really need it ? considering we shut down immediately... */
ring ( o , AST_CONTROL_ANSWER ) ;
# endif
2000-01-06 12:33:15 +00:00
return RESULT_SUCCESS ;
}
2006-09-08 03:51:26 +00:00
static char sendtext_usage [ ] = " Usage: send text <message> \n " " Sends a text message for display on the remote terminal. \n " ;
2000-03-26 01:59:06 +00:00
2005-08-03 04:11:52 +00:00
/*
2006-02-10 23:19:05 +00:00
* concatenate all arguments into a single string . argv is NULL - terminated
* so we can use it right away
2005-08-03 04:11:52 +00:00
*/
2000-03-26 01:59:06 +00:00
static int console_sendtext ( int fd , int argc , char * argv [ ] )
{
2005-08-03 04:11:52 +00:00
struct chan_oss_pvt * o = find_desc ( oss_active ) ;
2006-02-10 23:19:05 +00:00
char buf [ TEXT_SIZE ] ;
2005-08-03 04:11:52 +00:00
2002-12-30 16:50:12 +00:00
if ( argc < 2 )
2000-03-26 01:59:06 +00:00
return RESULT_SHOWUSAGE ;
2005-08-03 04:11:52 +00:00
if ( ! o - > owner ) {
ast_cli ( fd , " Not in a call \n " ) ;
2000-03-26 01:59:06 +00:00
return RESULT_FAILURE ;
}
2006-09-08 03:51:26 +00:00
ast_join ( buf , sizeof ( buf ) - 1 , argv + 2 ) ;
2006-02-10 23:19:05 +00:00
if ( ! ast_strlen_zero ( buf ) ) {
struct ast_frame f = { 0 , } ;
int i = strlen ( buf ) ;
buf [ i ] = ' \n ' ;
2002-05-30 01:34:15 +00:00
f . frametype = AST_FRAME_TEXT ;
f . subclass = 0 ;
2006-02-10 23:19:05 +00:00
f . data = buf ;
f . datalen = i + 1 ;
2005-08-03 04:11:52 +00:00
ast_queue_frame ( o - > owner , & f ) ;
2002-05-30 01:34:15 +00:00
}
2000-03-26 01:59:06 +00:00
return RESULT_SUCCESS ;
}
2006-09-08 03:51:26 +00:00
static char answer_usage [ ] = " Usage: answer \n " " Answers an incoming call on the console (OSS) channel. \n " ;
2000-01-06 12:33:15 +00:00
static int console_hangup ( int fd , int argc , char * argv [ ] )
{
2005-08-03 04:11:52 +00:00
struct chan_oss_pvt * o = find_desc ( oss_active ) ;
2000-01-06 12:33:15 +00:00
if ( argc ! = 1 )
return RESULT_SHOWUSAGE ;
2005-08-03 04:11:52 +00:00
o - > cursound = - 1 ;
o - > nosound = 0 ;
2006-09-08 03:51:26 +00:00
if ( ! o - > owner & & ! o - > hookstate ) { /* XXX maybe only one ? */
2005-11-12 23:44:16 +00:00
ast_cli ( fd , " No call to hang up \n " ) ;
2000-01-06 12:33:15 +00:00
return RESULT_FAILURE ;
}
2005-08-03 04:11:52 +00:00
o - > hookstate = 0 ;
if ( o - > owner )
ast_queue_hangup ( o - > owner ) ;
setformat ( o , O_CLOSE ) ;
2000-01-06 12:33:15 +00:00
return RESULT_SUCCESS ;
}
2006-09-08 03:51:26 +00:00
static char hangup_usage [ ] = " Usage: hangup \n " " Hangs up any call currently placed on the console. \n " ;
2005-08-03 04:11:52 +00:00
2004-12-23 11:34:33 +00:00
static int console_flash ( int fd , int argc , char * argv [ ] )
{
struct ast_frame f = { AST_FRAME_CONTROL , AST_CONTROL_FLASH } ;
2005-08-03 04:11:52 +00:00
struct chan_oss_pvt * o = find_desc ( oss_active ) ;
2004-12-23 11:34:33 +00:00
if ( argc ! = 1 )
return RESULT_SHOWUSAGE ;
2005-08-03 04:11:52 +00:00
o - > cursound = - 1 ;
2006-09-08 03:51:26 +00:00
o - > nosound = 0 ; /* when cursound is -1 nosound must be 0 */
if ( ! o - > owner ) { /* XXX maybe !o->hookstate too ? */
2004-12-23 11:34:33 +00:00
ast_cli ( fd , " No call to flash \n " ) ;
return RESULT_FAILURE ;
}
2005-08-03 04:11:52 +00:00
o - > hookstate = 0 ;
2006-09-08 03:51:26 +00:00
if ( o - > owner ) /* XXX must be true, right ? */
2005-08-03 04:11:52 +00:00
ast_queue_frame ( o - > owner , & f ) ;
2004-12-23 11:34:33 +00:00
return RESULT_SUCCESS ;
}
2000-01-06 12:33:15 +00:00
2006-09-08 03:51:26 +00:00
static char flash_usage [ ] = " Usage: flash \n " " Flashes the call currently placed on the console. \n " ;
2004-12-23 11:34:33 +00:00
2005-08-03 04:11:52 +00:00
2000-01-06 12:33:15 +00:00
static int console_dial ( int fd , int argc , char * argv [ ] )
{
2005-08-03 04:11:52 +00:00
char * s = NULL , * mye = NULL , * myc = NULL ;
struct chan_oss_pvt * o = find_desc ( oss_active ) ;
if ( argc ! = 1 & & argc ! = 2 )
2000-01-06 12:33:15 +00:00
return RESULT_SHOWUSAGE ;
2006-09-08 03:51:26 +00:00
if ( o - > owner ) { /* already in a call */
2005-08-03 04:11:52 +00:00
int i ;
struct ast_frame f = { AST_FRAME_DTMF , 0 } ;
2006-09-08 03:51:26 +00:00
if ( argc = = 1 ) { /* argument is mandatory here */
2005-08-03 04:11:52 +00:00
ast_cli ( fd , " Already in a call. You can only dial digits until you hangup. \n " ) ;
2000-01-06 12:33:15 +00:00
return RESULT_FAILURE ;
}
2005-08-03 04:11:52 +00:00
s = argv [ 1 ] ;
/* send the string one char at a time */
2006-09-08 03:51:26 +00:00
for ( i = 0 ; i < strlen ( s ) ; i + + ) {
2005-08-03 04:11:52 +00:00
f . subclass = s [ i ] ;
ast_queue_frame ( o - > owner , & f ) ;
}
2000-01-06 12:33:15 +00:00
return RESULT_SUCCESS ;
}
2005-08-03 04:11:52 +00:00
/* if we have an argument split it into extension and context */
if ( argc = = 2 )
s = ast_ext_ctx ( argv [ 1 ] , & mye , & myc ) ;
/* supply default values if needed */
if ( mye = = NULL )
mye = o - > ext ;
if ( myc = = NULL )
myc = o - > ctx ;
2001-08-23 17:57:10 +00:00
if ( ast_exists_extension ( NULL , myc , mye , 1 , NULL ) ) {
2005-08-03 04:11:52 +00:00
o - > hookstate = 1 ;
oss_new ( o , mye , myc , AST_STATE_RINGING ) ;
2000-01-06 12:33:15 +00:00
} else
ast_cli ( fd , " No such extension '%s' in context '%s' \n " , mye , myc ) ;
2005-08-03 04:11:52 +00:00
if ( s )
free ( s ) ;
2000-01-06 12:33:15 +00:00
return RESULT_SUCCESS ;
}
2006-09-08 03:51:26 +00:00
static char dial_usage [ ] = " Usage: dial [extension[@context]] \n " " Dials a given extension (and context if specified) \n " ;
2000-01-06 12:33:15 +00:00
2006-09-08 03:51:26 +00:00
static char mute_usage [ ] = " Usage: mute \n Mutes the microphone \n " ;
2005-08-03 04:11:52 +00:00
2006-09-08 03:51:26 +00:00
static char unmute_usage [ ] = " Usage: unmute \n Unmutes the microphone \n " ;
2005-08-03 04:11:52 +00:00
static int console_mute ( int fd , int argc , char * argv [ ] )
{
struct chan_oss_pvt * o = find_desc ( oss_active ) ;
if ( argc ! = 1 )
return RESULT_SHOWUSAGE ;
o - > mute = 1 ;
return RESULT_SUCCESS ;
}
static int console_unmute ( int fd , int argc , char * argv [ ] )
{
struct chan_oss_pvt * o = find_desc ( oss_active ) ;
if ( argc ! = 1 )
return RESULT_SHOWUSAGE ;
o - > mute = 0 ;
return RESULT_SUCCESS ;
}
2002-08-28 04:07:50 +00:00
static int console_transfer ( int fd , int argc , char * argv [ ] )
{
2005-08-03 04:11:52 +00:00
struct chan_oss_pvt * o = find_desc ( oss_active ) ;
struct ast_channel * b = NULL ;
char * tmp , * ext , * ctx ;
2002-08-28 04:07:50 +00:00
if ( argc ! = 2 )
return RESULT_SHOWUSAGE ;
2005-08-03 04:11:52 +00:00
if ( o = = NULL )
return RESULT_FAILURE ;
2006-09-08 03:51:26 +00:00
if ( o - > owner = = NULL | | ( b = ast_bridged_channel ( o - > owner ) ) = = NULL ) {
2002-08-28 04:07:50 +00:00
ast_cli ( fd , " There is no call to transfer \n " ) ;
2005-08-03 04:11:52 +00:00
return RESULT_SUCCESS ;
}
tmp = ast_ext_ctx ( argv [ 1 ] , & ext , & ctx ) ;
2006-09-08 03:51:26 +00:00
if ( ctx = = NULL ) /* supply default context if needed */
2005-08-03 04:11:52 +00:00
ctx = o - > owner - > context ;
if ( ! ast_exists_extension ( b , ctx , ext , 1 , b - > cid . cid_num ) )
ast_cli ( fd , " No such extension exists \n " ) ;
else {
2006-09-08 03:51:26 +00:00
ast_cli ( fd , " Whee, transferring %s to %s@%s. \n " , b - > name , ext , ctx ) ;
2005-08-03 04:11:52 +00:00
if ( ast_async_goto ( b , ctx , ext , 1 ) )
ast_cli ( fd , " Failed to transfer :( \n " ) ;
2002-08-28 04:07:50 +00:00
}
2005-08-03 04:11:52 +00:00
if ( tmp )
free ( tmp ) ;
2002-08-28 04:07:50 +00:00
return RESULT_SUCCESS ;
}
2006-09-08 03:51:26 +00:00
static char transfer_usage [ ] = " Usage: transfer <extension>[@context] \n " " Transfers the currently connected call to the given extension (and \n " " context if specified) \n " ;
2000-01-06 12:33:15 +00:00
2005-09-27 00:55:55 +00:00
static char console_usage [ ] =
2006-09-08 03:51:26 +00:00
" Usage: console [device] \n "
" If used without a parameter, displays which device is the current \n " " console. If a device is specified, the console sound device is changed to \n " " the device specified. \n " ;
2005-09-27 00:55:55 +00:00
2005-08-03 04:11:52 +00:00
static int console_active ( int fd , int argc , char * argv [ ] )
{
if ( argc = = 1 )
ast_cli ( fd , " active console is [%s] \n " , oss_active ) ;
else if ( argc ! = 2 )
return RESULT_SHOWUSAGE ;
else {
struct chan_oss_pvt * o ;
if ( strcmp ( argv [ 1 ] , " show " ) = = 0 ) {
2006-09-08 03:51:26 +00:00
for ( o = oss_default . next ; o ; o = o - > next )
ast_cli ( fd , " device [%s] exists \n " , o - > name ) ;
2005-08-03 04:11:52 +00:00
return RESULT_SUCCESS ;
}
o = find_desc ( argv [ 1 ] ) ;
if ( o = = NULL )
ast_cli ( fd , " No device [%s] exists \n " , argv [ 1 ] ) ;
else
oss_active = o - > name ;
}
return RESULT_SUCCESS ;
}
2006-02-10 23:19:05 +00:00
/*
* store the boost factor
*/
static void store_boost ( struct chan_oss_pvt * o , char * s )
{
double boost = 0 ;
if ( sscanf ( s , " %lf " , & boost ) ! = 1 ) {
ast_log ( LOG_WARNING , " invalid boost <%s> \n " , s ) ;
return ;
}
2006-09-08 03:51:26 +00:00
if ( boost < - BOOST_MAX ) {
ast_log ( LOG_WARNING , " boost %s too small, using %d \n " , s , - BOOST_MAX ) ;
2006-02-10 23:19:05 +00:00
boost = - BOOST_MAX ;
} else if ( boost > BOOST_MAX ) {
2006-09-08 03:51:26 +00:00
ast_log ( LOG_WARNING , " boost %s too large, using %d \n " , s , BOOST_MAX ) ;
2006-02-10 23:19:05 +00:00
boost = BOOST_MAX ;
}
2006-09-08 03:51:26 +00:00
boost = exp ( log ( 10 ) * boost / 20 ) * BOOST_SCALE ;
2006-02-10 23:19:05 +00:00
o - > boost = boost ;
ast_log ( LOG_WARNING , " setting boost %s to %d \n " , s , o - > boost ) ;
}
static int do_boost ( int fd , int argc , char * argv [ ] )
{
struct chan_oss_pvt * o = find_desc ( oss_active ) ;
if ( argc = = 2 )
2006-09-08 03:51:26 +00:00
ast_cli ( fd , " boost currently %5.1f \n " , 20 * log10 ( ( ( double ) o - > boost / ( double ) BOOST_SCALE ) ) ) ;
2006-02-10 23:19:05 +00:00
else if ( argc = = 3 )
store_boost ( o , argv [ 2 ] ) ;
return RESULT_SUCCESS ;
}
2000-01-06 12:33:15 +00:00
static struct ast_cli_entry myclis [ ] = {
2006-09-08 03:51:26 +00:00
{ { " answer " , NULL } , console_answer , " Answer an incoming console call " , answer_usage } ,
{ { " hangup " , NULL } , console_hangup , " Hangup a call on the console " , hangup_usage } ,
{ { " flash " , NULL } , console_flash , " Flash a call on the console " , flash_usage } ,
{ { " dial " , NULL } , console_dial , " Dial an extension on the console " , dial_usage } ,
{ { " mute " , NULL } , console_mute , " Disable mic input " , mute_usage } ,
{ { " unmute " , NULL } , console_unmute , " Enable mic input " , unmute_usage } ,
{ { " transfer " , NULL } , console_transfer , " Transfer a call to a different extension " , transfer_usage } ,
{ { " send " , " text " , NULL } , console_sendtext , " Send text to the remote device " , sendtext_usage } ,
{ { " autoanswer " , NULL } , console_autoanswer , " Sets/displays autoanswer " , autoanswer_usage , autoanswer_complete } ,
{ { " oss " , " boost " , NULL } , do_boost , " Sets/displays mic boost in dB " } ,
{ { " console " , NULL } , console_active , " Sets/displays active console " , console_usage } ,
2000-01-06 12:33:15 +00:00
} ;
2005-08-03 04:11:52 +00:00
/*
* store the mixer argument from the config file , filtering possibly
* invalid or dangerous values ( the string is used as argument for
* system ( " mixer %s " )
*/
static void store_mixer ( struct chan_oss_pvt * o , char * s )
2000-01-06 12:33:15 +00:00
{
2005-08-03 04:11:52 +00:00
int i ;
2006-09-08 03:51:26 +00:00
for ( i = 0 ; i < strlen ( s ) ; i + + ) {
2005-08-03 04:11:52 +00:00
if ( ! isalnum ( s [ i ] ) & & index ( " \t -/ " , s [ i ] ) = = NULL ) {
2006-09-08 03:51:26 +00:00
ast_log ( LOG_WARNING , " Suspect char %c in mixer cmd, ignoring: \n \t %s \n " , s [ i ] , s ) ;
2005-08-03 04:11:52 +00:00
return ;
}
2000-01-06 12:33:15 +00:00
}
2005-08-03 04:11:52 +00:00
if ( o - > mixer_cmd )
free ( o - > mixer_cmd ) ;
2006-01-11 01:20:29 +00:00
o - > mixer_cmd = ast_strdup ( s ) ;
2005-08-03 04:11:52 +00:00
ast_log ( LOG_WARNING , " setting mixer %s \n " , s ) ;
}
2005-11-21 01:11:40 +00:00
/*
* store the callerid components
*/
static void store_callerid ( struct chan_oss_pvt * o , char * s )
{
ast_callerid_split ( s , o - > cid_name , sizeof ( o - > cid_name ) , o - > cid_num , sizeof ( o - > cid_num ) ) ;
}
2005-08-03 04:11:52 +00:00
/*
* grab fields from the config file , init the descriptor and open the device .
*/
2006-09-08 03:51:26 +00:00
static struct chan_oss_pvt * store_config ( struct ast_config * cfg , char * ctg )
2005-08-03 04:11:52 +00:00
{
struct ast_variable * v ;
struct chan_oss_pvt * o ;
if ( ctg = = NULL ) {
o = & oss_default ;
ctg = " general " ;
} else {
2006-01-11 01:20:29 +00:00
if ( ! ( o = ast_calloc ( 1 , sizeof ( * o ) ) ) )
2005-08-03 04:11:52 +00:00
return NULL ;
* o = oss_default ;
/* "general" is also the default thing */
if ( strcmp ( ctg , " general " ) = = 0 ) {
2006-01-11 01:20:29 +00:00
o - > name = ast_strdup ( " dsp " ) ;
2005-08-03 04:11:52 +00:00
oss_active = o - > name ;
goto openit ;
2001-05-03 04:32:56 +00:00
}
2006-01-11 01:20:29 +00:00
o - > name = ast_strdup ( ctg ) ;
2000-01-06 12:33:15 +00:00
}
2005-08-03 04:11:52 +00:00
2006-07-19 20:44:39 +00:00
strcpy ( o - > mohinterpret , " default " ) ;
2006-09-08 03:51:26 +00:00
o - > lastopen = ast_tvnow ( ) ; /* don't leave it 0 or tvdiff may wrap */
2005-08-03 04:11:52 +00:00
/* fill other fields from configuration */
2006-09-08 03:51:26 +00:00
for ( v = ast_variable_browse ( cfg , ctg ) ; v ; v = v - > next ) {
2005-08-03 04:11:52 +00:00
M_START ( v - > name , v - > value ) ;
2006-05-31 16:56:50 +00:00
/* handle jb conf */
if ( ! ast_jb_read_conf ( & global_jbconf , v - > name , v - > value ) )
continue ;
2005-08-03 04:11:52 +00:00
M_BOOL ( " autoanswer " , o - > autoanswer )
2006-09-08 03:51:26 +00:00
M_BOOL ( " autohangup " , o - > autohangup )
M_BOOL ( " overridecontext " , o - > overridecontext )
M_STR ( " device " , o - > device )
M_UINT ( " frags " , o - > frags )
M_UINT ( " debug " , oss_debug )
M_UINT ( " queuesize " , o - > queuesize )
M_STR ( " context " , o - > ctx )
M_STR ( " language " , o - > language )
M_STR ( " mohinterpret " , o - > mohinterpret )
M_STR ( " extension " , o - > ext )
M_F ( " mixer " , store_mixer ( o , v - > value ) )
M_F ( " callerid " , store_callerid ( o , v - > value ) )
M_F ( " boost " , store_boost ( o , v - > value ) )
M_END ( ;
) ;
2005-08-03 04:11:52 +00:00
}
if ( ast_strlen_zero ( o - > device ) )
ast_copy_string ( o - > device , DEV_DSP , sizeof ( o - > device ) ) ;
if ( o - > mixer_cmd ) {
char * cmd ;
asprintf ( & cmd , " mixer %s " , o - > mixer_cmd ) ;
ast_log ( LOG_WARNING , " running [%s] \n " , cmd ) ;
system ( cmd ) ;
free ( cmd ) ;
2000-01-06 12:33:15 +00:00
}
2006-09-08 03:51:26 +00:00
if ( o = = & oss_default ) /* we are done with the default */
2005-08-03 04:11:52 +00:00
return NULL ;
2006-09-08 03:51:26 +00:00
openit :
2005-08-03 04:11:52 +00:00
# if TRYOPEN
if ( setformat ( o , O_RDWR ) < 0 ) { /* open device */
if ( option_verbose > 0 ) {
ast_verbose ( VERBOSE_PREFIX_2 " Device %s not detected \n " , ctg ) ;
2006-09-08 03:51:26 +00:00
ast_verbose ( VERBOSE_PREFIX_2 " Turn off OSS support by adding " " 'noload=chan_oss.so' in /etc/asterisk/modules.conf \n " ) ;
2000-01-06 12:33:15 +00:00
}
2005-08-03 04:11:52 +00:00
goto error ;
}
if ( o - > duplex ! = M_FULL )
2006-09-08 03:51:26 +00:00
ast_log ( LOG_WARNING , " XXX I don't work right with non " " full-duplex sound cards XXX \n " ) ;
2005-08-03 04:11:52 +00:00
# endif /* TRYOPEN */
if ( pipe ( o - > sndcmd ) ! = 0 ) {
ast_log ( LOG_ERROR , " Unable to create pipe \n " ) ;
goto error ;
}
ast_pthread_create ( & o - > sthread , NULL , sound_thread , o ) ;
/* link into list of devices */
if ( o ! = & oss_default ) {
o - > next = oss_default . next ;
oss_default . next = o ;
}
return o ;
2006-09-08 03:51:26 +00:00
error :
2005-08-03 04:11:52 +00:00
if ( o ! = & oss_default )
free ( o ) ;
return NULL ;
}
2006-08-21 02:11:39 +00:00
static int load_module ( void )
2005-08-03 04:11:52 +00:00
{
int i ;
struct ast_config * cfg ;
2006-05-31 16:56:50 +00:00
/* Copy the default jb config over global_jbconf */
memcpy ( & global_jbconf , & default_jbconf , sizeof ( struct ast_jb_conf ) ) ;
2005-08-03 04:11:52 +00:00
/* load config file */
cfg = ast_config_load ( config ) ;
if ( cfg ! = NULL ) {
char * ctg = NULL ; /* first pass is 'general' */
do {
store_config ( cfg , ctg ) ;
} while ( ( ctg = ast_category_browse ( cfg , ctg ) ) ! = NULL ) ;
2005-01-25 06:10:20 +00:00
ast_config_destroy ( cfg ) ;
2006-02-08 23:14:17 +00:00
} else {
ast_log ( LOG_NOTICE , " Unable to load config oss.conf \n " ) ;
2006-08-31 21:00:20 +00:00
return AST_MODULE_LOAD_DECLINE ;
2000-01-06 12:33:15 +00:00
}
2005-08-03 04:11:52 +00:00
if ( find_desc ( oss_active ) = = NULL ) {
ast_log ( LOG_NOTICE , " Device %s not found \n " , oss_active ) ;
/* XXX we could default to 'dsp' perhaps ? */
/* XXX should cleanup allocated memory etc. */
return - 1 ;
}
i = ast_channel_register ( & oss_tech ) ;
if ( i < 0 ) {
2006-02-01 23:05:28 +00:00
ast_log ( LOG_ERROR , " Unable to register channel class 'Console' \n " ) ;
2005-08-03 04:11:52 +00:00
/* XXX should cleanup allocated memory etc. */
return - 1 ;
}
2006-09-08 03:51:26 +00:00
ast_cli_register_multiple ( myclis , sizeof ( myclis ) / sizeof ( struct ast_cli_entry ) ) ;
2000-01-06 12:33:15 +00:00
return 0 ;
}
2006-08-21 02:11:39 +00:00
static int unload_module ( void )
2000-01-06 12:33:15 +00:00
{
2005-08-03 04:11:52 +00:00
struct chan_oss_pvt * o ;
2005-03-04 06:47:24 +00:00
ast_channel_unregister ( & oss_tech ) ;
2006-09-08 03:51:26 +00:00
ast_cli_unregister_multiple ( myclis , sizeof ( myclis ) / sizeof ( struct ast_cli_entry ) ) ;
2005-08-03 04:11:52 +00:00
2006-09-08 03:51:26 +00:00
for ( o = oss_default . next ; o ; o = o - > next ) {
2005-08-03 04:11:52 +00:00
close ( o - > sounddev ) ;
if ( o - > sndcmd [ 0 ] > 0 ) {
close ( o - > sndcmd [ 0 ] ) ;
close ( o - > sndcmd [ 1 ] ) ;
}
if ( o - > owner )
ast_softhangup ( o - > owner , AST_SOFTHANGUP_APPUNLOAD ) ;
2006-09-08 03:51:26 +00:00
if ( o - > owner ) /* XXX how ??? */
2005-08-03 04:11:52 +00:00
return - 1 ;
/* XXX what about the thread ? */
/* XXX what about the memory allocated ? */
2000-01-06 12:33:15 +00:00
}
return 0 ;
}
2006-08-21 02:11:39 +00:00
AST_MODULE_INFO_STANDARD ( ASTERISK_GPL_KEY , " OSS Console Channel Driver " ) ;