// // File: // // Written by: David M. Stanhope [voip@fobbit.com] // // UPDATES: // 1> fixed where ocasionally exited because got network packets where the // size was not an exact multiple of the voice-packet size, changed // to buffer across network reads // #include "vblast.h" // --------------------------------------------------------------------------- // this gets sent when starting up the device, it describes what to do // with 'init_2' static u_char init_1[] = { 0x3b,0x00,0x40,0x8b // first 2 bytes is length, second 2 bytes is command? }; static u_char init_2[] = { 0x00,0x01,0x00,0x00,0x18,0x02,0x8f,0x00, 0x10,0x00,0x28,0x40,0x03,0x1a,0x0d,0x0c, 0xfa,0x43,0xfd,0xea,0x93,0xfe,0x1a,0x41, 0x00,0x4a,0x93,0xfe,0x2a,0x40,0x00,0x1a, 0x93,0xfe,0x3a,0x0a,0x00,0x1f,0x3c,0x00, 0x8c,0x0d,0x03,0xa3,0x23,0xa2,0xdf,0x0d, 0x0c,0x3a,0x40,0x00,0x2a,0x93,0xfe,0x4a, 0x0a,0x00,0x0f }; // this gets sent each time the voice output goes idle static u_char init_3[] = { 0x75,0x58,0x9b,0x04,0x72,0x00,0x00,0x11, 0xe0,0x00,0x65,0x82,0x00,0x90,0x00,0x1c, 0x96,0xc1,0x0f,0xf2,0x3d,0x95,0x8e,0x5e, 0xe7,0x66,0xef,0xd4,0xba,0x21,0x0d,0x30, 0xcb,0x1e,0x52,0x35,0x9a,0xb6,0xff,0x7f, 0x74,0x58,0x9b,0x04,0x68,0x08,0x00,0x99, 0x52,0xfa,0x75,0xd7,0x72,0xba,0xdb,0x03, 0x3d,0xdb,0x77,0xd0,0x77,0x03,0x1f,0x05 }; // some silence data, for now only used in 'vblast_ring' to kill the dialtone static u_char silence[] = { 0xd5,0x14,0x0a,0x0d,0x1a,0x00,0x00,0x0a, 0x80,0x00,0xaf,0x06,0x00,0x00,0xfc,0x24, 0x9b,0x70,0xcf,0xc0,0xa1,0x53,0x18,0x00, 0x08,0x04,0xfe,0x50,0x8f,0x12,0x50,0x50, 0x4b,0x32,0x56,0x77,0x15,0xe9,0x0f,0xe8, 0xd5,0xc8,0xad,0x68,0xc8,0x74,0xe4,0x27, 0x71,0xd8,0x87,0x34,0xad,0x60,0x07,0xa8, 0xb6,0x64,0x0f,0x44,0x59,0x02,0xf4,0x00, 0x0e,0x80,0xba,0x68,0x73,0x00,0xe0,0xdf, 0x82,0xf4,0xa3,0x1f,0x18,0xb3,0x70,0x2e, 0xdd,0x1c,0xb1,0x00,0x02,0xfc,0x07,0xde, 0x09,0xc2,0x5e,0x82,0xaf,0xbc,0x5a,0x6c, 0xa2,0x02,0xbb,0xac,0x65,0x47,0x99,0x74, 0xfe,0xf4,0xe5,0xf7,0x70,0x90,0xe7,0x21, 0x15,0x33,0x56,0x8a,0x77,0x50,0xb5,0x42, 0x51,0x17,0xf2,0x4c,0xa8,0xfc,0x10,0x60, 0x82,0x1e,0x50,0x2f,0xa4,0x57,0x31,0x1e, 0xdd,0xc0,0xaf,0x6f,0x4d,0x1f,0xf5,0x08, 0x18,0x90,0x15,0x7f,0x70,0x1e,0x60,0x3e, 0x15,0xfb,0xa0,0x0d,0xbd,0x60,0x90,0x6d, 0x65,0x43,0xb1,0xdc,0xc2,0xd5,0x19,0xe5, 0xe1,0xe8,0x27,0xe4,0xcf,0x82,0x39,0xec, 0x38,0x84,0x3b,0x75,0x51,0x3b,0xb1,0x70, 0xde,0x68,0xba,0x06,0x79,0x0f,0x76,0x18, 0x74,0x53,0x45,0x4f,0x83,0x52,0xa0,0xa0, 0x5d,0xc1,0xed,0xc4,0x9f,0x77,0x27,0x36, 0x83,0xf9,0x07,0x7f,0x28,0xf3,0x41,0x41, 0x85,0x94,0x15,0xc0,0x45,0x13,0xee,0x30, 0x54,0x8c,0xea,0x37,0x03,0xf8,0xae,0x1f, 0x85,0xe7,0xed,0x21,0x58,0x01,0xff,0xe5, 0x75,0xc3,0x09,0x05,0x12,0x00,0x26,0xa7, 0x0b,0x26,0x9f,0x81,0x26,0x50,0x06,0x97, 0x31,0xa4,0x3c,0xca,0x1d,0x97,0xc2,0x9d, 0x42,0x6d,0xcf,0xae,0xe6,0xe5,0x27,0x04, 0x03,0xc2,0x93,0x5d,0xa7,0x62,0xf1,0x87, 0x5d,0x41,0xe5,0x0a,0x0a,0x70,0x69,0x3f, 0xf1,0xf4,0xfe,0x0d,0x4e,0x27,0x97,0x5b, 0xb0,0x0b,0xe5,0x78,0xa5,0x26,0x09,0x49, 0x90,0xfc,0xf1,0x77,0x52,0x0c,0xdf,0xef, 0x7f,0x8d,0x84,0x15,0x48,0xc7,0xff,0x5b, }; // --------------------------------------------------------------------------- // size of the serial number packet, this packet only set once on first // read of the voice-pipe after a device power-up #define ID_PACKET_SIZE (17) #define ID_STRING_LEN ((ID_PACKET_SIZE * 3) - 1) static char id_file_name [16]; static char device_id_string [80]; char serial_number_string[80]; // read stored device-id from file if have it static void vblast_load_device_id(int device_index) { FILE *fp; int i; #ifdef IS_VBWIN strcpy(id_file_name, "vb.id"); // more consistant if leave out index here #else sprintf(id_file_name, "vb%d.id", device_index); #endif device_id_string[0] = '\0'; if((fp = fopen(id_file_name, "r")) != NULL) { if(fgets(device_id_string, 79, fp) == NULL) { ERR(("Error reading <%s>\n", id_file_name)) fclose(fp); exit(-1); } i = strlen(device_id_string); while(i > 0) { i--; if((device_id_string[i] == '\r') || (device_id_string[i] == '\n')) { device_id_string[i] = '\0'; } } fclose(fp); if(strlen(device_id_string) != ID_STRING_LEN) { ERR(("Corrupt <%s> file\n", id_file_name)) exit(-1); } // TODO: call 'vblast_validate_id', but with no messages displayed? // but need to convert to binary to do so! MSG(("Device-Id(%s)\n", device_id_string)) } } static int vblast_validate_id(u_char *buf) { // check for correct software version // first 10 bytes seem to hold version information, in devices // I have only 2 bytes change with version // last 7 bytes seem to hold a device serial number, with the first of // these being the dallas 2401 family code 0x01. // in the devices I have, only 3 bytes of the serial number are different // (lsb) 0x??, 0x??, 0x??, 0x06, 0x00, 0x00 (msb) // can not find relationship with this number, number printed on // device and number displayed by original software. // suspect last three bytes are high bytes of serial-number? // firmware gets updated first time device plugged in with // the shipped windows driver if((buf[ 0] != 0x00) || (buf[ 1] != 0x8d) || (buf[ 2] != 0x0f) || (buf[ 3] != 0x80) || (buf[ 4] != 0x52) || // buf[ 5] checked below (buf[ 6] != 0xdb) || (buf[ 7] != 0x00) || // buf[ 8] checked below (buf[ 9] != 0x08) || (buf[10] != 0x01)) // dallas 2401 family code? { return -1; // bad } // might be able to accept shipped version, seems to work if((buf[ 5] == 0xd0) && (buf[ 8] == 0xc9)) { MSG(("Original Firmware Detected\n")) return 0; // ok, is shipped firmware version of voip-blaster } if((buf[ 5] == 0xcc) && (buf[ 8] == 0x91)) { MSG(("Updated Firmware Detected\n")) return 0; // ok, is updated firmware version of voip-blaster } if((buf[ 5] == 0xd0) && (buf[ 8] == 0xca)) { MSG(("Infoaccel USB Firmware Detected\n")) return 0; // ok, is Infoaccel USB } return -1; // bad } static void vblast_process_device_id(u_char *buf, int n) { FILE *fp; int i; char id_buf[80]; if(n != ID_PACKET_SIZE) { ERR(("vlbast_process_device_id: id-packet size <%d> not <%d>\n", n, ID_PACKET_SIZE)) exit(-1); } // build ID string for display id_buf[0] = '\0'; for(i = 0; i < ID_PACKET_SIZE; i++) { sprintf(id_buf + strlen(id_buf), (i == 0) ? "%02x" : ":%02x", buf[i]); } // check for correct software version if(vblast_validate_id(buf) != 0) { ERR(("Unknown Firmware Version in Hardware Device-Id:\n" " (%s)\n" " To upgrade the firmware, first install the device with\n" " the windows driver from the Creative CD, then re-install\n" " the Fobbit driver. If this doesn't fix the problem, send\n" " email with this message to voip@fobbit.com.\n", id_buf)) exit(-1); } // see if what just got matches what we have saved in the id file, // have to use the id file since only get to read the serial-number // if the this is the first access after the device was power-cycled. if(strcmp(device_id_string, id_buf) != 0) { if(strlen(device_id_string) == ID_STRING_LEN) // saved before? { ERR(("Device-Id stored in (%s) and hardware don't match\n" " File : (%s)\n" " Hardware: (%s)\n" " If have replaced the device and wish to update\n" " this file, delete the file, un-plug the device USB" " cable for a few seconds, then try again.\n", id_file_name, device_id_string, id_buf)) exit(-1); } else // first time { MSG(("Init (%s) to (%s)\n", id_file_name, id_buf)) } strcpy(device_id_string, id_buf); if((fp = fopen(id_file_name, "w")) == NULL) { ERR(("Can't open <%s> file for writing, error(%s)\n", id_file_name, Show_Error())) exit(-1); } fprintf(fp, "%s\n", id_buf); fclose(fp); } else { MSG(("Device-Id verified\n")) } } // --------------------------------------------------------------------------- // // in unix 'vblast_writer' and 'vblast_socket_writer' are really the // same routine, but keep separate so can also run under 'windows' where // must be different // #ifndef CUSTOM_WRITER static int vblast_writer(char *msg, HANDLE fd, u_char *bp, int n) { int r; dump(msg, bp, n); if((r = File_Write(fd, bp, n)) != n) { if(r < 0) { ERR(("%s: r(%d) error(%s)\n", msg, r, Show_Error())) } else { ERR(("%s: r(%d)\n", msg, r)) } return -1; } return n; } // // send a one byte command down the command usb-pipe // void vblast_command(VBLAST *p, int value) { u_char buf[1]; buf[0] = value; if(vblast_writer("command.out", p->fd_command, buf, 1) < 0) { exit(-1); } } // // send some voice data down the voice usb-pipe // void vblast_voice_write(VBLAST *p, u_char *pt, int n) { if(vblast_writer("voice...out", p->fd_voice_out, pt, n) < 0) { exit(-1); } } #endif // CUSTOM_WRITER int vblast_socket_writer(char *msg, SOCKET fd, u_char *bp, int n) { int r, err; dump(msg, bp, n); set_timeout(); if((r = Socket_Write(fd, bp, n)) != n) { clr_timeout(); if(r < 0) { err = Socket_Errno(); ERR(("%s: r(%d) error(%s)\n", msg, r, Socket_Error_String(err))) if(Socket_Would_Block(err)) { return 0; } } else { ERR(("%s: r(%d) != (%d)\n", msg, r, n)) } return -1; } clr_timeout(); return n; } // // under windows this only reads sockets since the usb-pipes are copied // to sockets, but under unix it will be reading sockets and the usb-pipes // directly. // static int vblast_socket_reader(char *msg, SOCKET fd, u_char *bp, int len) { int n; set_timeout(); if((n = Socket_Read(fd, bp, len)) < 0) { clr_timeout(); ERR(("%s: error(%s)\n", msg, Socket_Error())) return -1; } clr_timeout(); if(n == 0) { ERR(("%s: empty-read\n", msg)) return -1; } dump(msg, bp, n); return n; } // // enque bytes read from the status usb-pipe, but processes and drops // on-hook/off-hook status bytes // static void vblast_status_enque(VBLAST *v, int c) { MSG1(("status(%02x)\n", c)) if(c == STATUS_HOOK_ON) { v->status_hook = STATUS_HOOK_ON; } else if(c == STATUS_HOOK_OFF) { v->status_hook = STATUS_HOOK_OFF; } else if(c == STATUS_HEADSET_IN) { v->status_headset = STATUS_HEADSET_IN; } else if(c == STATUS_HEADSET_OUT) { v->status_headset = STATUS_HEADSET_OUT; } else { MSG1(("Status enqueue(%02x)\n", c)) v->sq_buffer[v->sq_head++] = c; if(v->sq_head >= SQ_SIZE) { v->sq_head = 0; } if(v->sq_head == v->sq_tail) { ERR(("status_queue overflow\n")) exit(-1); } } } // // if available get a status-byte off the status-queue // int vblast_status_deque(VBLAST *p) { int c; if(p->sq_head != p->sq_tail) { c = p->sq_buffer[p->sq_tail++]; if(p->sq_tail >= SQ_SIZE) { p->sq_tail = 0; } return c; } return -1; } // // enque a voice-packet on the appropriate queue, first gets an entry from // the free-list, copies the packet to it, and then enques it. if the packet // is from the network will already have its type set, but if from the // usb-device, add the type to it. // static void vblast_vp_enque(VBLAST *v, int type, u_char *bp, MSG_QUEUE *qp) { VOICE_ENTRY *ep, *tp; if(v->free_list) { ep = v->free_list; v->free_list = ep->next; } else { if((ep = (VOICE_ENTRY *) malloc(sizeof(VOICE_ENTRY))) == NULL) { ERR(("vblast_vp_enque: malloc failed\n")) exit(-1); } } if(type) { memcpy(&(ep->data[0]), bp, NP_SIZE); // from network } else { ep->data[0] = MAGIC_VOICE; // from voip blaster, flag as voice data memcpy(&(ep->data[1]), bp, VP_SIZE); } ep->next = NULL; if((tp = qp->tail) == NULL) // is empty { qp->head = ep; } else { tp->next = ep; } qp->tail = ep; qp->count++; } u_char * vblast_vp_deque(VBLAST *v, MSG_QUEUE *qp) { VOICE_ENTRY *ep; static u_char tbuf[NP_SIZE]; if((ep = qp->head) == NULL) { return NULL; } // is empty memcpy(tbuf, ep->data, NP_SIZE); // copy type and data // remove from the queue if((qp->head = ep->next) == NULL) // is now empty { qp->tail = NULL; } // add then entry to the free-list ep->next = v->free_list; v->free_list = ep; qp->count--; return tbuf; // return the type and data } void vblast_vp_reset(MSG_QUEUE *qp) { qp->head = NULL; qp->tail = NULL; qp->count = 0; } void vblast_vp_flush(VBLAST *v) { while(vblast_deque_voice(v)) { ; } vblast_reset_voice(v); while(vblast_deque_net (v)) { ; } vblast_reset_net (v); v->net_over_count = 0; v->net_buffered = 0; v->net_dropped = 0; v->net_blocked = 0; v->net_received_tcp = 0; v->net_received_udp = 0; v->net_sent_tcp = 0; v->net_sent_udp = 0; } // // wait for some input, up to the specified number of seconds, in any case // will return once there is some input, if finds input will read it, process // it and enque it as needed. returns 0 if no data ready, or the number of // devices on which data was found. // int vblast_wait_input(VBLAST *v, int seconds) { int i, r, n; SOCKET fd_max; fd_set imask; struct timeval timer; u_char *bp, tbuf[TN_LEN]; // bigger of TV_LEN and TN_LEN #ifdef ALLOW_KEYBOARD int c; #endif FD_ZERO(&imask); fd_max = 0; v->have_call = 0; if(v->fd_accept != INVALID_SOCKET) { FD_SET(v->fd_accept , &imask); } if(v->fd_peer_tcp != INVALID_SOCKET) { FD_SET(v->fd_peer_tcp , &imask); } if(v->fd_peer_udp != INVALID_SOCKET) { FD_SET(v->fd_peer_udp , &imask); } #ifdef ALLOW_KEYBOARD if(v->fd_keyboard != INVALID_SOCKET) { FD_SET(v->fd_keyboard , &imask); } #endif FD_SET(v->fd_status , &imask); FD_SET(v->fd_voice_inp, &imask); if(v->fd_accept > fd_max) { fd_max = v->fd_accept ; } if(v->fd_peer_tcp > fd_max) { fd_max = v->fd_peer_tcp ; } if(v->fd_peer_udp > fd_max) { fd_max = v->fd_peer_udp ; } #ifdef ALLOW_KEYBOARD if(v->fd_keyboard > fd_max) { fd_max = v->fd_keyboard ; } #endif if(v->fd_status > fd_max) { fd_max = v->fd_status ; } if(v->fd_voice_inp > fd_max) { fd_max = v->fd_voice_inp; } timer.tv_sec = seconds; timer.tv_usec = 0; r = select(fd_max + 1, &imask, NULL, NULL, &timer); if(r < 0) { ERR(("select: r(%d) error(%s)\n", r, Socket_Error())) exit(-1); } if(r == 0) return 0; if(FD_ISSET(v->fd_status, &imask)) // status { if((n = vblast_socket_reader("status-read", v->fd_status, tbuf, TV_LEN)) < 0) { exit(-1); } for(i = 0; i < n; i++) { vblast_status_enque(v, tbuf[i]); } } if(FD_ISSET(v->fd_voice_inp, &imask)) // voice { if((n = vblast_socket_reader("voice-read", v->fd_voice_inp, tbuf, TV_LEN)) < 0) { exit(-1); } if(v->startup) { vblast_process_device_id(tbuf, n); } else { if((n % VP_SIZE) != 0) // DIAGNOSTIC { ERR(("vblast_wait_input: V ((n = %d) %% %d) != 0\n", n, VP_SIZE)) exit(-1); } for(i = 0; i < n; i += VP_SIZE) { vblast_enque_voice(v, tbuf + i); } } } if(v->fd_peer_tcp != INVALID_SOCKET) // are we connected { if(FD_ISSET(v->fd_peer_tcp, &imask)) { // network delays may cause max size packets, which may not // be an exact multiple of NP_SIZE, so may need to buffer if((n = vblast_socket_reader("peer_tcp_read", v->fd_peer_tcp, v->net_buffer + v->net_buffered, TN_LEN - v->net_buffered)) < 0) { Socket_Close(v->fd_peer_tcp) ; // close connection v->fd_peer_tcp = INVALID_SOCKET; // so won't check again v->net_buffered = 0 ; // flush the buffer } else { n += v->net_buffered; // total in the buffer bp = v->net_buffer ; // start of the buffer while(n > 0) { if(*bp == MAGIC_VOICE) { if(n < NP_SIZE) { break; } // not enough data yet vblast_enque_net(v, bp); v->net_received_tcp++; bp += NP_SIZE; n -= NP_SIZE; } else if(*bp == MAGIC_CNTRL) { if(n < 4) { break; } // 3 byte header + EOS i = ((bp[1] << 8) | (bp[2])); // length of message if(n < i) { break; } // not enough data yet if(bp[i - 1] != '\0') { ERR(("vblast_wait_input: bogus control msg\n")) exit(-1); } process_remote_cmd(v, bp + 3); bp += i; n -= i; } else { ERR(("vblast_wait_input: bad net type (%x)\n", *bp)) exit(-1); } } if(n < 0) // DIAGNOSTIC { ERR(("vblast_wait_input: N (%d) bad size, NP_SIZE (%d)\n", n, NP_SIZE)) exit(-1); } if((n > 0) && (bp != v->net_buffer)) // anything left { memcpy(v->net_buffer, bp, n); // move leftover to start } v->net_buffered = n; // save residual count } } } if(v->fd_peer_udp != INVALID_SOCKET) // are we connected { if(FD_ISSET(v->fd_peer_udp, &imask)) { if((n = vblast_socket_reader("peer_udp_read", v->fd_peer_udp, tbuf, TN_LEN)) < 0) { Socket_Close(v->fd_peer_udp) ; // close connection v->fd_peer_udp = INVALID_SOCKET; // so won't check again } else if(tbuf[0] == MAGIC_VOICE) { if(n != NP_SIZE) { ERR(("vblast_wait_input: bad udp voice msg len(%d)\n", n)) exit(-1); } vblast_enque_net(v, tbuf); v->net_received_udp++; } else if(tbuf[0] == MAGIC_CNTRL) { if(n < 4) // 3 byte header + EOS { ERR(("vblast_wait_input: short udp control msg\n")) exit(-1); } i = ((tbuf[1] << 8) | (tbuf[2])); // length of message if(n != i) // not right amount of data { ERR(("vblast_wait_input: bogus udp control msg len\n")) exit(-1); } if(tbuf[i - 1] != '\0') { ERR(("vblast_wait_input: bogus udp control msg\n")) exit(-1); } process_remote_cmd(v, tbuf + 3); } else { ERR(("vblast_wait_input: bad udp-net type (%x)\n",tbuf[0])) exit(-1); } } } #ifdef ALLOW_KEYBOARD if(v->fd_keyboard != INVALID_SOCKET) { if(FD_ISSET(v->fd_keyboard, &imask)) // any keyboard characters { if((n = vblast_socket_reader("keyboard-read", v->fd_keyboard, tbuf, TN_LEN)) < 0) { exit(-1); } MSG1(("KEYBOARD - GOT(%d)\n", n)) for(i = 0; i < n; i++) { c = tbuf[i]; if((c == 3) || (c == 0x1b)) // CTRL-C or ESCAPE { if(c == 3) { MSG(("Got CONTROL-C\n")) } else { MSG(("Got ESCAPE\n")) } keyboard_close(v); exit(0); // FIXME: better cleanup? } else if((c == 'O') || (c == 'o')) // HEADSET-OFFHOOK { if(v->status_headset == STATUS_HEADSET_IN) { if(v->status_hook == STATUS_HOOK_ON) { MSG(("Headset OFFHOOK\n")) vblast_command(v, COMMAND_HS_OFFHOOK); } else { ERR(("Already OFFHOOK\n")); } } else { ERR(("headset not connected\n")); } } else if((c == 'H') || (c == 'h')) // HEADSET-ONHOOK { if(v->status_headset == STATUS_HEADSET_IN) { if(v->status_hook == STATUS_HOOK_OFF) { MSG(("Headset ONHOOK\n")) vblast_command(v, COMMAND_HS_ONHOOK); vblast_command(v, COMMAND_RING_OFF); } else { ERR(("Already ONHOOK\n")); } } else { ERR(("headset not connected\n")); } } else if((c == '+') || (c == '=')) // CHANGE VOLUME - UP { if(device_volume < 6) { device_volume++; vblast_command(v, COMMAND_VOL_0 + device_volume); } MSG(("Volume level: %d\n", device_volume)) } else if((c == '-') || (c == '_')) // CHANGE VOLUME - DOWN { if(device_volume > 0) { device_volume--; vblast_command(v, COMMAND_VOL_0 + device_volume); } MSG(("Volume level: %d\n", device_volume)) } else if((c == 'M') || (c == 'm')) // MUTE ON { MSG(("MUTE-ON\n")) vblast_command(v, COMMAND_MUTE_ON); } else if((c == 'U') || (c == 'u')) // MUTE OFF (Un-Mute) { MSG(("MUTE-OFF\n")) vblast_command(v, COMMAND_MUTE_OFF); } else if((c == '*') || (c == '#') || ((c >= '0') && (c <= '9'))) { vblast_status_enque(v, c); } else if(c == '.') { vblast_status_enque(v, '*'); // easier to enter ip address } else if((c == '\r') || (c == '\n')) { vblast_status_enque(v, '#'); // make easier to dial } else { MSG(("O : HeadSet Off-Hook\n" "H : HeadSet On-Hook\n" "M : Mute-On\n" "U : Mute-Off\n" "+ : Volume-Up\n" "- : Volume-Down\n" "? : Show This Help\n")) } } } } #endif if(v->fd_accept != INVALID_SOCKET) { if(FD_ISSET(v->fd_accept, &imask)) // any connections { v->have_call = 1; } } return r; } void vblast_poll(VBLAST *v, int nflag) { if(nflag) { nap(5); } while(vblast_wait_input(v, 0) > 0) { ; } } void vblast_flush(VBLAST *v) { vblast_poll(v, 1); while(vblast_status_deque(v) >= 0) { ; } // keep queue empty } int vblast_send_and_wait(VBLAST *p, int command, int response, int timeout) { int c; time_t t = RIGHT_NOW + timeout; vblast_command(p, command); // send the command while(RIGHT_NOW < t) { vblast_wait_input(p, 1); while((c = vblast_status_deque(p)) >= 0) { if(c == response) return c; ERR(("vblast_send_and_wait(%x,%x,%d): received(%x)\n", command, response, timeout, c)) } } return -1; } void vblast_shutdown_voice(VBLAST *p) { // send command to wait for voice play done vblast_send_and_wait(p, COMMAND_VOUT_DONE, STATUS_VOUT_DONE, 5); vblast_voice_write(p, init_3, sizeof(init_3)); // send setup string } VBLAST * vblast_open(int device_index) { U64 serial_number; int i, c; char *cp; VBLAST *v; vblast_load_device_id(device_index); // ----------------------------------------------------------------------- v = vblast_device_open(device_index); v->fd_accept = INVALID_SOCKET; // not open yet v->fd_peer_tcp = INVALID_SOCKET; // not open yet v->fd_peer_udp = INVALID_SOCKET; // not open yet #ifdef ALLOW_KEYBOARD v->fd_keyboard = INVALID_SOCKET; // not open yet #endif v->startup = 1; // in startup mode v->status_hook = -1; // not known yet v->status_headset = -1; // not known yet v->have_call = 0; // no call request v->free_list = NULL; // empty v->net_over_count = 0; // clear v->net_dropped = 0; // clear v->net_blocked = 0; // clear v->net_buffered = 0; // empty v->net_received_tcp = 0; // clear v->net_received_udp = 0; // clear v->net_sent_tcp = 0; // clear v->net_sent_udp = 0; // clear v->sq_head = 0; // empty v->sq_tail = 0; // empty vblast_reset_voice(v); // empty vblast_reset_net (v); // empty // ----------------------------------------------------------------------- // FIXME: this is a HACK, really want to determine it but can't as of yet v->status_hook = STATUS_HOOK_ON ; v->status_headset = STATUS_HEADSET_OUT; // will see if power-cycled // ----------------------------------------------------------------------- vblast_flush(v); // flush any input MSG(("plugin\n")) // put in setup mode and send setup strings, first string is // setup command (4 bytes) of which first 2 bytes are length // 2nd string is the setup data // if first time since device plugged in, will get firmware version // and device serial number in 17 bytes on voice channel right after // sending COMAND_SETUP_MODE, also if first time since device pluged // in, will get 0x0a on status channel after sending both init strings // won't get either if not first time since plugged in vblast_command(v, COMMAND_HS_ONHOOK); // just incase was left offhook, // seems to hang if so vblast_command (v, COMMAND_SETUP_MODE ); vblast_flush(v); vblast_voice_write(v, init_1, sizeof(init_1)); vblast_flush(v); vblast_voice_write(v, init_2, sizeof(init_2)); vblast_flush(v); // ----------------------------------------------------------------------- MSG(("startup\n")) for(i = 0; i < 2; i++) // may need to do twice to get the response { vblast_command(v, COMMAND_VOL_3 ); vblast_command(v, COMMAND_RING_OFF); vblast_command(v, COMMAND_PHONE_ON); while(1) { vblast_poll(v, 1); if((c = vblast_status_deque(v)) < 0) break; if(c == STATUS_RINGING_OFF) break; } if(c == STATUS_RINGING_OFF) break; if(i != 0) // too many tries? { if(i == 0) continue; // try again ERR(("startup: didn't get STATUS_RINGING_OFF\n")) exit(-1); } } vblast_command(v, COMMAND_VOL_3 ); vblast_command(v, COMMAND_MUTE_OFF); // ----------------------------------------------------------------------- v->startup = 0; // not in startup mode anymore // ----------------------------------------------------------------------- if(strlen(device_id_string) != ID_STRING_LEN) { ERR(("startup: no valid device-id string found, un-plug the device\n" " USB cable for a few seconds, then try again!\n")) exit(-1); } // ----------------------------------------------------------------------- cp = device_id_string + ID_STRING_LEN - 2; serial_number = get_hex2(cp); cp -= 3; serial_number <<= 8; serial_number |= get_hex2(cp); cp -= 3; serial_number <<= 8; serial_number |= get_hex2(cp); cp -= 3; serial_number <<= 8; serial_number |= get_hex2(cp); cp -= 3; serial_number <<= 8; serial_number |= get_hex2(cp); cp -= 3; serial_number <<= 8; serial_number |= get_hex2(cp); sprintf(serial_number_string, U64_FMT, serial_number); MSG(("Serial-Number(%s)\n", serial_number_string)) // ----------------------------------------------------------------------- MSG(("ready\n")) // ----------------------------------------------------------------------- return v; } void vblast_close(VBLAST *v) { vblast_command(v, COMMAND_VINP_STOP); vblast_shutdown_voice(v); vblast_flush(v); sleep(1); vblast_flush(v); // this seems to power off the phone vblast_command(v, COMMAND_PHONE_OFF); vblast_flush(v); vblast_command(v, COMMAND_MUTE_OFF ); vblast_flush(v); vblast_device_close(v); } #define VOICE_CHUNK_SIZE 64 // max size for voice write pipe // // play the specified file out the usb-device, play it continuously if 'repeat' // is set. as playing it, call the callback routine if specified, if the // callback routine returns non-zero or device goes on-hook, stop playing // and return. // void vblast_play(VBLAST *v, char *name, u_char *vbuf, int len, int flags, int (*callback)(VBLAST *v)) { off_t l; HANDLE fd; int n, c, have; u_char *bp, buf[VOICE_CHUNK_SIZE]; if(vbuf) { bp = vbuf; have = len; } else if((fd = File_Open_ReadOnly(name)) == INVALID_HANDLE_VALUE) { ERR(("Can't open(%s)\n", name)) exit(-1); } vblast_command(v, COMMAND_VOUT_START); vblast_command(v, COMMAND_0x10 ); vblast_command(v, COMMAND_0x11 ); while(1) { if(vbuf) // playing from a buffer { n = (have < VOICE_CHUNK_SIZE) ? have : VOICE_CHUNK_SIZE; if(n > 0) { memcpy(buf, bp, n); bp += n; have -= n; } else if((n == 0) && (flags & VB_PLAY_REPEAT)) { bp = vbuf; have = len; continue; } else { break; // all played out } } else if((n = File_Read(fd, buf, VOICE_CHUNK_SIZE)) < 1) { if((n == 0) && (flags & VB_PLAY_REPEAT)) { if((l = File_Rewind(fd)) == INVALID_POSITION_VALUE) { ERR(("vblast_play-rewind(%s): error(%s)\n", name, Show_Error())) break; } if(l != ((off_t) 0)) { ERR(("vblast_play-rewind(%s): got(" OFF_T_FMT ")\n", name, l)) break; } continue; } if(n < 0) { ERR(("vblast_play(%s): error(%s)\n", name, Show_Error())) } break; // all played out } vblast_voice_write(v, buf, n); vblast_wait_input(v, 0); // poll all input if((flags & VB_PLAY_KEEP_STATUS) == 0) // save for external processing? { while((c = vblast_status_deque(v)) >= 0) // keep queue empty { ERR(("vblast_play(%s): received(%x)\n", name, c)) } } if(v->status_hook == STATUS_HOOK_ON) { MSG(("vblast_play(%s): on-hook\n", name)) break; } if(callback) { if(((*callback)(v)) != 0) break; } } vblast_shutdown_voice(v); if(vbuf == NULL) { File_Close(fd); } } void vblast_play_file(VBLAST *v, char *name, int flags, int (*callback)(VBLAST *v)) { vblast_play(v, name, NULL, 0, flags, callback); } // send ring-command (0x03), expect that will get back 0x05 to say // is ringing, at most will ring for 30 seconds, then will get 0x06 // to say has stopped ringing, so then send the ring command again // returns if goes off-hook or callback requests void vblast_ring(VBLAST *v, int (*callback)(VBLAST *v)) { int c; MSG(("vblast_ring: ringing\n")) vblast_send_and_wait(v, COMMAND_RING_ON, STATUS_RINGING_ON, 5); while(1) { vblast_wait_input(v, 10); if(v->status_hook == STATUS_HOOK_OFF) { MSG(("vblast_ring: off-hook\n")) break; } while((c = vblast_status_deque(v)) >= 0) { if(c == STATUS_RINGING_OFF) { vblast_send_and_wait(v, COMMAND_RING_ON, STATUS_RINGING_ON, 5); } else { ERR(("vblast_ring: received(%x)\n", c)) } } if(callback) { if(((*callback)(v)) != 0) break; } } vblast_command(v, COMMAND_RING_OFF); vblast_shutdown_voice(v); // if don't do, get garbled output for a second // kill the dialtone vblast_play(v, "silence", silence, sizeof(silence), 0, NULL); } // // The End! //