#define NEW_ASTERISK /* * Asterisk -- An open source telephony toolkit. * * Copyright (C) 1999 - 2005, Digium, Inc. * Copyright (C) 2007 - 2008, Jim Dixon * * Jim Dixon, WB6NIL * Steve Henke, W9SH * Based upon work by Mark Spencer 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 * \author Steve Henke * * \par See also * \arg \ref Config_usbradio * * \ingroup channel_drivers */ /*** MODULEINFO oss alsa usb no ***/ /*** MAKEOPTS no chan_usbradio no chan_usbradio ***/ // 20070918 1600 EDT sph@xelatec.com changing to rx driven streams #include "asterisk.h" ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include #include #include #include #include #ifdef HAVE_SYS_IO_H #include #endif #include #include #include #include #include #include #include //#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 #if 0 #define traceusb1(a) {printf a;} #else #define traceusb1(a) #endif #if 0 #define traceusb2(a) {printf a;} #else #define traceusb2(a) #endif #ifdef __linux #include #elif defined(__FreeBSD__) #include #else #include #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 END_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 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; char devstr[128]; int spkrmax; int micmax; #ifndef NEW_ASTERISK pthread_t sthread; #endif 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 invertptt; 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; struct { 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); if (!elem) { snd_hctl_close(hctl); return(-1); } 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); if (!elem) { snd_hctl_close(hctl); return(-1); } 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); return(0); } static void hid_set_outputs(struct usb_dev_handle *handle, unsigned char *outputs) { usleep(1500); 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) { usleep(1500); 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]; 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); } 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 + strlen(cp)); if (!usb_device_list) return -1; usb_device_list_size += strlen(cp) + 2; i = 0; while(usb_device_list[i]) { i += strlen(usb_device_list + i) + 1; } strcat(usb_device_list + i,cp); usb_device_list[strlen(cp) + i + 1] = 0; } } } return 0; } /* returns internal formatted string from external one */ static int usb_get_usbdev(char *devstr) { int i; char str[200],desdev[200],*cp; for(i = 0;i < 32; i++) { 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++; if (!strcasecmp(cp,devstr)) break; } if (i >= 32) return -1; return i; } static int usb_list_check(char *devstr) { char *s = usb_device_list; if (!s) return(0); while(*s) { if (!strcasecmp(s,devstr)) return(1); s += strlen(s) + 1; } return(0); } static int hidhdwconfig(struct chan_usbradio_pvt *o) { if(o->hdwtype==1) //sphusb { o->hid_gpio_ctl = 0x08; /* set GPIO4 to output mode */ o->hid_gpio_ctl_loc = 2; /* For CTL of GPIO */ o->hid_io_cor = 4; /* GPIO3 is COR */ o->hid_io_cor_loc = 1; /* GPIO3 is COR */ o->hid_io_ctcss = 2; /* GPIO 2 is External CTCSS */ o->hid_io_ctcss_loc = 1; /* is GPIO 2 */ o->hid_io_ptt = 8; /* GPIO 4 is PTT */ o->hid_gpio_loc = 1; /* For ALL GPIO */ } else if(o->hdwtype==0) //dudeusb { o->hid_gpio_ctl = 0x0c; /* set GPIO 3 & 4 to output mode */ o->hid_gpio_ctl_loc = 2; /* For CTL of GPIO */ o->hid_io_cor = 2; /* VOLD DN is COR */ o->hid_io_cor_loc = 0; /* VOL DN COR */ o->hid_io_ctcss = 2; /* GPIO 2 is External CTCSS */ o->hid_io_ctcss_loc = 1; /* is GPIO 2 */ o->hid_io_ptt = 4; /* GPIO 3 is PTT */ o->hid_gpio_loc = 1; /* For ALL GPIO */ } else if(o->hdwtype==3) // custom version { o->hid_gpio_ctl = 0x0c; /* set GPIO 3 & 4 to output mode */ o->hid_gpio_ctl_loc = 2; /* For CTL of GPIO */ o->hid_io_cor = 2; /* VOLD DN is COR */ o->hid_io_cor_loc = 0; /* VOL DN COR */ o->hid_io_ctcss = 2; /* GPIO 2 is External CTCSS */ o->hid_io_ctcss_loc = 1; /* is GPIO 2 */ o->hid_io_ptt = 4; /* GPIO 3 is PTT */ o->hid_gpio_loc = 1; /* For ALL GPIO */ } return 0; } /* */ static void kickptt(struct chan_usbradio_pvt *o) { char c = 0; //printf("kickptt %i %i %i\n",o->txkeyed,o->txchankey,o->txtestkey); if (!o) return; if (!o->pttkick) return; if (write(o->pttkick[1],&c,1) < 0) { ast_log(LOG_ERROR, "write() failed: %s\n", strerror(errno)); } } /* */ static void *hidthread(void *arg) { unsigned char buf[4],bufsave[4],keyed; char lastrx, txtmp; int res; struct usb_device *usb_dev; struct usb_dev_handle *usb_handle; struct chan_usbradio_pvt *o = (struct chan_usbradio_pvt *) arg; struct pollfd pfd = { .events = POLLIN }; usb_dev = hid_device_init(o->devstr); if (usb_dev == NULL) { ast_log(LOG_ERROR,"USB HID device not found\n"); pthread_exit(NULL); } usb_handle = usb_open(usb_dev); if (usb_handle == NULL) { ast_log(LOG_ERROR,"Not able to open USB device\n"); pthread_exit(NULL); } if (usb_claim_interface(usb_handle,C108_HID_INTERFACE) < 0) { if (usb_detach_kernel_driver_np(usb_handle,C108_HID_INTERFACE) < 0) { ast_log(LOG_ERROR,"Not able to detach the USB device\n"); pthread_exit(NULL); } if (usb_claim_interface(usb_handle,C108_HID_INTERFACE) < 0) { ast_log(LOG_ERROR,"Not able to claim the USB device\n"); pthread_exit(NULL); } } memset(buf,0,sizeof(buf)); buf[2] = o->hid_gpio_ctl; buf[1] = 0; hid_set_outputs(usb_handle,buf); memcpy(bufsave,buf,sizeof(buf)); if (pipe(o->pttkick) == -1) { ast_log(LOG_ERROR,"Not able to create pipe\n"); pthread_exit(NULL); } traceusb1(("hidthread: Starting normally on %s!!\n",o->name)); lastrx = 0; // popen while (!o->stophid) { pfd.fd = o->pttkick[0]; pfd.revents = 0; res = ast_poll(&pfd, 1, 50); if (res < 0) { ast_log(LOG_WARNING, "poll() failed: %s\n", strerror(errno)); usleep(10000); continue; } if (pfd.revents & POLLIN) { char c; if (read(o->pttkick[0], &c, 1) < 0) { ast_log(LOG_ERROR, "read() failed: %s\n", strerror(errno)); } } if (o->wanteeprom) { ast_mutex_lock(&o->eepromlock); if (o->eepromctl == 1) { /* to read */ /* if CS okay */ if (!get_eeprom(usb_handle, o->eeprom)) { if (o->eeprom[EEPROM_MAGIC_ADDR] != EEPROM_MAGIC) { ast_log(LOG_NOTICE, "UNSUCCESSFUL: EEPROM MAGIC NUMBER BAD on channel %s\n", o->name); } else { o->rxmixerset = o->eeprom[EEPROM_RXMIXERSET]; o->txmixaset = o->eeprom[EEPROM_TXMIXASET]; o->txmixbset = o->eeprom[EEPROM_TXMIXBSET]; memcpy(&o->rxvoiceadj, &o->eeprom[EEPROM_RXVOICEADJ], sizeof(float)); memcpy(&o->rxctcssadj, &o->eeprom[EEPROM_RXCTCSSADJ], sizeof(float)); o->txctcssadj = o->eeprom[EEPROM_TXCTCSSADJ]; o->rxsquelchadj = o->eeprom[EEPROM_RXSQUELCHADJ]; ast_log(LOG_NOTICE,"EEPROM Loaded on channel %s\n",o->name); } } else { ast_log(LOG_NOTICE, "USB Adapter has no EEPROM installed or Checksum BAD on channel %s\n", o->name); } hid_set_outputs(usb_handle,bufsave); } if (o->eepromctl == 2) { /* to write */ put_eeprom(usb_handle,o->eeprom); hid_set_outputs(usb_handle,bufsave); ast_log(LOG_NOTICE, "USB Parameters written to EEPROM on %s\n", o->name); } o->eepromctl = 0; ast_mutex_unlock(&o->eepromlock); } buf[o->hid_gpio_ctl_loc] = o->hid_gpio_ctl; hid_get_inputs(usb_handle,buf); keyed = !(buf[o->hid_io_cor_loc] & o->hid_io_cor); if (keyed != o->rxhidsq) { if (o->debuglevel) { printf("chan_usbradio() hidthread: update rxhidsq = %d\n", keyed); } o->rxhidsq=keyed; } /* if change in tx state as controlled by xpmr */ txtmp = o->pmrChan->txPttOut; if (o->lasttx != txtmp) { o->pmrChan->txPttHid = o->lasttx = txtmp; if (o->debuglevel) { ast_debug(0, "hidthread: tx set to %d\n", txtmp); } buf[o->hid_gpio_loc] = 0; if (!o->invertptt) { if (txtmp) { buf[o->hid_gpio_loc] = o->hid_io_ptt; } } else { if (!txtmp) { buf[o->hid_gpio_loc] = o->hid_io_ptt; } } buf[o->hid_gpio_ctl_loc] = o->hid_gpio_ctl; memcpy(bufsave, buf, sizeof(buf)); hid_set_outputs(usb_handle, buf); } time(&o->lasthidtime); } buf[o->hid_gpio_loc] = 0; if (o->invertptt) { buf[o->hid_gpio_loc] = o->hid_io_ptt; } buf[o->hid_gpio_ctl_loc] = o->hid_gpio_ctl; hid_set_outputs(usb_handle, buf); pthread_exit(0); } /* * returns a pointer to the descriptor with the given name */ static struct chan_usbradio_pvt *find_desc(char *dev) { struct chan_usbradio_pvt *o = NULL; if (!dev) ast_log(LOG_WARNING, "null dev\n"); for (o = usbradio_default.next; o && o->name && dev && strcmp(o->name, dev) != 0; o = o->next); if (!o) { ast_log(LOG_WARNING, "could not find <%s>\n", dev ? dev : "--no-device--"); } return o; } static struct chan_usbradio_pvt *find_desc_usb(char *devstr) { struct chan_usbradio_pvt *o = NULL; if (!devstr) ast_log(LOG_WARNING, "null dev\n"); for (o = usbradio_default.next; o && devstr && strcmp(o->devstr, devstr) != 0; o = o->next); return o; } /* * split a string in extension-context, returns pointers to malloc'ed * strings. * If we do not have 'overridecontext' then the last @ is considered as * 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. */ #if 0 static char *ast_ext_ctx(const char *src, char **ext, char **ctx) { struct chan_usbradio_pvt *o = find_desc(usbradio_active); if (ext == NULL || ctx == NULL) return NULL; /* error */ *ext = *ctx = NULL; if (src && *src != '\0') *ext = ast_strdup(src); if (*ext == NULL) return NULL; if (!o->overridecontext) { /* parse from the right */ *ctx = strrchr(*ext, '@'); if (*ctx) *(*ctx)++ = '\0'; } return *ext; } #endif /* * Returns the number of blocks used in the audio output channel */ static int used_blocks(struct chan_usbradio_pvt *o) { struct audio_buf_info info; if (ioctl(o->sounddev, SNDCTL_DSP_GETOSPACE, &info)) { if (!(o->warned & WARN_used_blocks)) { ast_log(LOG_WARNING, "Error reading output space\n"); o->warned |= WARN_used_blocks; } return 1; } if (o->total_blocks == 0) { if (0) /* debugging */ ast_log(LOG_WARNING, "fragtotal %d size %d avail %d\n", info.fragstotal, info.fragsize, info.fragments); o->total_blocks = info.fragments; } return o->total_blocks - info.fragments; } /* Write an exactly FRAME_SIZE sized frame */ static int soundcard_writeframe(struct chan_usbradio_pvt *o, short *data) { int res; if (o->sounddev < 0) setformat(o, O_RDWR); if (o->sounddev < 0) return 0; /* not fatal */ // maw maw sph !!! may or may not be a good thing // drop the frame if not transmitting, this keeps from gradually // filling the buffer when asterisk clock > usb sound clock if(!o->pmrChan->txPttIn && !o->pmrChan->txPttOut) { //return 0; } /* * 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 */ // ast_log(LOG_WARNING, "sound device write buffer overflow\n"); if (o->w_errors++ == 0 && (usbradio_debug & 0x4)) ast_log(LOG_WARNING, "write: used %d blocks (%d)\n", res, o->w_errors); return 0; } o->w_errors = 0; return write(o->sounddev, ((void *) data), FRAME_SIZE * 2 * 12); } #ifndef NEW_ASTERISK /* * 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_usbradio_pvt *o) { short myframe[FRAME_SIZE]; int ofs, l, start; int l_sampsent = o->sampsent; struct sound *s; if (o->cursound < 0) /* no sound to send */ return; s = &sounds[o->cursound]; for (ofs = 0; ofs < FRAME_SIZE; ofs += l) { l = s->samplen - l_sampsent; /* # of available samples */ if (l > 0) { start = l_sampsent % s->datalen; /* source offset */ 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; memmove(myframe + ofs, s->data + start, l * 2); if (0) ast_log(LOG_WARNING, "send_sound sound %d/%d of %d into %d\n", l_sampsent, l, s->samplen, ofs); l_sampsent += l; } else { /* end of samples, maybe some silence */ static const short silence[FRAME_SIZE] = { 0, }; l += s->silencelen; if (l > 0) { if (l > FRAME_SIZE - ofs) l = FRAME_SIZE - ofs; memmove(myframe + ofs, silence, l * 2); l_sampsent += l; } else { /* silence is over, restart sound if loop */ if (s->repeat == 0) { /* last block */ o->cursound = -1; o->nosound = 0; /* allow audio data */ if (ofs < FRAME_SIZE) /* pad with silence */ memmove(myframe + ofs, silence, (FRAME_SIZE - ofs) * 2); } l_sampsent = 0; } } } l = soundcard_writeframe(o, myframe); if (l > 0) o->sampsent = l_sampsent; /* update status */ } static void *sound_thread(void *arg) { char ign[4096]; struct chan_usbradio_pvt *o = (struct chan_usbradio_pvt *) arg; /* * 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 (;;) { struct pollfd pfd[2] = { { .fd = o->sndcmd[0], .events = POLLIN }, { .fd = o->sounddev } }; int res; if (o->cursound > -1 && o->sounddev < 0) { setformat(o, O_RDWR); /* need the channel, try to reopen */ } else if (o->cursound == -1 && o->owner == NULL) { setformat(o, O_CLOSE); /* can close */ } if (o->sounddev > -1) { if (!o->owner) { /* no one owns the audio, so we must drain it */ pfd[1].events = POLLIN; } if (o->cursound > -1) { pfd[1].events |= POLLOUT; } } res = ast_poll(pfd, o->sounddev > -1 ? 2 : 1, -1); if (res < 1) { ast_log(LOG_WARNING, "poll failed: %s\n", strerror(errno)); sleep(1); continue; } if (pfd[0].revents & POLLIN) { /* 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; o->nosound = 1; /* block audio from pbx */ break; } } if (sounds[i].ind == -1) { ast_log(LOG_WARNING, "invalid sound index: %d\n", what); } } if (o->sounddev > -1) { if (pfd[1].revents & POLLIN) { /* read and ignore errors */ read(o->sounddev, ign, sizeof(ign)); } if (pfd[1].revents & POLLOUT) { send_sound(o); } } } return NULL; /* Never reached */ } #endif /* * 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_usbradio_pvt *o, int mode) { int fmt, desired, res, fd; char device[100]; if (o->sounddev >= 0) { ioctl(o->sounddev, SNDCTL_DSP_RESET, 0); close(o->sounddev); o->duplex = M_UNSET; o->sounddev = -1; } if (mode == O_CLOSE) /* we are done */ return 0; o->lastopen = ast_tvnow(); strcpy(device,"/dev/dsp"); if (o->devicenum) sprintf(device,"/dev/dsp%d",o->devicenum); fd = o->sounddev = open(device, mode | O_NONBLOCK); if (fd < 0) { ast_log(LOG_WARNING, "Unable to re-open DSP device %d: %s\n", o->devicenum, strerror(errno)); return -1; } if (o->owner) o->owner->fds[0] = fd; #if __BYTE_ORDER == __LITTLE_ENDIAN fmt = AFMT_S16_LE; #else fmt = AFMT_S16_BE; #endif 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; } switch (mode) { 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; } fmt = 1; res = ioctl(fd, SNDCTL_DSP_STEREO, &fmt); if (res < 0) { ast_log(LOG_WARNING, "Failed to set audio device to mono\n"); return -1; } fmt = desired = 48000; /* 8000 Hz desired */ res = ioctl(fd, SNDCTL_DSP_SPEED, &fmt); if (res < 0) { ast_log(LOG_WARNING, "Failed to set audio device to mono\n"); return -1; } if (fmt != desired) { 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; } } /* * 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; } } } /* 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; } /* * some of the standard methods supported by channels. */ static int usbradio_digit_begin(struct ast_channel *c, char digit) { return 0; } static int usbradio_digit_end(struct ast_channel *c, char digit, unsigned int duration) { /* no better use for received digits than print them */ ast_verbose(" << Console Received digit %c of duration %u ms >> \n", digit, duration); return 0; } /* SETFREQ - sets spi programmable xcvr SETCHAN - sets binary parallel xcvr */ static int usbradio_text(struct ast_channel *c, const char *text) { struct chan_usbradio_pvt *o = find_desc(usbradio_active); double tx,rx; char cnt,rxs[16],txs[16],txpl[16],rxpl[16]; char pwr,*cmd; cmd = alloca(strlen(text) + 10); /* print received messages */ if(o->debuglevel)ast_verbose(" << Console Received usbradio text %s >> \n", text); cnt = sscanf(text, "%300s %15s %15s %15s %15s %1c", cmd, rxs, txs, rxpl, txpl, &pwr); if (strcmp(cmd,"SETCHAN")==0) { u8 chan; chan=strtod(rxs,NULL); ppbinout(chan); if(o->debuglevel)ast_log(LOG_NOTICE,"parse usbradio SETCHAN cmd: %s chan: %i\n",text,chan); return 0; } if (cnt < 6) { ast_log(LOG_ERROR,"Cannot parse usbradio text: %s\n",text); return 0; } else { if(o->debuglevel)ast_verbose(" << %s %s %s %s %s %c >> \n", cmd,rxs,txs,rxpl,txpl,pwr); } if (strcmp(cmd,"SETFREQ")==0) { if(o->debuglevel)ast_log(LOG_NOTICE,"parse usbradio SETFREQ cmd: %s\n",text); tx=strtod(txs,NULL); rx=strtod(rxs,NULL); o->set_txfreq = round(tx * (double)1000000); o->set_rxfreq = round(rx * (double)1000000); o->pmrChan->txpower = (pwr == 'H'); strcpy(o->set_rxctcssfreqs,rxpl); strcpy(o->set_txctcssfreqs,txpl); o->b.remoted=1; xpmr_config(o); return 0; } ast_log(LOG_ERROR,"Cannot parse usbradio cmd: %s\n",text); return 0; } /* Play ringtone 'x' on device 'o' */ static void ring(struct chan_usbradio_pvt *o, int x) { #ifndef NEW_ASTERISK write(o->sndcmd[1], &x, sizeof(x)); #endif } /* * handler for incoming calls. Either autoanswer, or start ringing */ static int usbradio_call(struct ast_channel *c, char *dest, int timeout) { struct chan_usbradio_pvt *o = c->tech_pvt; o->stophid = 0; time(&o->lasthidtime); ast_pthread_create_background(&o->hidthread, NULL, hidthread, o); ast_setstate(c, AST_STATE_UP); return 0; } /* * remote side answered the phone */ static int usbradio_answer(struct ast_channel *c) { #ifndef NEW_ASTERISK struct chan_usbradio_pvt *o = c->tech_pvt; #endif ast_setstate(c, AST_STATE_UP); #ifndef NEW_ASTERISK o->cursound = -1; o->nosound = 0; #endif return 0; } static int usbradio_hangup(struct ast_channel *c) { struct chan_usbradio_pvt *o = c->tech_pvt; //ast_log(LOG_NOTICE, "usbradio_hangup()\n"); #ifndef NEW_ASTERISK o->cursound = -1; o->nosound = 0; #endif c->tech_pvt = NULL; o->owner = NULL; ast_module_unref(ast_module_info->self); if (o->hookstate) { if (o->autoanswer || o->autohangup) { /* Assume auto-hangup too */ o->hookstate = 0; setformat(o, O_CLOSE); } else { /* Make congestion noise */ ring(o, AST_CONTROL_CONGESTION); } } o->stophid = 1; pthread_join(o->hidthread,NULL); return 0; } /* used for data coming from the network */ static int usbradio_write(struct ast_channel *c, struct ast_frame *f) { struct chan_usbradio_pvt *o = c->tech_pvt; traceusb2(("usbradio_write() o->nosound= %i\n",o->nosound)); #ifndef NEW_ASTERISK /* Immediately return if no sound is enabled */ if (o->nosound) return 0; /* Stop any currently playing sound */ o->cursound = -1; #endif /* * 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. */ #if DEBUG_CAPTURES == 1 // to write input data to a file datalen=320 if (ftxcapraw && o->b.txcapraw) { i16 i, tbuff[f->datalen]; for(i=0;idatalen;i+=2) { tbuff[i]= ((i16*)(f->data.ptr))[i/2]; tbuff[i+1]= o->txkeyed*M_Q13; } if (fwrite(tbuff,2,f->datalen,ftxcapraw) != f->datalen) { ast_log(LOG_ERROR, "write() failed: %s\n", strerror(errno)); } //fwrite(f->data,1,f->datalen,ftxcapraw); } #endif // maw just take the data from the network and save it for PmrRx processing PmrTx(o->pmrChan,(i16*)f->data.ptr); return 0; } static struct ast_frame *usbradio_read(struct ast_channel *c) { int res, src, datalen, oldpttout; int cd,sd; struct chan_usbradio_pvt *o = c->tech_pvt; struct ast_frame *f = &o->read_f,*f1; struct ast_frame wf = { AST_FRAME_CONTROL }; time_t now; traceusb2(("usbradio_read()\n")); if (o->lasthidtime) { time(&now); if ((now - o->lasthidtime) > 3) { ast_log(LOG_ERROR,"HID process has died or something!!\n"); return NULL; } } /* XXX can be simplified returning &ast_null_frame */ /* prepare a NULL frame in case we don't have enough data to return */ memset(f, '\0', sizeof(struct ast_frame)); f->frametype = AST_FRAME_NULL; f->src = usbradio_tech.type; res = read(o->sounddev, o->usbradio_read_buf + o->readpos, sizeof(o->usbradio_read_buf) - o->readpos); if (res < 0) /* audio data not ready, return a NULL frame */ { if (errno != EAGAIN) return NULL; if (o->readerrs++ > READERR_THRESHOLD) { ast_log(LOG_ERROR,"Stuck USB read channel [%s], un-sticking it!\n",o->name); o->readerrs = 0; return NULL; } if (o->readerrs == 1) ast_log(LOG_WARNING,"Possibly stuck USB read channel. [%s]\n",o->name); return f; } if (o->readerrs) ast_log(LOG_WARNING,"Nope, USB read channel [%s] wasn't stuck after all.\n",o->name); o->readerrs = 0; o->readpos += res; if (o->readpos < sizeof(o->usbradio_read_buf)) /* not enough samples */ return f; if (o->mute) return f; #if DEBUG_CAPTURES == 1 if ((o->b.rxcapraw && frxcapraw) && (fwrite((o->usbradio_read_buf + AST_FRIENDLY_OFFSET),1,FRAME_SIZE * 2 * 2 * 6,frxcapraw) != FRAME_SIZE * 2 * 2 * 6)) { ast_log(LOG_ERROR, "fwrite() failed: %s\n", strerror(errno)); } #endif #if 1 if(o->txkeyed||o->txtestkey) { if(!o->pmrChan->txPttIn) { o->pmrChan->txPttIn=1; if(o->debuglevel) ast_log(LOG_NOTICE,"txPttIn = %i, chan %s\n",o->pmrChan->txPttIn,o->owner->name); } } else if(o->pmrChan->txPttIn) { o->pmrChan->txPttIn=0; if(o->debuglevel) ast_log(LOG_NOTICE,"txPttIn = %i, chan %s\n",o->pmrChan->txPttIn,o->owner->name); } oldpttout = o->pmrChan->txPttOut; PmrRx( o->pmrChan, (i16 *)(o->usbradio_read_buf + AST_FRIENDLY_OFFSET), (i16 *)(o->usbradio_read_buf_8k + AST_FRIENDLY_OFFSET), (i16 *)(o->usbradio_write_buf_1)); if (oldpttout != o->pmrChan->txPttOut) { if(o->debuglevel) ast_log(LOG_NOTICE,"txPttOut = %i, chan %s\n",o->pmrChan->txPttOut,o->owner->name); kickptt(o); } #if 0 // to write 48KS/s stereo tx data to a file if (!ftxoutraw) ftxoutraw = fopen(TX_CAP_OUT_FILE,"w"); if (ftxoutraw) fwrite(o->usbradio_write_buf_1,1,FRAME_SIZE * 2 * 6,ftxoutraw); #endif #if DEBUG_CAPTURES == 1 && XPMR_DEBUG0 == 1 if ((o->b.txcap2 && ftxcaptrace) && (fwrite((o->pmrChan->ptxDebug),1,FRAME_SIZE * 2 * 16,ftxcaptrace) != FRAME_SIZE * 2 * 16)) { ast_log(LOG_ERROR, "fwrite() failed: %s\n", strerror(errno)); } #endif // 160 samples * 2 bytes/sample * 2 chan * 6x oversampling to 48KS/s datalen = FRAME_SIZE * 24; src = 0; /* read position into f->data */ while (src < datalen) { /* Compute spare room in the buffer */ int l = sizeof(o->usbradio_write_buf) - o->usbradio_write_dst; if (datalen - src >= l) { /* enough to fill a frame */ memcpy(o->usbradio_write_buf + o->usbradio_write_dst, o->usbradio_write_buf_1 + src, l); soundcard_writeframe(o, (short *) o->usbradio_write_buf); src += l; o->usbradio_write_dst = 0; } else { /* copy residue */ l = datalen - src; memcpy(o->usbradio_write_buf + o->usbradio_write_dst, o->usbradio_write_buf_1 + src, l); src += l; /* but really, we are done */ o->usbradio_write_dst += l; } } #else static FILE *hInput; i16 iBuff[FRAME_SIZE*2*6]; o->pmrChan->b.rxCapture=1; if(!hInput) { hInput = fopen("/usr/src/xpmr/testdata/rx_in.pcm","r"); if(!hInput) { printf(" Input Data File Not Found.\n"); return 0; } } if(0==fread((void *)iBuff,2,FRAME_SIZE*2*6,hInput))exit; PmrRx( o->pmrChan, (i16 *)iBuff, (i16 *)(o->usbradio_read_buf_8k + AST_FRIENDLY_OFFSET)); #endif #if 0 if (!frxoutraw) frxoutraw = fopen(RX_CAP_OUT_FILE,"w"); if (frxoutraw) fwrite((o->usbradio_read_buf_8k + AST_FRIENDLY_OFFSET),1,FRAME_SIZE * 2,frxoutraw); #endif #if DEBUG_CAPTURES == 1 && XPMR_DEBUG0 == 1 if ((frxcaptrace && o->b.rxcap2 && o->pmrChan->b.radioactive) && (fwrite((o->pmrChan->prxDebug),1,FRAME_SIZE * 2 * 16,frxcaptrace) != FRAME_SIZE * 2 * 16 )) { ast_log(LOG_ERROR, "fwrite() failed: %s\n", strerror(errno)); } #endif cd = 0; if(o->rxcdtype==CD_HID && (o->pmrChan->rxExtCarrierDetect!=o->rxhidsq)) o->pmrChan->rxExtCarrierDetect=o->rxhidsq; if(o->rxcdtype==CD_HID_INVERT && (o->pmrChan->rxExtCarrierDetect==o->rxhidsq)) o->pmrChan->rxExtCarrierDetect=!o->rxhidsq; if( (o->rxcdtype==CD_HID && o->rxhidsq) || (o->rxcdtype==CD_HID_INVERT && !o->rxhidsq) || (o->rxcdtype==CD_XPMR_NOISE && o->pmrChan->rxCarrierDetect) || (o->rxcdtype==CD_XPMR_VOX && o->pmrChan->rxCarrierDetect) ) { if (!o->pmrChan->txPttOut || o->radioduplex)cd=1; } else { cd=0; } if(cd!=o->rxcarrierdetect) { o->rxcarrierdetect=cd; if(o->debuglevel) ast_log(LOG_NOTICE,"rxcarrierdetect = %i, chan %s\n",cd,o->owner->name); // printf("rxcarrierdetect = %i, chan %s\n",res,o->owner->name); } if(o->pmrChan->b.ctcssRxEnable && o->pmrChan->rxCtcss->decode!=o->rxctcssdecode) { if(o->debuglevel)ast_log(LOG_NOTICE,"rxctcssdecode = %i, chan %s\n",o->pmrChan->rxCtcss->decode,o->owner->name); // printf("rxctcssdecode = %i, chan %s\n",o->pmrChan->rxCtcss->decode,o->owner->name); o->rxctcssdecode=o->pmrChan->rxCtcss->decode; strcpy(o->rxctcssfreq, o->pmrChan->rxctcssfreq); } #ifndef HAVE_XPMRX if( !o->pmrChan->b.ctcssRxEnable || ( o->pmrChan->b.ctcssRxEnable && o->pmrChan->rxCtcss->decode>CTCSS_NULL && o->pmrChan->smode==SMODE_CTCSS ) ) { sd=1; } else { sd=0; } #else if( (!o->pmrChan->b.ctcssRxEnable && !o->pmrChan->b.dcsRxEnable && !o->pmrChan->b.lmrRxEnable) || ( o->pmrChan->b.ctcssRxEnable && o->pmrChan->rxCtcss->decode>CTCSS_NULL && o->pmrChan->smode==SMODE_CTCSS ) || ( o->pmrChan->b.dcsRxEnable && o->pmrChan->decDcs->decode > 0 && o->pmrChan->smode==SMODE_DCS ) ) { sd=1; } else { sd=0; } if(o->pmrChan->decDcs->decode!=o->rxdcsdecode) { if(o->debuglevel)ast_log(LOG_NOTICE,"rxdcsdecode = %s, chan %s\n",o->pmrChan->rxctcssfreq,o->owner->name); // printf("rxctcssdecode = %i, chan %s\n",o->pmrChan->rxCtcss->decode,o->owner->name); o->rxdcsdecode=o->pmrChan->decDcs->decode; strcpy(o->rxctcssfreq, o->pmrChan->rxctcssfreq); } if(o->pmrChan->rptnum && (o->pmrChan->pLsdCtl->cs[o->pmrChan->rptnum].b.rxkeyed != o->rxlsddecode)) { if(o->debuglevel)ast_log(LOG_NOTICE,"rxLSDecode = %s, chan %s\n",o->pmrChan->rxctcssfreq,o->owner->name); o->rxlsddecode=o->pmrChan->pLsdCtl->cs[o->pmrChan->rptnum].b.rxkeyed; strcpy(o->rxctcssfreq, o->pmrChan->rxctcssfreq); } if( (o->pmrChan->rptnum>0 && o->pmrChan->smode==SMODE_LSD && o->pmrChan->pLsdCtl->cs[o->pmrChan->rptnum].b.rxkeyed)|| (o->pmrChan->smode==SMODE_DCS && o->pmrChan->decDcs->decode>0) ) { sd=1; } #endif if ( cd && sd ) { //if(!o->rxkeyed)o->pmrChan->dd.b.doitnow=1; if(!o->rxkeyed && o->debuglevel)ast_log(LOG_NOTICE,"o->rxkeyed = 1, chan %s\n", o->owner->name); o->rxkeyed = 1; } else { //if(o->rxkeyed)o->pmrChan->dd.b.doitnow=1; if(o->rxkeyed && o->debuglevel)ast_log(LOG_NOTICE,"o->rxkeyed = 0, chan %s\n",o->owner->name); o->rxkeyed = 0; } // provide rx signal detect conditions if (o->lastrx && (!o->rxkeyed)) { o->lastrx = 0; //printf("AST_CONTROL_RADIO_UNKEY\n"); wf.subclass.integer = AST_CONTROL_RADIO_UNKEY; ast_queue_frame(o->owner, &wf); } else if ((!o->lastrx) && (o->rxkeyed)) { o->lastrx = 1; //printf("AST_CONTROL_RADIO_KEY\n"); wf.subclass.integer = AST_CONTROL_RADIO_KEY; if(o->rxctcssdecode) { wf.data.ptr = o->rxctcssfreq; wf.datalen = strlen(o->rxctcssfreq) + 1; TRACEO(1,("AST_CONTROL_RADIO_KEY text=%s\n",o->rxctcssfreq)); } ast_queue_frame(o->owner, &wf); } 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.codec = AST_FORMAT_SLINEAR; f->samples = FRAME_SIZE; f->datalen = FRAME_SIZE * 2; f->data.ptr = o->usbradio_read_buf_8k + AST_FRIENDLY_OFFSET; if (o->boost != BOOST_SCALE) { /* scale and clip values */ int i, x; int16_t *p = (int16_t *) f->data.ptr; 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; } } f->offset = AST_FRIENDLY_OFFSET; if (o->dsp) { f1 = ast_dsp_process(c,o->dsp,f); if ((f1->frametype == AST_FRAME_DTMF_END) || (f1->frametype == AST_FRAME_DTMF_BEGIN)) { if ((f1->subclass.integer == 'm') || (f1->subclass.integer == 'u')) { f1->frametype = AST_FRAME_NULL; f1->subclass.integer = 0; return(f1); } if (f1->frametype == AST_FRAME_DTMF_END) ast_log(LOG_NOTICE, "Got DTMF char %c\n", f1->subclass.integer); return(f1); } } return f; } static int usbradio_fixup(struct ast_channel *oldchan, struct ast_channel *newchan) { struct chan_usbradio_pvt *o = newchan->tech_pvt; ast_log(LOG_WARNING,"usbradio_fixup()\n"); o->owner = newchan; return 0; } static int usbradio_indicate(struct ast_channel *c, int cond, const void *data, size_t datalen) { struct chan_usbradio_pvt *o = c->tech_pvt; int res = -1; switch (cond) { case AST_CONTROL_BUSY: case AST_CONTROL_CONGESTION: case AST_CONTROL_RINGING: res = cond; break; case -1: #ifndef NEW_ASTERISK o->cursound = -1; o->nosound = 0; /* when cursound is -1 nosound must be 0 */ #endif 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; case AST_CONTROL_PROCEEDING: ast_verbose(" << Call Proceeding... >> \n"); ast_moh_stop(c); break; case AST_CONTROL_PROGRESS: ast_verbose(" << Call Progress... >> \n"); ast_moh_stop(c); break; case AST_CONTROL_RADIO_KEY: o->txkeyed = 1; if(o->debuglevel)ast_verbose(" << AST_CONTROL_RADIO_KEY Radio Transmit On. >> \n"); break; case AST_CONTROL_RADIO_UNKEY: o->txkeyed = 0; if(o->debuglevel)ast_verbose(" << AST_CONTROL_RADIO_UNKEY Radio Transmit Off. >> \n"); break; default: ast_log(LOG_WARNING, "Don't know how to display condition %d on %s\n", cond, c->name); return -1; } if (res > -1) ring(o, res); return 0; } /* * allocate a new channel. */ static struct ast_channel *usbradio_new(struct chan_usbradio_pvt *o, char *ext, char *ctx, int state, const char *linkedid) { struct ast_channel *c; c = ast_channel_alloc(1, state, o->cid_num, o->cid_name, "", ext, ctx, linkedid, 0, "Radio/%s", o->name); if (c == NULL) return NULL; c->tech = &usbradio_tech; if (o->sounddev < 0) setformat(o, O_RDWR); c->fds[0] = o->sounddev; /* -1 if device closed, override later */ c->nativeformats = AST_FORMAT_SLINEAR; c->readformat = AST_FORMAT_SLINEAR; c->writeformat = AST_FORMAT_SLINEAR; c->tech_pvt = o; if (!ast_strlen_zero(o->language)) ast_string_field_set(c, language, o->language); /* Don't use ast_set_callerid() here because it will * generate a needless NewCallerID event */ if (!ast_strlen_zero(o->cid_num)) { c->caller.ani.number.valid = 1; c->caller.ani.number.str = ast_strdup(o->cid_num); } if (!ast_strlen_zero(ext)) { c->dialed.number.str = ast_strdup(ext); } o->owner = c; ast_module_ref(ast_module_info->self); ast_jb_configure(c, &global_jbconf); 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); o->owner = c = NULL; /* XXX what about the channel itself ? */ /* XXX what about usecnt ? */ } } return c; } /* */ static struct ast_channel *usbradio_request(const char *type, format_t format, const struct ast_channel *requestor, void *data, int *cause) { struct ast_channel *c; struct chan_usbradio_pvt *o = find_desc(data); TRACEO(1,("usbradio_request()\n")); if (0) { ast_log(LOG_WARNING, "usbradio_request type <%s> data 0x%p <%s>\n", type, data, (char *) data); } if (o == NULL) { ast_log(LOG_NOTICE, "Device %s not found\n", (char *) data); /* XXX we could default to 'dsp' perhaps ? */ return NULL; } if ((format & AST_FORMAT_SLINEAR) == 0) { ast_log(LOG_NOTICE, "Format 0x%" PRIx64 " unsupported\n", format); return NULL; } if (o->owner) { ast_log(LOG_NOTICE, "Already have a call (chan %p) on the usb channel\n", o->owner); *cause = AST_CAUSE_BUSY; return NULL; } c = usbradio_new(o, NULL, NULL, AST_STATE_DOWN, requestor ? requestor->linkedid : NULL); if (c == NULL) { ast_log(LOG_WARNING, "Unable to create new usb channel\n"); return NULL; } o->b.remoted=0; xpmr_config(o); return c; } /* */ static int console_key(int fd, int argc, char *argv[]) { struct chan_usbradio_pvt *o = find_desc(usbradio_active); if (argc != 2) return RESULT_SHOWUSAGE; o->txtestkey = 1; return RESULT_SUCCESS; } /* */ static int console_unkey(int fd, int argc, char *argv[]) { struct chan_usbradio_pvt *o = find_desc(usbradio_active); if (argc != 2) return RESULT_SHOWUSAGE; o->txtestkey = 0; return RESULT_SUCCESS; } static int radio_tune(int fd, int argc, char *argv[]) { struct chan_usbradio_pvt *o = find_desc(usbradio_active); int i=0; if ((argc < 2) || (argc > 4)) return RESULT_SHOWUSAGE; if (argc == 2) /* just show stuff */ { ast_cli(fd,"Active radio interface is [%s]\n",usbradio_active); ast_cli(fd,"Output A is currently set to "); if(o->txmixa==TX_OUT_COMPOSITE)ast_cli(fd,"composite.\n"); else if (o->txmixa==TX_OUT_VOICE)ast_cli(fd,"voice.\n"); else if (o->txmixa==TX_OUT_LSD)ast_cli(fd,"tone.\n"); else if (o->txmixa==TX_OUT_AUX)ast_cli(fd,"auxvoice.\n"); else ast_cli(fd,"off.\n"); ast_cli(fd,"Output B is currently set to "); if(o->txmixb==TX_OUT_COMPOSITE)ast_cli(fd,"composite.\n"); else if (o->txmixb==TX_OUT_VOICE)ast_cli(fd,"voice.\n"); else if (o->txmixb==TX_OUT_LSD)ast_cli(fd,"tone.\n"); else if (o->txmixb==TX_OUT_AUX)ast_cli(fd,"auxvoice.\n"); else ast_cli(fd,"off.\n"); ast_cli(fd,"Tx Voice Level currently set to %d\n",o->txmixaset); ast_cli(fd,"Tx Tone Level currently set to %d\n",o->txctcssadj); ast_cli(fd,"Rx Squelch currently set to %d\n",o->rxsquelchadj); ast_cli(fd,"Device String is %s\n",o->devstr); return RESULT_SHOWUSAGE; } o->pmrChan->b.tuning=1; if (!strcasecmp(argv[2],"rxnoise")) tune_rxinput(fd,o); else if (!strcasecmp(argv[2],"rxvoice")) tune_rxvoice(fd,o); else if (!strcasecmp(argv[2],"rxtone")) tune_rxctcss(fd,o); else if (!strcasecmp(argv[2],"rxsquelch")) { if (argc == 3) { ast_cli(fd,"Current Signal Strength is %d\n",((32767-o->pmrChan->rxRssi)*1000/32767)); ast_cli(fd,"Current Squelch setting is %d\n",o->rxsquelchadj); //ast_cli(fd,"Current Raw RSSI is %d\n",o->pmrChan->rxRssi); //ast_cli(fd,"Current (real) Squelch setting is %d\n",*(o->pmrChan->prxSquelchAdjust)); } else { i = atoi(argv[3]); if ((i < 0) || (i > 999)) return RESULT_SHOWUSAGE; ast_cli(fd,"Changed Squelch setting to %d\n",i); o->rxsquelchadj = i; *(o->pmrChan->prxSquelchAdjust)= ((999 - i) * 32767) / 1000; } } else if (!strcasecmp(argv[2],"txvoice")) { i = 0; if( (o->txmixa!=TX_OUT_VOICE) && (o->txmixb!=TX_OUT_VOICE) && (o->txmixa!=TX_OUT_COMPOSITE) && (o->txmixb!=TX_OUT_COMPOSITE) ) { ast_log(LOG_ERROR,"No txvoice output configured.\n"); } else if (argc == 3) { if((o->txmixa==TX_OUT_VOICE)||(o->txmixa==TX_OUT_COMPOSITE)) ast_cli(fd,"Current txvoice setting on Channel A is %d\n",o->txmixaset); else ast_cli(fd,"Current txvoice setting on Channel B is %d\n",o->txmixbset); } else { i = atoi(argv[3]); if ((i < 0) || (i > 999)) return RESULT_SHOWUSAGE; if((o->txmixa==TX_OUT_VOICE)||(o->txmixa==TX_OUT_COMPOSITE)) { o->txmixaset=i; ast_cli(fd,"Changed txvoice setting on Channel A to %d\n",o->txmixaset); } else { o->txmixbset=i; ast_cli(fd,"Changed txvoice setting on Channel B to %d\n",o->txmixbset); } mixer_write(o); mult_set(o); ast_cli(fd,"Changed Tx Voice Output setting to %d\n",i); } o->pmrChan->b.txCtcssInhibit=1; tune_txoutput(o,i,fd); o->pmrChan->b.txCtcssInhibit=0; } else if (!strcasecmp(argv[2],"txall")) { i = 0; if( (o->txmixa!=TX_OUT_VOICE) && (o->txmixb!=TX_OUT_VOICE) && (o->txmixa!=TX_OUT_COMPOSITE) && (o->txmixb!=TX_OUT_COMPOSITE) ) { ast_log(LOG_ERROR,"No txvoice output configured.\n"); } else if (argc == 3) { if((o->txmixa==TX_OUT_VOICE)||(o->txmixa==TX_OUT_COMPOSITE)) ast_cli(fd,"Current txvoice setting on Channel A is %d\n",o->txmixaset); else ast_cli(fd,"Current txvoice setting on Channel B is %d\n",o->txmixbset); } else { i = atoi(argv[3]); if ((i < 0) || (i > 999)) return RESULT_SHOWUSAGE; if((o->txmixa==TX_OUT_VOICE)||(o->txmixa==TX_OUT_COMPOSITE)) { o->txmixaset=i; ast_cli(fd,"Changed txvoice setting on Channel A to %d\n",o->txmixaset); } else { o->txmixbset=i; ast_cli(fd,"Changed txvoice setting on Channel B to %d\n",o->txmixbset); } mixer_write(o); mult_set(o); ast_cli(fd,"Changed Tx Voice Output setting to %d\n",i); } tune_txoutput(o,i,fd); } else if (!strcasecmp(argv[2],"auxvoice")) { i = 0; if( (o->txmixa!=TX_OUT_AUX) && (o->txmixb!=TX_OUT_AUX)) { ast_log(LOG_WARNING,"No auxvoice output configured.\n"); } else if (argc == 3) { if(o->txmixa==TX_OUT_AUX) ast_cli(fd,"Current auxvoice setting on Channel A is %d\n",o->txmixaset); else ast_cli(fd,"Current auxvoice setting on Channel B is %d\n",o->txmixbset); } else { i = atoi(argv[3]); if ((i < 0) || (i > 999)) return RESULT_SHOWUSAGE; if(o->txmixa==TX_OUT_AUX) { o->txmixbset=i; ast_cli(fd,"Changed auxvoice setting on Channel A to %d\n",o->txmixaset); } else { o->txmixbset=i; ast_cli(fd,"Changed auxvoice setting on Channel B to %d\n",o->txmixbset); } mixer_write(o); mult_set(o); } //tune_auxoutput(o,i); } else if (!strcasecmp(argv[2],"txtone")) { if (argc == 3) ast_cli(fd,"Current Tx CTCSS modulation setting = %d\n",o->txctcssadj); else { i = atoi(argv[3]); if ((i < 0) || (i > 999)) return RESULT_SHOWUSAGE; o->txctcssadj = i; set_txctcss_level(o); ast_cli(fd,"Changed Tx CTCSS modulation setting to %i\n",i); } o->txtestkey=1; usleep(5000000); o->txtestkey=0; } else if (!strcasecmp(argv[2],"dump")) pmrdump(o); else if (!strcasecmp(argv[2],"nocap")) { ast_cli(fd,"File capture (trace) was rx=%d tx=%d and now off.\n",o->b.rxcap2,o->b.txcap2); ast_cli(fd,"File capture (raw) was rx=%d tx=%d and now off.\n",o->b.rxcapraw,o->b.txcapraw); o->b.rxcapraw=o->b.txcapraw=o->b.rxcap2=o->b.txcap2=o->pmrChan->b.rxCapture=o->pmrChan->b.txCapture=0; if (frxcapraw) { fclose(frxcapraw); frxcapraw = NULL; } if (frxcaptrace) { fclose(frxcaptrace); frxcaptrace = NULL; } if (frxoutraw) { fclose(frxoutraw); frxoutraw = NULL; } if (ftxcapraw) { fclose(ftxcapraw); ftxcapraw = NULL; } if (ftxcaptrace) { fclose(ftxcaptrace); ftxcaptrace = NULL; } if (ftxoutraw) { fclose(ftxoutraw); ftxoutraw = NULL; } } else if (!strcasecmp(argv[2],"rxtracecap")) { if (!frxcaptrace) frxcaptrace= fopen(RX_CAP_TRACE_FILE,"w"); ast_cli(fd,"Trace rx on.\n"); o->b.rxcap2=o->pmrChan->b.rxCapture=1; } else if (!strcasecmp(argv[2],"txtracecap")) { if (!ftxcaptrace) ftxcaptrace= fopen(TX_CAP_TRACE_FILE,"w"); ast_cli(fd,"Trace tx on.\n"); o->b.txcap2=o->pmrChan->b.txCapture=1; } else if (!strcasecmp(argv[2],"rxcap")) { if (!frxcapraw) frxcapraw = fopen(RX_CAP_RAW_FILE,"w"); ast_cli(fd,"cap rx raw on.\n"); o->b.rxcapraw=1; } else if (!strcasecmp(argv[2],"txcap")) { if (!ftxcapraw) ftxcapraw = fopen(TX_CAP_RAW_FILE,"w"); ast_cli(fd,"cap tx raw on.\n"); o->b.txcapraw=1; } else if (!strcasecmp(argv[2],"save")) { tune_write(o); ast_cli(fd,"Saved radio tuning settings to usbradio_tune_%s.conf\n",o->name); } else if (!strcasecmp(argv[2],"load")) { ast_mutex_lock(&o->eepromlock); while(o->eepromctl) { ast_mutex_unlock(&o->eepromlock); usleep(10000); ast_mutex_lock(&o->eepromlock); } o->eepromctl = 1; /* request a load */ ast_mutex_unlock(&o->eepromlock); ast_cli(fd,"Requesting loading of tuning settings from EEPROM for channel %s\n",o->name); } else { o->pmrChan->b.tuning=0; return RESULT_SHOWUSAGE; } o->pmrChan->b.tuning=0; return RESULT_SUCCESS; } /* set transmit ctcss modulation level adjust mixer output or internal gain depending on output type setting range is 0.0 to 0.9 */ static int set_txctcss_level(struct chan_usbradio_pvt *o) { if (o->txmixa == TX_OUT_LSD) { // o->txmixaset=(151*o->txctcssadj) / 1000; o->txmixaset=o->txctcssadj; mixer_write(o); mult_set(o); } else if (o->txmixb == TX_OUT_LSD) { // o->txmixbset=(151*o->txctcssadj) / 1000; o->txmixbset=o->txctcssadj; mixer_write(o); mult_set(o); } else { *o->pmrChan->ptxCtcssAdjust=(o->txctcssadj * M_Q8) / 1000; } return 0; } /* CLI debugging on and off */ static int radio_set_debug(int fd, int argc, char *argv[]) { struct chan_usbradio_pvt *o = find_desc(usbradio_active); o->debuglevel=1; ast_cli(fd,"usbradio debug on.\n"); return RESULT_SUCCESS; } static int radio_set_debug_off(int fd, int argc, char *argv[]) { struct chan_usbradio_pvt *o = find_desc(usbradio_active); o->debuglevel=0; ast_cli(fd,"usbradio debug off.\n"); return RESULT_SUCCESS; } static int radio_active(int fd, int argc, char *argv[]) { if (argc == 2) ast_cli(fd, "active (command) USB Radio device is [%s]\n", usbradio_active); else if (argc != 3) return RESULT_SHOWUSAGE; else { struct chan_usbradio_pvt *o; if (strcmp(argv[2], "show") == 0) { for (o = usbradio_default.next; o; o = o->next) ast_cli(fd, "device [%s] exists\n", o->name); return RESULT_SUCCESS; } o = find_desc(argv[2]); if (o == NULL) ast_cli(fd, "No device [%s] exists\n", argv[2]); else { struct chan_usbradio_pvt *ao; for (ao = usbradio_default.next; ao && ao->name ; ao = ao->next)ao->pmrChan->b.radioactive=0; usbradio_active = o->name; o->pmrChan->b.radioactive=1; } } return RESULT_SUCCESS; } /* CLI debugging on and off */ static int radio_set_xpmr_debug(int fd, int argc, char *argv[]) { struct chan_usbradio_pvt *o = find_desc(usbradio_active); if (argc == 4) { int i; i = atoi(argv[3]); if ((i >= 0) && (i <= 100)) { o->pmrChan->tracelevel=i; } } // add ability to set it for a number of frames after which it reverts ast_cli(fd,"usbradio xdebug on tracelevel %i\n",o->pmrChan->tracelevel); return RESULT_SUCCESS; } static char key_usage[] = "Usage: radio key\n" " Simulates COR active.\n"; static char unkey_usage[] = "Usage: radio unkey\n" " Simulates COR un-active.\n"; static char active_usage[] = "Usage: radio active [device-name]\n" " If used without a parameter, displays which device is the current\n" "one being commanded. If a device is specified, the commanded radio device is changed\n" "to the device specified.\n"; /* radio tune 6 3000 measured tx value */ static char radio_tune_usage[] = "Usage: radio tune \n" " rxnoise\n" " rxvoice\n" " rxtone\n" " rxsquelch [newsetting]\n" " txvoice [newsetting]\n" " txtone [newsetting]\n" " auxvoice [newsetting]\n" " save (settings to tuning file)\n" " load (tuning settings from EEPROM)\n" "\n All [newsetting]'s are values 0-999\n\n"; #ifndef NEW_ASTERISK static struct ast_cli_entry cli_usbradio[] = { { { "radio", "key", NULL }, console_key, "Simulate Rx Signal Present", key_usage, NULL, NULL}, { { "radio", "unkey", NULL }, console_unkey, "Simulate Rx Signal Lusb", unkey_usage, NULL, NULL }, { { "radio", "tune", NULL }, radio_tune, "Radio Tune", radio_tune_usage, NULL, NULL }, { { "radio", "set", "debug", NULL }, radio_set_debug, "Radio Debug", radio_tune_usage, NULL, NULL }, { { "radio", "set", "debug", "off", NULL }, radio_set_debug_off, "Radio Debug", radio_tune_usage, NULL, NULL }, { { "radio", "active", NULL }, radio_active, "Change commanded device", active_usage, NULL, NULL }, { { "radio", "set", "xdebug", NULL }, radio_set_xpmr_debug, "Radio set xpmr debug level", active_usage, NULL, NULL }, }; #endif /* * store the callerid components */ #if 0 static void store_callerid(struct chan_usbradio_pvt *o, char *s) { ast_callerid_split(s, o->cid_name, sizeof(o->cid_name), o->cid_num, sizeof(o->cid_num)); } #endif static void store_rxdemod(struct chan_usbradio_pvt *o, const char *s) { if (!strcasecmp(s,"no")){ o->rxdemod = RX_AUDIO_NONE; } else if (!strcasecmp(s,"speaker")){ o->rxdemod = RX_AUDIO_SPEAKER; } else if (!strcasecmp(s,"flat")){ o->rxdemod = RX_AUDIO_FLAT; } else { ast_log(LOG_WARNING,"Unrecognized rxdemod parameter: %s\n",s); } //ast_log(LOG_WARNING, "set rxdemod = %s\n", s); } static void store_txmixa(struct chan_usbradio_pvt *o, const char *s) { if (!strcasecmp(s,"no")){ o->txmixa = TX_OUT_OFF; } else if (!strcasecmp(s,"voice")){ o->txmixa = TX_OUT_VOICE; } else if (!strcasecmp(s,"tone")){ o->txmixa = TX_OUT_LSD; } else if (!strcasecmp(s,"composite")){ o->txmixa = TX_OUT_COMPOSITE; } else if (!strcasecmp(s,"auxvoice")){ o->txmixa = TX_OUT_AUX; } else { ast_log(LOG_WARNING,"Unrecognized txmixa parameter: %s\n",s); } //ast_log(LOG_WARNING, "set txmixa = %s\n", s); } static void store_txmixb(struct chan_usbradio_pvt *o, const char *s) { if (!strcasecmp(s,"no")){ o->txmixb = TX_OUT_OFF; } else if (!strcasecmp(s,"voice")){ o->txmixb = TX_OUT_VOICE; } else if (!strcasecmp(s,"tone")){ o->txmixb = TX_OUT_LSD; } else if (!strcasecmp(s,"composite")){ o->txmixb = TX_OUT_COMPOSITE; } else if (!strcasecmp(s,"auxvoice")){ o->txmixb = TX_OUT_AUX; } else { ast_log(LOG_WARNING,"Unrecognized txmixb parameter: %s\n",s); } //ast_log(LOG_WARNING, "set txmixb = %s\n", s); } /* */ static void store_rxcdtype(struct chan_usbradio_pvt *o, const char *s) { if (!strcasecmp(s,"no")){ o->rxcdtype = CD_IGNORE; } else if (!strcasecmp(s,"usb")){ o->rxcdtype = CD_HID; } else if (!strcasecmp(s,"dsp")){ o->rxcdtype = CD_XPMR_NOISE; } else if (!strcasecmp(s,"vox")){ o->rxcdtype = CD_XPMR_VOX; } else if (!strcasecmp(s,"usbinvert")){ o->rxcdtype = CD_HID_INVERT; } else { ast_log(LOG_WARNING,"Unrecognized rxcdtype parameter: %s\n",s); } //ast_log(LOG_WARNING, "set rxcdtype = %s\n", s); } /* */ static void store_rxsdtype(struct chan_usbradio_pvt *o, const char *s) { if (!strcasecmp(s,"no") || !strcasecmp(s,"SD_IGNORE")){ o->rxsdtype = SD_IGNORE; } else if (!strcasecmp(s,"usb") || !strcasecmp(s,"SD_HID")){ o->rxsdtype = SD_HID; } else if (!strcasecmp(s,"usbinvert") || !strcasecmp(s,"SD_HID_INVERT")){ o->rxsdtype = SD_HID_INVERT; } else if (!strcasecmp(s,"software") || !strcasecmp(s,"SD_XPMR")){ o->rxsdtype = SD_XPMR; } else { ast_log(LOG_WARNING,"Unrecognized rxsdtype parameter: %s\n",s); } //ast_log(LOG_WARNING, "set rxsdtype = %s\n", s); } /* */ static void store_rxgain(struct chan_usbradio_pvt *o, const char *s) { float f; sscanf(s, "%30f", &f); o->rxgain = f; //ast_log(LOG_WARNING, "set rxgain = %f\n", f); } /* */ static void store_rxvoiceadj(struct chan_usbradio_pvt *o, const char *s) { float f; sscanf(s, "%30f", &f); o->rxvoiceadj = f; //ast_log(LOG_WARNING, "set rxvoiceadj = %f\n", f); } /* */ static void store_rxctcssadj(struct chan_usbradio_pvt *o, const char *s) { float f; sscanf(s, "%30f", &f); o->rxctcssadj = f; //ast_log(LOG_WARNING, "set rxctcssadj = %f\n", f); } /* */ static void store_txtoctype(struct chan_usbradio_pvt *o, const char *s) { if (!strcasecmp(s,"no") || !strcasecmp(s,"TOC_NONE")){ o->txtoctype = TOC_NONE; } else if (!strcasecmp(s,"phase") || !strcasecmp(s,"TOC_PHASE")){ o->txtoctype = TOC_PHASE; } else if (!strcasecmp(s,"notone") || !strcasecmp(s,"TOC_NOTONE")){ o->txtoctype = TOC_NOTONE; } else { ast_log(LOG_WARNING,"Unrecognized txtoctype parameter: %s\n",s); } } /* */ static void tune_txoutput(struct chan_usbradio_pvt *o, int value, int fd) { o->txtestkey=1; o->pmrChan->txPttIn=1; TxTestTone(o->pmrChan, 1); // generate 1KHz tone at 7200 peak if (fd > 0) ast_cli(fd,"Tone output starting on channel %s...\n",o->name); usleep(5000000); TxTestTone(o->pmrChan, 0); if (fd > 0) ast_cli(fd,"Tone output ending on channel %s...\n",o->name); o->pmrChan->txPttIn=0; o->txtestkey=0; } /* */ static void tune_rxinput(int fd, struct chan_usbradio_pvt *o) { const int target=23000; const int tolerance=2000; const int settingmin=1; const int settingstart=2; const int maxtries=12; float settingmax; int setting=0, tries=0, tmpdiscfactor, meas; int tunetype=0; settingmax = o->micmax; if(o->pmrChan->rxDemod)tunetype=1; o->pmrChan->b.tuning=1; setting = settingstart; ast_cli(fd,"tune rxnoise maxtries=%i, target=%i, tolerance=%i\n",maxtries,target,tolerance); while(triesdevicenum,MIXER_PARAM_MIC_CAPTURE_VOL,setting,0); setamixer(o->devicenum,MIXER_PARAM_MIC_BOOST,o->rxboostset,0); usleep(100000); if(o->rxcdtype!=CD_XPMR_NOISE || o->rxdemod==RX_AUDIO_SPEAKER) { // printf("Measure Direct Input\n"); o->pmrChan->spsMeasure->source = o->pmrChan->spsRx->source; o->pmrChan->spsMeasure->discfactor=2000; o->pmrChan->spsMeasure->enabled=1; o->pmrChan->spsMeasure->amax = o->pmrChan->spsMeasure->amin = 0; usleep(400000); meas=o->pmrChan->spsMeasure->apeak; o->pmrChan->spsMeasure->enabled=0; } else { // printf("Measure HF Noise\n"); tmpdiscfactor=o->pmrChan->spsRx->discfactor; o->pmrChan->spsRx->discfactor=(i16)2000; o->pmrChan->spsRx->discounteru=o->pmrChan->spsRx->discounterl=0; o->pmrChan->spsRx->amax=o->pmrChan->spsRx->amin=0; usleep(200000); meas=o->pmrChan->rxRssi; o->pmrChan->spsRx->discfactor=tmpdiscfactor; o->pmrChan->spsRx->discounteru=o->pmrChan->spsRx->discounterl=0; o->pmrChan->spsRx->amax=o->pmrChan->spsRx->amin=0; } if(!meas)meas++; ast_cli(fd,"tries=%i, setting=%i, meas=%i\n",tries,setting,meas); if( meas<(target-tolerance) || meas>(target+tolerance) || tries<3){ setting=setting*target/meas; } else if(tries>4 && meas>(target-tolerance) && meas<(target+tolerance) ) { break; } if(settingsettingmax)setting=settingmax; tries++; } ast_cli(fd,"DONE tries=%i, setting=%i, meas=%i\n",tries, (setting * 1000) / o->micmax,meas); if( meas<(target-tolerance) || meas>(target+tolerance) ){ ast_cli(fd,"ERROR: RX INPUT ADJUST FAILED.\n"); }else{ ast_cli(fd,"INFO: RX INPUT ADJUST SUCCESS.\n"); o->rxmixerset=(setting * 1000) / o->micmax; } o->pmrChan->b.tuning=0; } /* */ static void tune_rxvoice(int fd, struct chan_usbradio_pvt *o) { const int target=7200; // peak const int tolerance=360; // peak to peak const float settingmin=0.1; const float settingmax=4; const float settingstart=1; const int maxtries=12; float setting; int tries=0, meas; ast_cli(fd,"INFO: RX VOICE ADJUST START.\n"); ast_cli(fd,"target=%i tolerance=%i \n",target,tolerance); o->pmrChan->b.tuning=1; if(!o->pmrChan->spsMeasure) ast_cli(fd,"ERROR: NO MEASURE BLOCK.\n"); if(!o->pmrChan->spsMeasure->source || !o->pmrChan->prxVoiceAdjust ) ast_cli(fd,"ERROR: NO SOURCE OR MEASURE SETTING.\n"); o->pmrChan->spsMeasure->source=o->pmrChan->spsRxOut->sink; o->pmrChan->spsMeasure->enabled=1; o->pmrChan->spsMeasure->discfactor=1000; setting=settingstart; // ast_cli(fd,"ERROR: NO MEASURE BLOCK.\n"); while(triespmrChan->prxVoiceAdjust)=setting*M_Q8; usleep(10000); o->pmrChan->spsMeasure->amax = o->pmrChan->spsMeasure->amin = 0; usleep(1000000); meas = o->pmrChan->spsMeasure->apeak; ast_cli(fd,"tries=%i, setting=%f, meas=%i\n",tries,setting,meas); if( meas<(target-tolerance) || meas>(target+tolerance) || tries<3){ setting=setting*target/meas; } else if(tries>4 && meas>(target-tolerance) && meas<(target+tolerance) ) { break; } if(settingsettingmax)setting=settingmax; tries++; } o->pmrChan->spsMeasure->enabled=0; ast_cli(fd,"DONE tries=%i, setting=%f, meas=%f\n",tries,setting,(float)meas); if( meas<(target-tolerance) || meas>(target+tolerance) ){ ast_cli(fd,"ERROR: RX VOICE GAIN ADJUST FAILED.\n"); }else{ ast_cli(fd,"INFO: RX VOICE GAIN ADJUST SUCCESS.\n"); o->rxvoiceadj=setting; } o->pmrChan->b.tuning=0; } /* */ static void tune_rxctcss(int fd, struct chan_usbradio_pvt *o) { const int target=2400; // was 4096 pre 20080205 const int tolerance=100; const float settingmin=0.1; const float settingmax=8; const float settingstart=1; const int maxtries=12; float setting; int tries=0, meas; ast_cli(fd,"INFO: RX CTCSS ADJUST START.\n"); ast_cli(fd,"target=%i tolerance=%i \n",target,tolerance); o->pmrChan->b.tuning=1; o->pmrChan->spsMeasure->source=o->pmrChan->prxCtcssMeasure; o->pmrChan->spsMeasure->discfactor=400; o->pmrChan->spsMeasure->enabled=1; setting=settingstart; while(triespmrChan->prxCtcssAdjust)=setting*M_Q8; usleep(10000); o->pmrChan->spsMeasure->amax = o->pmrChan->spsMeasure->amin = 0; usleep(500000); meas = o->pmrChan->spsMeasure->apeak; ast_cli(fd,"tries=%i, setting=%f, meas=%i\n",tries,setting,meas); if( meas<(target-tolerance) || meas>(target+tolerance) || tries<3){ setting=setting*target/meas; } else if(tries>4 && meas>(target-tolerance) && meas<(target+tolerance) ) { break; } if(settingsettingmax)setting=settingmax; tries++; } o->pmrChan->spsMeasure->enabled=0; ast_cli(fd,"DONE tries=%i, setting=%f, meas=%f\n",tries,setting,(float)meas); if( meas<(target-tolerance) || meas>(target+tolerance) ){ ast_cli(fd,"ERROR: RX CTCSS GAIN ADJUST FAILED.\n"); }else{ ast_cli(fd,"INFO: RX CTCSS GAIN ADJUST SUCCESS.\n"); o->rxctcssadj=setting; } o->pmrChan->b.tuning=0; } /* after radio tune is performed data is serialized here */ static void tune_write(struct chan_usbradio_pvt *o) { FILE *fp; char fname[200]; snprintf(fname,sizeof(fname) - 1,"/etc/asterisk/usbradio_tune_%s.conf",o->name); fp = fopen(fname,"w"); fprintf(fp,"[%s]\n",o->name); fprintf(fp,"; name=%s\n",o->name); fprintf(fp,"; devicenum=%i\n",o->devicenum); fprintf(fp,"devstr=%s\n",o->devstr); fprintf(fp,"rxmixerset=%i\n",o->rxmixerset); fprintf(fp,"txmixaset=%i\n",o->txmixaset); fprintf(fp,"txmixbset=%i\n",o->txmixbset); fprintf(fp,"rxvoiceadj=%f\n",o->rxvoiceadj); fprintf(fp,"rxctcssadj=%f\n",o->rxctcssadj); fprintf(fp,"txctcssadj=%i\n",o->txctcssadj); fprintf(fp,"rxsquelchadj=%i\n",o->rxsquelchadj); fclose(fp); if(o->wanteeprom) { ast_mutex_lock(&o->eepromlock); while(o->eepromctl) { ast_mutex_unlock(&o->eepromlock); usleep(10000); ast_mutex_lock(&o->eepromlock); } o->eeprom[EEPROM_RXMIXERSET] = o->rxmixerset; o->eeprom[EEPROM_TXMIXASET] = o->txmixaset; o->eeprom[EEPROM_TXMIXBSET] = o->txmixbset; memcpy(&o->eeprom[EEPROM_RXVOICEADJ],&o->rxvoiceadj,sizeof(float)); memcpy(&o->eeprom[EEPROM_RXCTCSSADJ],&o->rxctcssadj,sizeof(float)); o->eeprom[EEPROM_TXCTCSSADJ] = o->txctcssadj; o->eeprom[EEPROM_RXSQUELCHADJ] = o->rxsquelchadj; o->eepromctl = 2; /* request a write */ ast_mutex_unlock(&o->eepromlock); } } // static void mixer_write(struct chan_usbradio_pvt *o) { setamixer(o->devicenum,MIXER_PARAM_MIC_PLAYBACK_SW,0,0); setamixer(o->devicenum,MIXER_PARAM_MIC_PLAYBACK_VOL,0,0); setamixer(o->devicenum,MIXER_PARAM_SPKR_PLAYBACK_SW,1,0); setamixer(o->devicenum,MIXER_PARAM_SPKR_PLAYBACK_VOL, o->txmixaset * o->spkrmax / 1000, o->txmixbset * o->spkrmax / 1000); setamixer(o->devicenum,MIXER_PARAM_MIC_CAPTURE_VOL, o->rxmixerset * o->micmax / 1000,0); setamixer(o->devicenum,MIXER_PARAM_MIC_BOOST,o->rxboostset,0); setamixer(o->devicenum,MIXER_PARAM_MIC_CAPTURE_SW,1,0); } /* adjust dsp multiplier to add resolution to tx level adjustment */ static void mult_set(struct chan_usbradio_pvt *o) { if(o->pmrChan->spsTxOutA) { o->pmrChan->spsTxOutA->outputGain = mult_calc((o->txmixaset * 152) / 1000); } if(o->pmrChan->spsTxOutB){ o->pmrChan->spsTxOutB->outputGain = mult_calc((o->txmixbset * 152) / 1000); } } // // input 0 - 151 outputs are pot and multiplier // static int mult_calc(int value) { const int multx=M_Q8; int pot,mult; pot=((int)(value/4)*4)+2; mult = multx-( ( multx * (3-(value%4)) ) / (pot+2) ); return(mult); } #define pd(x) {printf(#x" = %d\n",x);} #define pp(x) {printf(#x" = %p\n",x);} #define ps(x) {printf(#x" = %s\n",x);} #define pf(x) {printf(#x" = %f\n",x);} #if 0 /* do hid output if only requirement is ptt out this give fastest performance with least overhead where gpio inputs are not required. */ static int usbhider(struct chan_usbradio_pvt *o, int opt) { unsigned char buf[4]; char lastrx, txtmp; if(opt) { struct usb_device *usb_dev; usb_dev = hid_device_init(o->devstr); if (usb_dev == NULL) { ast_log(LOG_ERROR,"USB HID device not found\n"); return -1; } o->usb_handle = usb_open(usb_dev); if (o->usb_handle == NULL) { ast_log(LOG_ERROR,"Not able to open USB device\n"); return -1; } if (usb_claim_interface(o->usb_handle,C108_HID_INTERFACE) < 0) { if (usb_detach_kernel_driver_np(o->usb_handle,C108_HID_INTERFACE) < 0) { ast_log(LOG_ERROR,"Not able to detach the USB device\n"); return -1; } if (usb_claim_interface(o->usb_handle,C108_HID_INTERFACE) < 0) { ast_log(LOG_ERROR,"Not able to claim the USB device\n"); return -1; } } memset(buf,0,sizeof(buf)); buf[2] = o->hid_gpio_ctl; buf[1] = 0; hid_set_outputs(o->usb_handle,buf); memcpy(bufsave,buf,sizeof(buf)); buf[o->hid_gpio_ctl_loc] = o->hid_gpio_ctl; o->lasttx=0; } /* if change in tx state as controlled by xpmr */ txtmp=o->pmrChan->txPttOut; if (o->lasttx != txtmp) { o->pmrChan->txPttHid=o->lasttx = txtmp; if(o->debuglevel)printf("usbhid: tx set to %d\n",txtmp); buf[o->hid_gpio_loc] = 0; if (!o->invertptt) { if (txtmp) buf[o->hid_gpio_loc] = o->hid_io_ptt; } else { if (!txtmp) buf[o->hid_gpio_loc] = o->hid_io_ptt; } buf[o->hid_gpio_ctl_loc] = o->hid_gpio_ctl; hid_set_outputs(o->usb_handle,buf); } return(0); } #endif /* */ static void pmrdump(struct chan_usbradio_pvt *o) { t_pmr_chan *p; int i; p=o->pmrChan; printf("\nodump()\n"); pd(o->devicenum); ps(o->devstr); pd(o->micmax); pd(o->spkrmax); pd(o->rxdemod); pd(o->rxcdtype); pd(o->rxsdtype); pd(o->txtoctype); pd(o->rxmixerset); pd(o->rxboostset); pf(o->rxvoiceadj); pf(o->rxctcssadj); pd(o->rxsquelchadj); ps(o->txctcssdefault); ps(o->txctcssfreq); pd(o->numrxctcssfreqs); if(o->numrxctcssfreqs>0) { for(i=0;inumrxctcssfreqs;i++) { printf(" %i = %s %s\n",i,o->rxctcss[i],o->txctcss[i]); } } pd(o->b.rxpolarity); pd(o->b.txpolarity); pd(o->txprelim); pd(o->txmixa); pd(o->txmixb); pd(o->txmixaset); pd(o->txmixbset); printf("\npmrdump()\n"); pd(p->devicenum); printf("prxSquelchAdjust=%i\n",*(o->pmrChan->prxSquelchAdjust)); pd(p->rxCarrierPoint); pd(p->rxCarrierHyst); pd(*p->prxVoiceAdjust); pd(*p->prxCtcssAdjust); pd(p->rxfreq); pd(p->txfreq); pd(p->rxCtcss->relax); //pf(p->rxCtcssFreq); pd(p->numrxcodes); if(o->pmrChan->numrxcodes>0) { for(i=0;ipmrChan->numrxcodes;i++) { printf(" %i = %s\n",i,o->pmrChan->pRxCode[i]); } } pd(p->txTocType); ps(p->pTxCodeDefault); pd(p->txcodedefaultsmode); pd(p->numtxcodes); if(o->pmrChan->numtxcodes>0) { for(i=0;ipmrChan->numtxcodes;i++) { printf(" %i = %s\n",i,o->pmrChan->pTxCode[i]); } } pd(p->b.rxpolarity); pd(p->b.txpolarity); pd(p->b.dcsrxpolarity); pd(p->b.dcstxpolarity); pd(p->b.lsdrxpolarity); pd(p->b.lsdtxpolarity); pd(p->txMixA); pd(p->txMixB); pd(p->rxDeEmpEnable); pd(p->rxCenterSlicerEnable); pd(p->rxCtcssDecodeEnable); pd(p->rxDcsDecodeEnable); pd(p->b.ctcssRxEnable); pd(p->b.dcsRxEnable); pd(p->b.lmrRxEnable); pd(p->b.dstRxEnable); pd(p->smode); pd(p->txHpfEnable); pd(p->txLimiterEnable); pd(p->txPreEmpEnable); pd(p->txLpfEnable); if(p->spsTxOutA)pd(p->spsTxOutA->outputGain); if(p->spsTxOutB)pd(p->spsTxOutB->outputGain); pd(p->txPttIn); pd(p->txPttOut); pd(p->tracetype); return; } /* takes data from a chan_usbradio_pvt struct (e.g. o->) and configures the xpmr radio layer */ static int xpmr_config(struct chan_usbradio_pvt *o) { //ast_log(LOG_NOTICE,"xpmr_config()\n"); TRACEO(1,("xpmr_config()\n")); if(o->pmrChan==NULL) { ast_log(LOG_ERROR,"pmr channel structure NULL\n"); return 1; } o->pmrChan->rxCtcss->relax = o->rxctcssrelax; o->pmrChan->txpower=0; if(o->b.remoted) { o->pmrChan->pTxCodeDefault = o->set_txctcssdefault; o->pmrChan->pRxCodeSrc=o->set_rxctcssfreqs; o->pmrChan->pTxCodeSrc=o->set_txctcssfreqs; o->pmrChan->rxfreq=o->set_rxfreq; o->pmrChan->txfreq=o->set_txfreq; /* printf(" remoted %s %s --> %s \n",o->pmrChan->txctcssdefault, o->pmrChan->txctcssfreq,o->pmrChan->rxctcssfreq); */ } else { // set xpmr pointers to source strings o->pmrChan->pTxCodeDefault = o->txctcssdefault; o->pmrChan->pRxCodeSrc = o->rxctcssfreqs; o->pmrChan->pTxCodeSrc = o->txctcssfreqs; o->pmrChan->rxfreq = o->rxfreq; o->pmrChan->txfreq = o->txfreq; } code_string_parse(o->pmrChan); if(o->pmrChan->rxfreq) o->pmrChan->b.reprog=1; return 0; } /* * grab fields from the config file, init the descriptor and open the device. */ static struct chan_usbradio_pvt *store_config(struct ast_config *cfg, char *ctg) { struct ast_variable *v; struct chan_usbradio_pvt *o; struct ast_config *cfg1; int i; char fname[200]; #ifdef NEW_ASTERISK struct ast_flags zeroflag = {0}; #endif if (ctg == NULL) { traceusb1((" store_config() ctg == NULL\n")); o = &usbradio_default; ctg = "general"; } else { /* "general" is also the default thing */ if (strcmp(ctg, "general") == 0) { o = &usbradio_default; } else { // ast_log(LOG_NOTICE,"ast_calloc for chan_usbradio_pvt of %s\n",ctg); if (!(o = ast_calloc(1, sizeof(*o)))) return NULL; *o = usbradio_default; o->name = ast_strdup(ctg); if (!usbradio_active) usbradio_active = o->name; } } ast_mutex_init(&o->eepromlock); strcpy(o->mohinterpret, "default"); /* fill other fields from configuration */ for (v = ast_variable_browse(cfg, ctg); v; v = v->next) { M_START((char *)v->name, (char *)v->value); /* handle jb conf */ if (!ast_jb_read_conf(&global_jbconf, v->name, v->value)) continue; #if 0 M_BOOL("autoanswer", o->autoanswer) M_BOOL("autohangup", o->autohangup) M_BOOL("overridecontext", o->overridecontext) M_STR("context", o->ctx) M_STR("language", o->language) M_STR("mohinterpret", o->mohinterpret) M_STR("extension", o->ext) M_F("callerid", store_callerid(o, v->value)) #endif M_UINT("frags", o->frags) M_UINT("queuesize",o->queuesize) #if 0 M_UINT("devicenum",o->devicenum) #endif M_UINT("debug", usbradio_debug) M_BOOL("rxcpusaver",o->rxcpusaver) M_BOOL("txcpusaver",o->txcpusaver) M_BOOL("invertptt",o->invertptt) M_F("rxdemod",store_rxdemod(o,(char *)v->value)) M_BOOL("txprelim",o->txprelim); M_F("txmixa",store_txmixa(o,(char *)v->value)) M_F("txmixb",store_txmixb(o,(char *)v->value)) M_F("carrierfrom",store_rxcdtype(o,(char *)v->value)) M_F("rxsdtype",store_rxsdtype(o,(char *)v->value)) M_UINT("rxsqvox",o->rxsqvoxadj) M_STR("txctcssdefault",o->txctcssdefault) M_STR("rxctcssfreqs",o->rxctcssfreqs) M_STR("txctcssfreqs",o->txctcssfreqs) M_UINT("rxfreq",o->rxfreq) M_UINT("txfreq",o->txfreq) M_F("rxgain",store_rxgain(o,(char *)v->value)) M_BOOL("rxboost",o->rxboostset) M_UINT("rxctcssrelax",o->rxctcssrelax) M_F("txtoctype",store_txtoctype(o,(char *)v->value)) M_UINT("hdwtype",o->hdwtype) M_UINT("eeprom",o->wanteeprom) M_UINT("duplex",o->radioduplex) M_UINT("txsettletime",o->txsettletime) M_BOOL("rxpolarity",o->b.rxpolarity) M_BOOL("txpolarity",o->b.txpolarity) M_BOOL("dcsrxpolarity",o->b.dcsrxpolarity) M_BOOL("dcstxpolarity",o->b.dcstxpolarity) M_BOOL("lsdrxpolarity",o->b.lsdrxpolarity) M_BOOL("lsdtxpolarity",o->b.lsdtxpolarity) M_BOOL("loopback",o->b.loopback) M_BOOL("radioactive",o->b.radioactive) M_UINT("rptnum",o->rptnum) M_UINT("idleinterval",o->idleinterval) M_UINT("turnoffs",o->turnoffs) M_UINT("tracetype",o->tracetype) M_UINT("tracelevel",o->tracelevel) M_UINT("area",o->area) M_STR("ukey",o->ukey) M_END(; ); } o->debuglevel=0; if (o == &usbradio_default) /* we are done with the default */ return NULL; snprintf(fname,sizeof(fname) - 1,config1,o->name); #ifdef NEW_ASTERISK cfg1 = ast_config_load(fname,zeroflag); #else cfg1 = ast_config_load(fname); #endif o->rxmixerset = 500; o->txmixaset = 500; o->txmixbset = 500; o->rxvoiceadj = 0.5; o->rxctcssadj = 0.5; o->txctcssadj = 200; o->rxsquelchadj = 500; o->devstr[0] = 0; if (cfg1 && cfg1 != CONFIG_STATUS_FILEINVALID) { for (v = ast_variable_browse(cfg1, o->name); v; v = v->next) { M_START((char *)v->name, (char *)v->value); M_UINT("rxmixerset", o->rxmixerset) M_UINT("txmixaset", o->txmixaset) M_UINT("txmixbset", o->txmixbset) M_F("rxvoiceadj",store_rxvoiceadj(o,(char *)v->value)) M_F("rxctcssadj",store_rxctcssadj(o,(char *)v->value)) M_UINT("txctcssadj",o->txctcssadj); M_UINT("rxsquelchadj", o->rxsquelchadj) M_STR("devstr", o->devstr) M_END(; ); } ast_config_destroy(cfg1); } else ast_log(LOG_WARNING,"File %s not found, using default parameters.\n",fname); if(o->wanteeprom) { ast_mutex_lock(&o->eepromlock); while(o->eepromctl) { ast_mutex_unlock(&o->eepromlock); usleep(10000); ast_mutex_lock(&o->eepromlock); } o->eepromctl = 1; /* request a load */ ast_mutex_unlock(&o->eepromlock); } /* if our specified one exists in the list */ if ((!usb_list_check(o->devstr)) || find_desc_usb(o->devstr)) { char *s; for(s = usb_device_list; *s; s += strlen(s) + 1) { if (!find_desc_usb(s)) break; } if (!*s) { ast_log(LOG_WARNING,"Unable to assign USB device for channel %s\n",o->name); goto error; } ast_log(LOG_NOTICE,"Assigned USB device %s to usbradio channel %s\n",s,o->name); strcpy(o->devstr,s); } i = usb_get_usbdev(o->devstr); if (i < 0) { ast_log(LOG_ERROR,"Not able to find alsa USB device\n"); goto error; } o->devicenum = i; o->micmax = amixer_max(o->devicenum,MIXER_PARAM_MIC_CAPTURE_VOL); o->spkrmax = amixer_max(o->devicenum,MIXER_PARAM_SPKR_PLAYBACK_VOL); o->lastopen = ast_tvnow(); /* don't leave it 0 or tvdiff may wrap */ o->dsp = ast_dsp_new(); if (o->dsp) { #ifdef NEW_ASTERISK ast_dsp_set_features(o->dsp,DSP_FEATURE_DIGIT_DETECT); ast_dsp_set_digitmode(o->dsp,DSP_DIGITMODE_DTMF | DSP_DIGITMODE_MUTECONF | DSP_DIGITMODE_RELAXDTMF); #else ast_dsp_set_features(o->dsp,DSP_FEATURE_DTMF_DETECT); ast_dsp_digitmode(o->dsp,DSP_DIGITMODE_DTMF | DSP_DIGITMODE_MUTECONF | DSP_DIGITMODE_RELAXDTMF); #endif } if(o->pmrChan==NULL) { t_pmr_chan tChan; // ast_log(LOG_NOTICE,"createPmrChannel() %s\n",o->name); memset(&tChan,0,sizeof(t_pmr_chan)); tChan.pTxCodeDefault = o->txctcssdefault; tChan.pRxCodeSrc = o->rxctcssfreqs; tChan.pTxCodeSrc = o->txctcssfreqs; tChan.rxDemod=o->rxdemod; tChan.rxCdType=o->rxcdtype; tChan.rxSqVoxAdj=o->rxsqvoxadj; if (o->txprelim) tChan.txMod = 2; tChan.txMixA = o->txmixa; tChan.txMixB = o->txmixb; tChan.rxCpuSaver=o->rxcpusaver; tChan.txCpuSaver=o->txcpusaver; tChan.b.rxpolarity=o->b.rxpolarity; tChan.b.txpolarity=o->b.txpolarity; tChan.b.dcsrxpolarity=o->b.dcsrxpolarity; tChan.b.dcstxpolarity=o->b.dcstxpolarity; tChan.b.lsdrxpolarity=o->b.lsdrxpolarity; tChan.b.lsdtxpolarity=o->b.lsdtxpolarity; tChan.tracetype=o->tracetype; tChan.tracelevel=o->tracelevel; tChan.rptnum=o->rptnum; tChan.idleinterval=o->idleinterval; tChan.turnoffs=o->turnoffs; tChan.area=o->area; tChan.ukey=o->ukey; tChan.name=o->name; o->pmrChan=createPmrChannel(&tChan,FRAME_SIZE); o->pmrChan->radioDuplex=o->radioduplex; o->pmrChan->b.loopback=0; o->pmrChan->txsettletime=o->txsettletime; o->pmrChan->rxCpuSaver=o->rxcpusaver; o->pmrChan->txCpuSaver=o->txcpusaver; *(o->pmrChan->prxSquelchAdjust) = ((999 - o->rxsquelchadj) * 32767) / 1000; *(o->pmrChan->prxVoiceAdjust)=o->rxvoiceadj*M_Q8; *(o->pmrChan->prxCtcssAdjust)=o->rxctcssadj*M_Q8; o->pmrChan->rxCtcss->relax=o->rxctcssrelax; o->pmrChan->txTocType = o->txtoctype; if ( (o->txmixa == TX_OUT_LSD) || (o->txmixa == TX_OUT_COMPOSITE) || (o->txmixb == TX_OUT_LSD) || (o->txmixb == TX_OUT_COMPOSITE)) { set_txctcss_level(o); } if( (o->txmixa!=TX_OUT_VOICE) && (o->txmixb!=TX_OUT_VOICE) && (o->txmixa!=TX_OUT_COMPOSITE) && (o->txmixb!=TX_OUT_COMPOSITE) ) { ast_log(LOG_ERROR,"No txvoice output configured.\n"); } if( o->txctcssfreq[0] && o->txmixa!=TX_OUT_LSD && o->txmixa!=TX_OUT_COMPOSITE && o->txmixb!=TX_OUT_LSD && o->txmixb!=TX_OUT_COMPOSITE ) { ast_log(LOG_ERROR,"No txtone output configured.\n"); } if(o->b.radioactive) { // 20080328 sphenke asdf maw !!! // this diagnostic option was working but now appears broken // it's not required for operation so I'll fix it later. //struct chan_usbradio_pvt *ao; //for (ao = usbradio_default.next; ao && ao->name ; ao = ao->next)ao->pmrChan->b.radioactive=0; usbradio_active = o->name; // o->pmrChan->b.radioactive=1; //o->b.radioactive=0; //o->pmrChan->b.radioactive=0; ast_log(LOG_NOTICE,"radio active set to [%s]\n",o->name); } } xpmr_config(o); TRACEO(1,("store_config() 120\n")); mixer_write(o); TRACEO(1,("store_config() 130\n")); mult_set(o); TRACEO(1,("store_config() 140\n")); hidhdwconfig(o); TRACEO(1,("store_config() 200\n")); #ifndef NEW_ASTERISK if (pipe(o->sndcmd) != 0) { ast_log(LOG_ERROR, "Unable to create pipe\n"); goto error; } ast_pthread_create_background(&o->sthread, NULL, sound_thread, o); #endif /* link into list of devices */ if (o != &usbradio_default) { o->next = usbradio_default.next; usbradio_default.next = o; } TRACEO(1,("store_config() complete\n")); return o; error: if (o != &usbradio_default) free(o); return NULL; } #if DEBUG_FILETEST == 1 /* Test It on a File */ int RxTestIt(struct chan_usbradio_pvt *o) { const int numSamples = SAMPLES_PER_BLOCK; const int numChannels = 16; i16 sample,i,ii; i32 txHangTime; i16 txEnable; t_pmr_chan tChan; t_pmr_chan *pChan; FILE *hInput=NULL, *hOutput=NULL, *hOutputTx=NULL; i16 iBuff[numSamples*2*6], oBuff[numSamples]; printf("RxTestIt()\n"); pChan=o->pmrChan; pChan->b.txCapture=1; pChan->b.rxCapture=1; txEnable = 0; hInput = fopen("/usr/src/xpmr/testdata/rx_in.pcm","r"); if(!hInput){ printf(" RxTestIt() File Not Found.\n"); return 0; } hOutput = fopen("/usr/src/xpmr/testdata/rx_debug.pcm","w"); printf(" RxTestIt() Working...\n"); while(!feof(hInput)) { fread((void *)iBuff,2,numSamples*2*6,hInput); if(txHangTime)txHangTime-=numSamples; if(txHangTime<0)txHangTime=0; if(pChan->rxCtcss->decode)txHangTime=(8000/1000*2000); if(pChan->rxCtcss->decode && !txEnable) { txEnable=1; //pChan->inputBlanking=(8000/1000*200); } else if(!pChan->rxCtcss->decode && txEnable) { txEnable=0; } PmrRx(pChan,iBuff,oBuff); if (fwrite((void *)pChan->prxDebug,2,numSamples*numChannels,hOutput) != numSamples * numChannels) { ast_log(LOG_ERROR, "fwrite() failed: %s\n", strerror(errno)); } } pChan->b.txCapture=0; pChan->b.rxCapture=0; if(hInput)fclose(hInput); if(hOutput)fclose(hOutput); printf(" RxTestIt() Complete.\n"); return 0; } #endif #ifdef NEW_ASTERISK static char *res2cli(int r) { switch (r) { case RESULT_SUCCESS: return(CLI_SUCCESS); case RESULT_SHOWUSAGE: return(CLI_SHOWUSAGE); default: return(CLI_FAILURE); } } static char *handle_console_key(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { char *argv[] = { "radio", "key", NULL }; switch (cmd) { case CLI_INIT: e->command = "radio key"; e->usage = key_usage; return NULL; case CLI_GENERATE: return NULL; } return res2cli(console_key(a->fd, 2, argv)); } static char *handle_console_unkey(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { char *argv[] = { "radio", "unkey", NULL }; switch (cmd) { case CLI_INIT: e->command = "radio unkey"; e->usage = unkey_usage; return NULL; case CLI_GENERATE: return NULL; } return res2cli(console_unkey(a->fd, 2, argv)); } static char *handle_radio_tune(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { char *argv[5] = { "radio", "tune", a->argc > 2 ? (char *) a->argv[2] : NULL, a->argc > 3 ? (char *) a->argv[3] : NULL }; switch (cmd) { case CLI_INIT: e->command = "radio tune"; e->usage = radio_tune_usage; return NULL; case CLI_GENERATE: return NULL; } return res2cli(radio_tune(a->fd, a->argc, argv)); } static char *handle_radio_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { switch (cmd) { case CLI_INIT: e->command = "radio debug"; e->usage = radio_tune_usage; return NULL; case CLI_GENERATE: return NULL; } return res2cli(radio_set_debug(a->fd, a->argc, NULL /* ignored */)); } static char *handle_radio_debug_off(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { switch (cmd) { case CLI_INIT: e->command = "radio debug off"; e->usage = radio_tune_usage; return NULL; case CLI_GENERATE: return NULL; } return res2cli(radio_set_debug_off(a->fd, a->argc, NULL /* ignored */)); } static char *handle_radio_active(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { char *argv[4] = { "radio", "active", a->argc > 2 ? (char *) a->argv[2] : NULL, }; switch (cmd) { case CLI_INIT: e->command = "radio active"; e->usage = active_usage; return NULL; case CLI_GENERATE: return NULL; } return res2cli(radio_active(a->fd, a->argc, argv)); } static char *handle_set_xdebug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { char *argv[5] = { "radio", "set", "xdebug", a->argc == 4 ? (char *) a->argv[3] : NULL, }; switch (cmd) { case CLI_INIT: e->command = "radio set xdebug"; e->usage = active_usage; return NULL; case CLI_GENERATE: return NULL; } return res2cli(radio_set_xpmr_debug(a->fd, a->argc, argv)); } static struct ast_cli_entry cli_usbradio[] = { AST_CLI_DEFINE(handle_console_key,"Simulate Rx Signal Present"), AST_CLI_DEFINE(handle_console_unkey,"Simulate Rx Signal Loss"), AST_CLI_DEFINE(handle_radio_tune,"Radio Tune"), AST_CLI_DEFINE(handle_radio_debug,"Radio Debug On"), AST_CLI_DEFINE(handle_radio_debug_off,"Radio Debug Off"), AST_CLI_DEFINE(handle_radio_active,"Change commanded device"), AST_CLI_DEFINE(handle_set_xdebug,"Radio set xpmr debug level") }; #endif #include "./xpmr/xpmr.c" #ifdef HAVE_XPMRX #include "./xpmrx/xpmrx.c" #endif /* */ static int load_module(void) { struct ast_config *cfg = NULL; char *ctg = NULL; #ifdef NEW_ASTERISK struct ast_flags zeroflag = {0}; #endif if (hid_device_mklist()) { ast_log(LOG_NOTICE, "Unable to make hid list\n"); return AST_MODULE_LOAD_DECLINE; } usb_list_check(""); usbradio_active = NULL; /* Copy the default jb config over global_jbconf */ memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf)); /* load config file */ #ifdef NEW_ASTERISK if (!(cfg = ast_config_load(config,zeroflag)) || cfg == CONFIG_STATUS_FILEINVALID) { #else if (!(cfg = ast_config_load(config))) || cfg == CONFIG_STATUS_FILEINVALID { #endif ast_log(LOG_NOTICE, "Unable to load config %s\n", config); return AST_MODULE_LOAD_DECLINE; } do { store_config(cfg, ctg); } while ( (ctg = ast_category_browse(cfg, ctg)) != NULL); ast_config_destroy(cfg); if (find_desc(usbradio_active) == NULL) { ast_log(LOG_NOTICE, "radio active device %s not found\n", usbradio_active); /* XXX we could default to 'dsp' perhaps ? */ /* XXX should cleanup allocated memory etc. */ return AST_MODULE_LOAD_DECLINE; } if (ast_channel_register(&usbradio_tech)) { ast_log(LOG_ERROR, "Unable to register channel type 'usb'\n"); return AST_MODULE_LOAD_DECLINE; } ast_cli_register_multiple(cli_usbradio, ARRAY_LEN(cli_usbradio)); return AST_MODULE_LOAD_SUCCESS; } /* */ static int unload_module(void) { struct chan_usbradio_pvt *o; ast_log(LOG_WARNING, "unload_module() called\n"); ast_channel_unregister(&usbradio_tech); ast_cli_unregister_multiple(cli_usbradio, ARRAY_LEN(cli_usbradio)); for (o = usbradio_default.next; o; o = o->next) { ast_log(LOG_WARNING, "destroyPmrChannel() called\n"); if(o->pmrChan)destroyPmrChannel(o->pmrChan); #if DEBUG_CAPTURES == 1 if (frxcapraw) { fclose(frxcapraw); frxcapraw = NULL; } if (frxcaptrace) { fclose(frxcaptrace); frxcaptrace = NULL; } if (frxoutraw) { fclose(frxoutraw); frxoutraw = NULL; } if (ftxcapraw) { fclose(ftxcapraw); ftxcapraw = NULL; } if (ftxcaptrace) { fclose(ftxcaptrace); ftxcaptrace = NULL; } if (ftxoutraw) { fclose(ftxoutraw); ftxoutraw = NULL; } #endif close(o->sounddev); #ifndef NEW_ASTERISK if (o->sndcmd[0] > 0) { close(o->sndcmd[0]); close(o->sndcmd[1]); } #endif if (o->dsp) ast_dsp_free(o->dsp); if (o->owner) ast_softhangup(o->owner, AST_SOFTHANGUP_APPUNLOAD); if (o->owner) /* XXX how ??? */ return -1; /* XXX what about the thread ? */ /* XXX what about the memory allocated ? */ } return 0; } AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "usb Console Channel Driver"); /* end of file */