Newer
Older
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 1999 - 2005, Digium, Inc.
* Copyright (C) 2007 - 2008, Jim Dixon
*
* Jim Dixon, WB6NIL <jim@lambdatel.com>
* Steve Henke, W9SH <w9sh@arrl.net>
* Based upon work by Mark Spencer <markster@digium.com> and Luigi Rizzo
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*! \file
*
* \brief Channel driver for CM108 USB Cards with Radio Interface
*
* \author Jim Dixon <jim@lambdatel.com>
* \author Steve Henke <w9sh@arrl.net>
*
* \par See also
* \arg \ref Config_usbradio
*
* \ingroup channel_drivers
*/
/*** MODULEINFO
Kevin P. Fleming
committed
<depend>oss</depend>
<depend>usb</depend>
***/
/*** MAKEOPTS
Tilghman Lesher
committed
<category name="MENUSELECT_CFLAGS" displayname="Compiler Flags" positive_output="yes">
<member name="RADIO_RTX" displayname="Build RTX/DTX Radio Programming" touch_on_change="channels/chan_usbradio.c channels/xpmr/xpmr.h">
<defaultenabled>no</defaultenabled>
<depend>chan_usbradio</depend>
</member>
Tilghman Lesher
committed
<member name="RADIO_XPMRX" displayname="Build Experimental Radio Protocols" touch_on_change="channels/chan_usbradio.c">
<defaultenabled>no</defaultenabled>
<depend>chan_usbradio</depend>
</member>
</category>
// 20070918 1600 EDT sph@xelatec.com changing to rx driven streams
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <sys/time.h>
#include <stdlib.h>
#include <errno.h>
#include <usb.h>
#include <alsa/asoundlib.h>
//#define HAVE_XPMRX 1
#ifdef RADIO_XPMRX
#define HAVE_XPMRX 1
#endif
#define 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
#ifdef HAVE_XPMRX
#include "./xpmrx/xpmrx.h"
#include "./xpmrx/bitweight.h"
#endif
#define traceusb1(a) {printf a;}
#define traceusb2(a) {printf a;}
#endif
#ifdef __linux
#include <linux/soundcard.h>
#elif defined(__FreeBSD__)
#include <sys/soundcard.h>
#else
#include <soundcard.h>
#endif
#include "asterisk/lock.h"
#include "asterisk/frame.h"
#include "asterisk/logger.h"
#include "asterisk/callerid.h"
#include "asterisk/channel.h"
#include "asterisk/module.h"
#include "asterisk/options.h"
#include "asterisk/pbx.h"
#include "asterisk/config.h"
#include "asterisk/cli.h"
#include "asterisk/utils.h"
#include "asterisk/causes.h"
#include "asterisk/endian.h"
#include "asterisk/stringfields.h"
#include "asterisk/abstract_jb.h"
#include "asterisk/musiconhold.h"
#include "asterisk/dsp.h"
#ifndef NEW_ASTERISK
/* ringtones we use */
#include "busy.h"
#include "ringtone.h"
#include "ring10.h"
#include "answer.h"
#endif
#define C108_VENDOR_ID 0x0d8c
#define C108_PRODUCT_ID 0x000c
#define C108_HID_INTERFACE 3
#define HID_REPORT_GET 0x01
#define HID_REPORT_SET 0x09
#define HID_RT_INPUT 0x01
#define HID_RT_OUTPUT 0x02
#define EEPROM_START_ADDR 6
#define EEPROM_END_ADDR 63
#define EEPROM_PHYSICAL_LEN 64
#define EEPROM_TEST_ADDR EEPROM_END_ADDR
#define EEPROM_MAGIC_ADDR 6
#define EEPROM_MAGIC 34329
#define EEPROM_CS_ADDR 62
#define EEPROM_RXMIXERSET 8
#define EEPROM_TXMIXASET 9
#define EEPROM_TXMIXBSET 10
#define EEPROM_RXVOICEADJ 11
#define EEPROM_RXCTCSSADJ 13
#define EEPROM_TXCTCSSADJ 15
#define EEPROM_RXSQUELCHADJ 16
/*! Global jitterbuffer configuration - by default, jb is disabled */
static struct ast_jb_conf default_jbconf =
{
.flags = 0,
.max_size = -1,
.resync_threshold = -1,
.impl = "",
};
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
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
; 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
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
/*
* 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)))
/*
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
* 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
#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_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 */
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
/*
* 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 */
int stophid;
FILE *hkickhid;
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
*/
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
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;
char rxcpusaver;
char txcpusaver;
char rxdemod;
float rxgain;
char rxcdtype;
char rxsdtype;
int rxsquelchadj; /* this copy needs to be here for initialization */
int rxsqvoxadj;
char txtoctype;
char txprelim;
float txctcssgain;
char txmixa;
char txmixb;
char rxctcssrelax;
float rxctcssgain;
char txctcssdefault[16]; // for repeater operation
char rxctcssfreqs[512]; // a string
char txctcssfreqs[512];
char txctcssfreq[32]; // encode now
char rxctcssfreq[32]; // decode now
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
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 hid_gpio_ctl;
int hid_gpio_ctl_loc;
int hid_io_cor;
int hid_io_cor_loc;
int hid_io_ctcss;
int hid_io_ctcss_loc;
int hid_io_ptt;
int hid_gpio_loc;
unsigned 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);
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
#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_ctl_elem_id_alloca(&id);
snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER);
snd_ctl_elem_id_set_name(id, param);
elem = snd_hctl_find_elem(hctl, id);
snd_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_ctl_elem_id_alloca(&id);
snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER);
snd_ctl_elem_id_set_name(id, param);
elem = snd_hctl_find_elem(hctl, id);
snd_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);
switch(type)
{
case SND_CTL_ELEM_TYPE_INTEGER:
snd_ctl_elem_value_set_integer(control, 0, v1);
if (v2 > 0) snd_ctl_elem_value_set_integer(control, 1, v2);
break;
case SND_CTL_ELEM_TYPE_BOOLEAN:
snd_ctl_elem_value_set_integer(control, 0, (v1 != 0));
break;
}
if (snd_hctl_elem_write(elem, control))
{
snd_hctl_close(hctl);
return(-1);
}
snd_hctl_close(hctl);
}
static void hid_set_outputs(struct usb_dev_handle *handle,
unsigned char *outputs)
{
USB_ENDPOINT_OUT + USB_TYPE_CLASS + USB_RECIP_INTERFACE,
HID_REPORT_SET,
0 + (HID_RT_OUTPUT << 8),
C108_HID_INTERFACE,
(char*)outputs, 4, 5000);
}
static void hid_get_inputs(struct usb_dev_handle *handle,
unsigned char *inputs)
{
USB_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)
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
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);
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
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 +