Skip to content
Snippets Groups Projects
chan_usbradio.c 107 KiB
Newer Older
  • Learn to ignore specific revisions
  • #define	NEW_ASTERISK
    
    /*
     * Asterisk -- An open source telephony toolkit.
     *
     * Copyright (C) 1999 - 2005, Digium, Inc.
    
     * Copyright (C) 2007 - 2008, Jim Dixon
    
     *
     * Jim Dixon, WB6NIL <jim@lambdatel.com>
     * Steve Henke, W9SH  <w9sh@arrl.net>
     * Based upon work by Mark Spencer <markster@digium.com> and Luigi Rizzo
     *
     * 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.
     */
    
    /*! \file
     *
     * \brief Channel driver for CM108 USB Cards with Radio Interface
     *
     * \author Jim Dixon  <jim@lambdatel.com>
     * \author Steve Henke  <w9sh@arrl.net>
     *
     * \par See also
     * \arg \ref Config_usbradio
     *
     * \ingroup channel_drivers
     */
    
    /*** MODULEINFO
    
    	<depend>alsa</depend>
    
    	<defaultenabled>no</defaultenabled>
    
    <category name="MENUSELECT_CFLAGS" displayname="Compiler Flags" positive_output="yes">
    	<member name="RADIO_RTX" displayname="Build RTX/DTX Radio Programming" touch_on_change="channels/chan_usbradio.c channels/xpmr/xpmr.h">
    
    		<defaultenabled>no</defaultenabled>
    		<depend>chan_usbradio</depend>
    	</member>
    
    	<member name="RADIO_XPMRX" displayname="Build Experimental Radio Protocols" touch_on_change="channels/chan_usbradio.c">
    
    		<defaultenabled>no</defaultenabled>
    		<depend>chan_usbradio</depend>
    	</member>
    </category>
    
    // 20070918 1600 EDT sph@xelatec.com changing to rx driven streams
    
    
    #include "asterisk.h"
    
    ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
    
    
    #include <stdio.h>
    
    #include <ctype.h>
    #include <math.h>
    
    #include <string.h>
    #include <unistd.h>
    
    #ifdef HAVE_SYS_IO_H
    
    #include <sys/io.h>
    
    #include <sys/ioctl.h>
    #include <fcntl.h>
    #include <sys/time.h>
    
    #include <stdlib.h>
    #include <errno.h>
    
    #include <usb.h>
    #include <alsa/asoundlib.h>
    
    
    //#define HAVE_XPMRX				1
    #ifdef RADIO_XPMRX
    #define HAVE_XPMRX				1
    #endif
    
    #define CHAN_USBRADIO           1
    
    #define DEBUG_USBRADIO          0	
    #define DEBUG_CAPTURES	 		1
    #define DEBUG_CAP_RX_OUT		0   		
    #define DEBUG_CAP_TX_OUT	    0			
    #define DEBUG_FILETEST			0			 
    
    #define RX_CAP_RAW_FILE			"/tmp/rx_cap_in.pcm"
    #define RX_CAP_TRACE_FILE		"/tmp/rx_trace.pcm"
    #define RX_CAP_OUT_FILE			"/tmp/rx_cap_out.pcm"
    
    #define TX_CAP_RAW_FILE			"/tmp/tx_cap_in.pcm"
    #define TX_CAP_TRACE_FILE		"/tmp/tx_trace.pcm"
    #define TX_CAP_OUT_FILE			"/tmp/tx_cap_out.pcm"
    
    #define	MIXER_PARAM_MIC_PLAYBACK_SW "Mic Playback Switch"
    #define MIXER_PARAM_MIC_PLAYBACK_VOL "Mic Playback Volume"
    #define	MIXER_PARAM_MIC_CAPTURE_SW "Mic Capture Switch"
    #define	MIXER_PARAM_MIC_CAPTURE_VOL "Mic Capture Volume"
    #define	MIXER_PARAM_MIC_BOOST "Auto Gain Control"
    #define	MIXER_PARAM_SPKR_PLAYBACK_SW "Speaker Playback Switch"
    #define	MIXER_PARAM_SPKR_PLAYBACK_VOL "Speaker Playback Volume"
    
    
    #define	DELIMCHR ','
    #define	QUOTECHR 34
    
    #define	READERR_THRESHOLD 50
    
    
    #include "./xpmr/xpmr.h"
    
    #ifdef HAVE_XPMRX
    #include "./xpmrx/xpmrx.h"
    #include "./xpmrx/bitweight.h"
    #endif
    
    #define traceusb1(a) {printf a;}
    
    #else
    
    #define traceusb1(a)
    
    #define traceusb2(a) {printf a;}
    
    #else
    
    #define traceusb2(a)
    
    #endif
    
    #ifdef __linux
    #include <linux/soundcard.h>
    #elif defined(__FreeBSD__)
    #include <sys/soundcard.h>
    #else
    #include <soundcard.h>
    #endif
    
    #include "asterisk/lock.h"
    #include "asterisk/frame.h"
    
    #include "asterisk/logger.h"
    
    #include "asterisk/callerid.h"
    #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"
    #include "asterisk/stringfields.h"
    #include "asterisk/abstract_jb.h"
    #include "asterisk/musiconhold.h"
    #include "asterisk/dsp.h"
    
    
    #ifndef	NEW_ASTERISK
    
    /* ringtones we use */
    #include "busy.h"
    #include "ringtone.h"
    #include "ring10.h"
    #include "answer.h"
    
    #endif
    
    
    #define C108_VENDOR_ID		0x0d8c
    #define C108_PRODUCT_ID  	0x000c
    #define C108_HID_INTERFACE	3
    
    #define HID_REPORT_GET		0x01
    #define HID_REPORT_SET		0x09
    
    #define HID_RT_INPUT		0x01
    #define HID_RT_OUTPUT		0x02
    
    
    #define	EEPROM_START_ADDR	6
    #define	EEPROM_END_ADDR		63
    #define	EEPROM_PHYSICAL_LEN	64
    #define EEPROM_TEST_ADDR	EEPROM_END_ADDR
    #define	EEPROM_MAGIC_ADDR	6
    #define	EEPROM_MAGIC		34329
    #define	EEPROM_CS_ADDR		62
    #define	EEPROM_RXMIXERSET	8
    #define	EEPROM_TXMIXASET	9
    #define	EEPROM_TXMIXBSET	10
    #define	EEPROM_RXVOICEADJ	11
    #define	EEPROM_RXCTCSSADJ	13
    #define	EEPROM_TXCTCSSADJ	15
    #define	EEPROM_RXSQUELCHADJ	16
    
    
    /*! Global jitterbuffer configuration - by default, jb is disabled */
    static struct ast_jb_conf default_jbconf =
    {
    	.flags = 0,
    	.max_size = -1,
    	.resync_threshold = -1,
    	.impl = "",
    
    	.target_extra = -1,
    
    };
    static struct ast_jb_conf global_jbconf;
    
    
     * usbradio.conf parameters are
    START_CONFIG
    
    [general]
    
        ; General config options which propigate to all devices, with
        ; default values shown. You may have as many devices as the
        ; system will allow. You must use one section per device, with
        ; [usb] generally (although its up to you) being the first device.
    
        ;
        ;
        ; debug = 0x0		; misc debug flags, default is 0
    
    	; Set the device to use for I/O
    	; devicenum = 0
    	; Set hardware type here
    	; hdwtype=0               ; 0=limey, 1=sph
    
    
    	; rxboost=0          ; no rx gain boost
    
    	; rxctcssrelax=1        ; reduce talkoff from radios w/o CTCSS Tx HPF
    
    	; rxctcssfreqs=100.0,123.0      ; list of rx ctcss freq in floating point. must be in table
    	; txctcssfreqs=100.0,123.0      ; list tx ctcss freq, any frequency permitted
    	; txctcssdefault=100.0      ; default tx ctcss freq, any frequency permitted
    
    
    	; carrierfrom=dsp     ;no,usb,usbinvert,dsp,vox
    	; ctcssfrom=dsp       ;no,usb,dsp
    
    	; rxdemod=flat            ; input type from radio: no,speaker,flat
    	; txprelim=yes            ; output is pre-emphasised and limited
    	; txtoctype=no            ; no,phase,notone
    
    	; txmixa=composite        ;no,voice,tone,composite,auxvoice
    	; txmixb=no               ;no,voice,tone,composite,auxvoice
    
    	; invertptt=0
    
        ;------------------------------ JITTER BUFFER CONFIGURATION --------------------------
        ; jbenable = yes              ; Enables the use of a jitterbuffer on the receiving side of an
                                      ; USBRADIO channel. Defaults to "no". An enabled jitterbuffer will
                                      ; be used only if the sending side can create and the receiving
                                      ; side can not accept jitter. The USBRADIO channel can't accept jitter,
                                      ; thus an enabled jitterbuffer on the receive USBRADIO side will always
                                      ; be used if the sending side can create jitter.
    
        ; 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.
    
        ; jbimpl = fixed              ; Jitterbuffer implementation, used on the receiving side of an USBRADIO
                                      ; channel. Two implementations are currenlty available - "fixed"
                                      ; (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".
        ;-----------------------------------------------------------------------------------
    
    
    [usb]
    
    ; First channel unique config
    
    [usb1]
    
    ; Second channel config
    
    /*
     * 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 usbradio.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 usbradio.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	2				
    
    
    #if defined(__FreeBSD__)
    #define	FRAGS	0x8
    #else
    #define	FRAGS	( ( (6 * 5) << 16 ) | 0xc )
    #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
    #define	TRYOPEN	1				/* try to open on startup */
    #endif
    #define	O_CLOSE	0x444			/* special 'close' mode for device */
    /* Which device to use */
    #if defined( __OpenBSD__ ) || defined( __NetBSD__ )
    #define DEV_DSP "/dev/audio"
    #else
    #define DEV_DSP "/dev/dsp"
    #endif
    
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    static const char *config = "usbradio.conf";	/* default config file */
    #define config1 "usbradio_tune_%s.conf"    /* tune config file */
    
    
    static FILE *frxcapraw = NULL, *frxcaptrace = NULL, *frxoutraw = NULL;
    static FILE *ftxcapraw = NULL, *ftxcaptrace = NULL, *ftxoutraw = NULL;
    
    
    static char *usb_device_list = NULL;
    static int usb_device_list_size = 0;
    
    
    static int usbradio_debug;
    
    #if 0 //maw asdf sph
    
    static int usbradio_debug_level = 0;
    #endif
    
    enum {RX_AUDIO_NONE,RX_AUDIO_SPEAKER,RX_AUDIO_FLAT};
    enum {CD_IGNORE,CD_XPMR_NOISE,CD_XPMR_VOX,CD_HID,CD_HID_INVERT};
    
    enum {SD_IGNORE,SD_HID,SD_HID_INVERT,SD_XPMR};    				 // no,external,externalinvert,software
    
    enum {RX_KEY_CARRIER,RX_KEY_CARRIER_CODE};
    enum {TX_OUT_OFF,TX_OUT_VOICE,TX_OUT_LSD,TX_OUT_COMPOSITE,TX_OUT_AUX};
    enum {TOC_NONE,TOC_PHASE,TOC_NOTONE};
    
    /*	DECLARE STRUCTURES */
    
    
    /*
     * 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.
     */
    struct sound {
    	int ind;
    	char *desc;
    	short *data;
    	int datalen;
    	int samplen;
    	int silencelen;
    	int repeat;
    };
    
    #ifndef	NEW_ASTERISK
    
    static struct sound sounds[] = {
    	{ 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 */
    };
    
    #endif
    
    
    /*
     * 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_usbradio_pvt {
    	struct chan_usbradio_pvt *next;
    
    	char *name;
    
    #ifndef	NEW_ASTERISK
    	/*
    	 * 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.).
    	 */
    	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 */
    #endif
    
    	int pttkick[2];
    	int total_blocks;			/* total blocks in the output device */
    
    	int sounddev;
    	enum { M_UNSET, M_FULL, M_READ, M_WRITE } duplex;
    	i16 cdMethod;
    	int autoanswer;
    	int autohangup;
    	int hookstate;
    	unsigned int queuesize;		/* max fragments in queue */
    	unsigned int frags;			/* parameter for SETFRAGMENT */
    
    
    	int warned;					/* various flags used for warnings */
    
    #define WARN_used_blocks	1
    #define WARN_speed		2
    #define WARN_frag		4
    
    	int w_errors;				/* overfull in the write path */
    
    	struct timeval lastopen;
    
    	int overridecontext;
    	int mute;
    
    	/* boost support. BOOST_SCALE * 10 ^(BOOST_MAX/20) must
    	 * be representable in 16 bits to avoid overflows.
    	 */
    #define	BOOST_SCALE	(1<<9)
    
    #define	BOOST_MAX	40			/* slightly less than 7 bits */
    	int boost;					/* input boost, scaled by BOOST_SCALE */
    
    	char devicenum;
    
    	int spkrmax;
    	int micmax;
    
    
    #ifndef	NEW_ASTERISK
    
    	pthread_t sthread;
    
    	pthread_t hidthread;
    
    
    	int stophid;
    	FILE *hkickhid;
    
    
    	struct ast_channel *owner;
    
    	char ext[AST_MAX_EXTENSION];
    	char ctx[AST_MAX_CONTEXT];
    	char language[MAX_LANGUAGE];
    	char cid_name[256];			/*XXX */
    	char cid_num[256];			/*XXX */
    	char mohinterpret[MAX_MUSICCLASS];
    
    
    	/* buffers used in usbradio_write, 2 per int by 2 channels by 6 times oversampling (48KS/s) */
    
    	char usbradio_write_buf[FRAME_SIZE * 2 * 2 * 6];    
    	char usbradio_write_buf_1[FRAME_SIZE * 2 * 2* 6];
    
    	int usbradio_write_dst;
    
    	/* buffers used in usbradio_read - AST_FRIENDLY_OFFSET space for headers
    	 * plus enough room for a full frame
    	 */
    
    	char usbradio_read_buf[FRAME_SIZE * (2 * 12) + AST_FRIENDLY_OFFSET];
    	char usbradio_read_buf_8k[FRAME_SIZE * 2 + AST_FRIENDLY_OFFSET];
    	int readpos;				/* read position above */
    	struct ast_frame read_f;	/* returned by usbradio_read */
    
    	char 	debuglevel;
    	char 	radioduplex;			// 
    	char    wanteeprom;
    
    	int  	tracetype;
    	int     tracelevel;
    	char    area;
    	char 	rptnum;
    	int     idleinterval;
    	int		turnoffs;
    	int  	txsettletime;
    	char    ukey[48];
    
    	char lastrx;
    	char rxhidsq;
    	char rxcarrierdetect;		// status from pmr channel
    	char rxctcssdecode;			// status from pmr channel
    
    	int  rxdcsdecode;
    	int  rxlsddecode;
    
    	char rxkeytype;
    	char rxkeyed;	  			// indicates rx signal present
    
    	char lasttx;
    	char txkeyed;				// tx key request from upper layers 
    	char txchankey;
    	char txtestkey;
    
    	time_t lasthidtime;
        struct ast_dsp *dsp;
    
    	t_pmr_chan	*pmrChan;
    
    	char    rxcpusaver;
    	char    txcpusaver;
    
    	char	rxdemod;
    	float	rxgain;
    	char 	rxcdtype;
    	char 	rxsdtype;
    	int		rxsquelchadj;   /* this copy needs to be here for initialization */
    	int     rxsqvoxadj;
    	char	txtoctype;
    
    	char    txprelim;
    	float	txctcssgain;
    	char 	txmixa;
    	char 	txmixb;
    
    	char	rxctcssrelax;
    	float	rxctcssgain;
    
    	char    txctcssdefault[16];				// for repeater operation
    	char	rxctcssfreqs[512];				// a string
    	char    txctcssfreqs[512];
    
    	char	txctcssfreq[32];				// encode now
    	char	rxctcssfreq[32];				// decode now
    
    	char    numrxctcssfreqs;	  			// how many
    	char    numtxctcssfreqs;
    
    	char    *rxctcss[CTCSS_NUM_CODES]; 		// pointers to strings
    	char    *txctcss[CTCSS_NUM_CODES];
    
    	int   	txfreq;			 				// in Hz
    	int     rxfreq;
    
    	// 		start remote operation info
    	char    set_txctcssdefault[16];				// for remote operation
    	char	set_txctcssfreq[16];				// encode now
    	char	set_rxctcssfreq[16];				// decode now
    
    	char    set_numrxctcssfreqs;	  			// how many
    	char    set_numtxctcssfreqs;
    
    	char	set_rxctcssfreqs[16];				// a string
    	char    set_txctcssfreqs[16];
    
    	char    *set_rxctcss; 					    // pointers to strings
    	char    *set_txctcss;
    
    	int   	set_txfreq;			 				// in Hz
    	int     set_rxfreq;
    	// 		end remote operation info
    
    	int	   	rxmixerset;	   	
    	int 	rxboostset;
    	float	rxvoiceadj;
    	float	rxctcssadj;
    	int 	txmixaset;
    	int 	txmixbset;
    
    	int     txctcssadj;
    
    	int    	hdwtype;
    
    	int		hid_gpio_ctl;		
    	int		hid_gpio_ctl_loc;	
    	int		hid_io_cor; 		
    	int		hid_io_cor_loc; 	
    	int		hid_io_ctcss;		
    	int		hid_io_ctcss_loc; 	
    	int		hid_io_ptt; 		
    	int		hid_gpio_loc; 		
    
    	    unsigned rxcapraw:1;
    
    		unsigned txcapraw:1;
    		unsigned txcap2:1;
    		unsigned rxcap2:1;
    
    		unsigned rxplmon:1;
    		unsigned remoted:1;
    		unsigned txpolarity:1;
    		unsigned rxpolarity:1;
    		unsigned dcstxpolarity:1;
    		unsigned dcsrxpolarity:1;
    		unsigned lsdtxpolarity:1;
    		unsigned lsdrxpolarity:1;
    		unsigned loopback:1;
    		unsigned radioactive:1;
    	}b;
    	unsigned short eeprom[EEPROM_PHYSICAL_LEN];
    	char eepromctl;
    	ast_mutex_t eepromlock;
    
    	struct usb_dev_handle *usb_handle;
    	int readerrs;
    
    // maw add additional defaults !!!
    
    static struct chan_usbradio_pvt usbradio_default = {
    
    #ifndef	NEW_ASTERISK
    	.cursound = -1,
    #endif
    
    	.sounddev = -1,
    	.duplex = M_UNSET,			/* XXX check this */
    	.autoanswer = 1,
    	.autohangup = 1,
    	.queuesize = QUEUE_SIZE,
    	.frags = FRAGS,
    	.ext = "s",
    	.ctx = "default",
    	.readpos = AST_FRIENDLY_OFFSET,	/* start here on reads */
    	.lastopen = { 0, 0 },
    	.boost = BOOST_SCALE,
    
    	.wanteeprom = 1,
    	.area = 0,
    	.rptnum = 0,
    
    };
    
    /*	DECLARE FUNCTION PROTOTYPES	*/
    
    
    static void store_txtoctype(struct chan_usbradio_pvt *o, const char *s);
    
    static int	hidhdwconfig(struct chan_usbradio_pvt *o);
    static int set_txctcss_level(struct chan_usbradio_pvt *o);
    
    static void pmrdump(struct chan_usbradio_pvt *o);
    static void mult_set(struct chan_usbradio_pvt *o);
    static int  mult_calc(int value);
    static void mixer_write(struct chan_usbradio_pvt *o);
    
    static void tune_rxinput(int fd, struct chan_usbradio_pvt *o);
    static void tune_rxvoice(int fd, struct chan_usbradio_pvt *o);
    static void tune_rxctcss(int fd, struct chan_usbradio_pvt *o);
    static void tune_txoutput(struct chan_usbradio_pvt *o, int value, int fd);
    
    static void tune_write(struct chan_usbradio_pvt *o);
    
    static char *usbradio_active;	 /* the active device */
    
    
    static int setformat(struct chan_usbradio_pvt *o, int mode);
    
    static struct ast_channel *usbradio_request(const char *type, format_t format,
    		const struct ast_channel *requestor,
    		void *data, int *cause);
    
    static int usbradio_digit_begin(struct ast_channel *c, char digit);
    static int usbradio_digit_end(struct ast_channel *c, char digit, unsigned int duration);
    static int usbradio_text(struct ast_channel *c, const char *text);
    static int usbradio_hangup(struct ast_channel *c);
    static int usbradio_answer(struct ast_channel *c);
    static struct ast_frame *usbradio_read(struct ast_channel *chan);
    static int usbradio_call(struct ast_channel *c, char *dest, int timeout);
    static int usbradio_write(struct ast_channel *chan, struct ast_frame *f);
    static int usbradio_indicate(struct ast_channel *chan, int cond, const void *data, size_t datalen);
    static int usbradio_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
    
    static int xpmr_config(struct chan_usbradio_pvt *o);
    
    
    #if	DEBUG_FILETEST == 1
    static int RxTestIt(struct chan_usbradio_pvt *o);
    #endif
    
    static char tdesc[] = "USB (CM108) Radio Channel Driver";
    
    static const struct ast_channel_tech usbradio_tech = {
    	.type = "Radio",
    	.description = tdesc,
    	.capabilities = AST_FORMAT_SLINEAR,
    	.requester = usbradio_request,
    	.send_digit_begin = usbradio_digit_begin,
    	.send_digit_end = usbradio_digit_end,
    	.send_text = usbradio_text,
    	.hangup = usbradio_hangup,
    	.answer = usbradio_answer,
    	.read = usbradio_read,
    	.call = usbradio_call,
    	.write = usbradio_write,
    	.indicate = usbradio_indicate,
    	.fixup = usbradio_fixup,
    };
    
    /* Call with:  devnum: alsa major device number, param: ascii Formal
    Parameter Name, val1, first or only value, val2 second value, or 0 
    if only 1 value. Values: 0-99 (percent) or 0-1 for baboon.
    
    Note: must add -lasound to end of linkage */
    
    static int amixer_max(int devnum,char *param)
    {
    
    int	rv,type;
    char	str[100];
    snd_hctl_t *hctl;
    snd_ctl_elem_id_t *id;
    snd_hctl_elem_t *elem;
    snd_ctl_elem_info_t *info;
    
    	sprintf(str,"hw:%d",devnum);
    	if (snd_hctl_open(&hctl, str, 0)) return(-1);
    
    	snd_hctl_load(hctl);
    
    	snd_ctl_elem_id_alloca(&id);
    
    	snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER);
    	snd_ctl_elem_id_set_name(id, param);  
    	elem = snd_hctl_find_elem(hctl, id);
    
    		snd_hctl_close(hctl);
    
    	snd_ctl_elem_info_alloca(&info);
    
    	snd_hctl_elem_info(elem,info);
    	type = snd_ctl_elem_info_get_type(info);
    	rv = 0;
    
    	switch(type)
    	{
    	    case SND_CTL_ELEM_TYPE_INTEGER:
    
    		rv = snd_ctl_elem_info_get_max(info);
    		break;
    
    	    case SND_CTL_ELEM_TYPE_BOOLEAN:
    
    		rv = 1;
    		break;
    	}
    	snd_hctl_close(hctl);
    	return(rv);
    }
    
    
    /* Call with:  devnum: alsa major device number, param: ascii Formal
    
    Parameter Name, val1, first or only value, val2 second value, or 0 
    if only 1 value. Values: 0-99 (percent) or 0-1 for baboon.
    
    Note: must add -lasound to end of linkage */
    
    
    static int setamixer(int devnum,char *param, int v1, int v2)
    
    int	type;
    char	str[100];
    snd_hctl_t *hctl;
    snd_ctl_elem_id_t *id;
    snd_ctl_elem_value_t *control;
    snd_hctl_elem_t *elem;
    snd_ctl_elem_info_t *info;
    
    	sprintf(str,"hw:%d",devnum);
    	if (snd_hctl_open(&hctl, str, 0)) return(-1);
    
    	snd_hctl_load(hctl);
    
    	snd_ctl_elem_id_alloca(&id);
    
    	snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER);
    	snd_ctl_elem_id_set_name(id, param);  
    	elem = snd_hctl_find_elem(hctl, id);
    
    		snd_hctl_close(hctl);
    
    	snd_ctl_elem_info_alloca(&info);
    
    	snd_hctl_elem_info(elem,info);
    	type = snd_ctl_elem_info_get_type(info);
    
    	snd_ctl_elem_value_alloca(&control);
    
    	snd_ctl_elem_value_set_id(control, id);    
    
    	switch(type)
    	{
    	    case SND_CTL_ELEM_TYPE_INTEGER:
    
    		snd_ctl_elem_value_set_integer(control, 0, v1);
    		if (v2 > 0) snd_ctl_elem_value_set_integer(control, 1, v2);
    		break;
    
    	    case SND_CTL_ELEM_TYPE_BOOLEAN:
    
    		snd_ctl_elem_value_set_integer(control, 0, (v1 != 0));
    		break;
    	}
    
    	if (snd_hctl_elem_write(elem, control))
    	{
    
    		snd_hctl_close(hctl);
    		return(-1);
    	}
    	snd_hctl_close(hctl);
    
    }
    
    static void hid_set_outputs(struct usb_dev_handle *handle,
             unsigned char *outputs)
    {
    
    	usb_control_msg(handle,
    
    	      USB_ENDPOINT_OUT + USB_TYPE_CLASS + USB_RECIP_INTERFACE,
    	      HID_REPORT_SET,
    	      0 + (HID_RT_OUTPUT << 8),
    	      C108_HID_INTERFACE,
    	      (char*)outputs, 4, 5000);
    
    }
    
    static void hid_get_inputs(struct usb_dev_handle *handle,
             unsigned char *inputs)
    {
    
    	usb_control_msg(handle,
    
    	      USB_ENDPOINT_IN + USB_TYPE_CLASS + USB_RECIP_INTERFACE,
    	      HID_REPORT_GET,
    	      0 + (HID_RT_INPUT << 8),
    	      C108_HID_INTERFACE,
    	      (char*)inputs, 4, 5000);
    
    static unsigned short read_eeprom(struct usb_dev_handle *handle, int addr)
    
    	unsigned char buf[4];
    
    Olle Johansson's avatar
    Olle Johansson committed
    
    
    	buf[0] = 0x80;
    	buf[1] = 0;
    	buf[2] = 0;
    	buf[3] = 0x80 | (addr & 0x3f);
    	hid_set_outputs(handle,buf);
    	memset(buf,0,sizeof(buf));
    	hid_get_inputs(handle,buf);
    	return(buf[1] + (buf[2] << 8));
    }
    
    static void write_eeprom(struct usb_dev_handle *handle, int addr, 
       unsigned short data)
    {
    
    	unsigned char buf[4];
    
    	buf[0] = 0x80;
    	buf[1] = data & 0xff;
    	buf[2] = data >> 8;
    	buf[3] = 0xc0 | (addr & 0x3f);
    	hid_set_outputs(handle,buf);
    }
    
    static unsigned short get_eeprom(struct usb_dev_handle *handle,
    	unsigned short *buf)
    {
    int	i;
    unsigned short cs;
    
    	cs = 0xffff;
    	for(i = EEPROM_START_ADDR; i < EEPROM_END_ADDR; i++)
    	{
    		cs += buf[i] = read_eeprom(handle,i);
    
    Olle Johansson's avatar
    Olle Johansson committed
    	}
    
    	return(cs);
    }
    
    static void put_eeprom(struct usb_dev_handle *handle,unsigned short *buf)
    {
    int	i;
    unsigned short cs;
    
    	cs = 0xffff;
    	buf[EEPROM_MAGIC_ADDR] = EEPROM_MAGIC;
    	for(i = EEPROM_START_ADDR; i < EEPROM_CS_ADDR; i++)
    	{
    		write_eeprom(handle,i,buf[i]);
    		cs += buf[i];
    	}
    	buf[EEPROM_CS_ADDR] = (65535 - cs) + 1;
    	write_eeprom(handle,i,buf[EEPROM_CS_ADDR]);
    }
    
    static struct usb_device *hid_device_init(char *desired_device)
    {
        struct usb_bus *usb_bus;
        struct usb_device *dev;
        char devstr[200],str[200],desdev[200],*cp;
        int i;
        FILE *fp;
    
        usb_init();
        usb_find_busses();
        usb_find_devices();
        for (usb_bus = usb_busses;
             usb_bus;
             usb_bus = usb_bus->next) {
            for (dev = usb_bus->devices;
                 dev;
                 dev = dev->next) {
                if ((dev->descriptor.idVendor
                      == C108_VENDOR_ID) &&
                    (dev->descriptor.idProduct
                      == C108_PRODUCT_ID))
    		{
                            sprintf(devstr,"%s/%s", usb_bus->dirname,dev->filename);
    			for(i = 0; i < 32; i++)
    			{
    				sprintf(str,"/proc/asound/card%d/usbbus",i);
    				fp = fopen(str,"r");
    				if (!fp) continue;
    				if ((!fgets(desdev,sizeof(desdev) - 1,fp)) || (!desdev[0]))
    				{
    					fclose(fp);
    					continue;
    				}
    				fclose(fp);
    				if (desdev[strlen(desdev) - 1] == '\n')
    			        	desdev[strlen(desdev) -1 ] = 0;
    				if (strcasecmp(desdev,devstr)) continue;
    				if (i) sprintf(str,"/sys/class/sound/dsp%d/device",i);
    				else strcpy(str,"/sys/class/sound/dsp/device");
    				memset(desdev,0,sizeof(desdev));
    				if (readlink(str,desdev,sizeof(desdev) - 1) == -1)
    				{
    					sprintf(str,"/sys/class/sound/controlC%d/device",i);
    					memset(desdev,0,sizeof(desdev));
    					if (readlink(str,desdev,sizeof(desdev) - 1) == -1) continue;
    				}
    				cp = strrchr(desdev,'/');
    				if (cp) *cp = 0; else continue;
    				cp = strrchr(desdev,'/');
    				if (!cp) continue;
    				cp++;
    				break;
    			}
    			if (i >= 32) continue;
                            if (!strcmp(cp,desired_device)) return dev;
    		}
    
            }
        }
        return NULL;
    }
    
    static int hid_device_mklist(void)
    {
        struct usb_bus *usb_bus;
        struct usb_device *dev;
        char devstr[200],str[200],desdev[200],*cp;
        int i;
        FILE *fp;
    
        usb_device_list = ast_malloc(2);
        if (!usb_device_list) return -1;
        memset(usb_device_list,0,2);
    
        usb_init();
        usb_find_busses();
        usb_find_devices();
        for (usb_bus = usb_busses;
             usb_bus;
             usb_bus = usb_bus->next) {
            for (dev = usb_bus->devices;
                 dev;
                 dev = dev->next) {
                if ((dev->descriptor.idVendor
                      == C108_VENDOR_ID) &&
                    (dev->descriptor.idProduct
                      == C108_PRODUCT_ID))
    		{
                            sprintf(devstr,"%s/%s", usb_bus->dirname,dev->filename);
    			for(i = 0;i < 32; i++)
    			{
    				sprintf(str,"/proc/asound/card%d/usbbus",i);
    				fp = fopen(str,"r");
    				if (!fp) continue;
    				if ((!fgets(desdev,sizeof(desdev) - 1,fp)) || (!desdev[0]))
    				{
    					fclose(fp);
    					continue;
    				}
    				fclose(fp);
    				if (desdev[strlen(desdev) - 1] == '\n')
    			        	desdev[strlen(desdev) -1 ] = 0;
    				if (strcasecmp(desdev,devstr)) continue;
    				if (i) sprintf(str,"/sys/class/sound/dsp%d/device",i);
    				else strcpy(str,"/sys/class/sound/dsp/device");
    				memset(desdev,0,sizeof(desdev));
    				if (readlink(str,desdev,sizeof(desdev) - 1) == -1)
    				{
    					sprintf(str,"/sys/class/sound/controlC%d/device",i);
    					memset(desdev,0,sizeof(desdev));
    					if (readlink(str,desdev,sizeof(desdev) - 1) == -1) continue;
    				}
    				cp = strrchr(desdev,'/');
    				if (cp) *cp = 0; else continue;
    				cp = strrchr(desdev,'/');
    				if (!cp) continue;
    				cp++;
    				break;
    			}
    			if (i >= 32) return -1;
    			usb_device_list = ast_realloc(usb_device_list,
    				usb_device_list_size + 2 +