/* $Id: capifax.c,v 1.2 1998/10/23 12:50:47 fritz Exp $ * * A FAX send application for CAPI. * This stuff is based heavily on AVM's CAPI-adk for linux. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: capifax.c,v $ * Revision 1.2 1998/10/23 12:50:47 fritz * Added RCS keywords and GPL notice. * */ #include #include #include #include #include #include #include #include #include #include "c20msg.h" #include "capi.h" #include "connect.h" #include "contr.h" #include "data.h" #include "id.h" #include "init.h" #include "fax.h" extern char *stationID; extern char *headLine; static char *CallingPartyNumber = NULL; static char *CalledPartyNumber = NULL; static ConnectionID Slot; #define B1PROTOCOL 4 #define B2PROTOCOL 4 #define B3PROTOCOL 4 #define QueueSize 8 typedef struct __DataElement { char DATA[SendBlockSize]; unsigned short DATA_LENGTH; unsigned SENT; } _DataElement; typedef struct __DataQueue { _DataElement Element[QueueSize]; unsigned Head; unsigned Tail; unsigned Fill; } _DataQueue; _DataQueue Queue; static unsigned FileTransfer = FALSE; /* signals if transfer is in progress */ static int verbose; static int reason; static int reason_b3; /*--------------------------------------------------------------------------*\ * MainDataConf: signals the successful sending of a datablock * This function is called after receiving a DATA_B3_CONFirmation. CAPI signals * that the datablock identified by DataHandle has been sent and the memory * area may be freed. The DataHandle is the same as specified in SendBlock. \*--------------------------------------------------------------------------*/ void MainDataConf(ConnectionID Connection, unsigned short DataHandle, unsigned short Info) { assert (Connection != INVALID_CONNECTION_ID); if (Info != 0) return; if (FileTransfer) { assert (DataHandle == (unsigned short)Queue.Tail); Queue.Element[Queue.Tail].SENT = FALSE; if (++Queue.Tail >= QueueSize) Queue.Tail = 0; Queue.Fill--; } } /*--------------------------------------------------------------------------*\ * MainStateChange: signals a state change on both B-channels (connected, * disconnected). Whenever a channel changes his state this function is called \*--------------------------------------------------------------------------*/ void MainStateChange(ConnectionID Connection, ConnectionState State) { faxNCPI_t *faxNCPI; assert (Connection != INVALID_CONNECTION_ID); if (State == Disconnected) { unsigned short r3 = GetB3Reason(Connection); unsigned short r = GetReason(Connection); Slot = INVALID_CONNECTION_ID; reason_b3 = r3; reason = r; if (!verbose) return; printf("Disconnected.\n"); printf(" Reason : %04x %s\n", r, Decode_Info(r)); printf(" Reason-B3 : %04x %s\n", r3, Decode_Info(r3)); if ((faxNCPI = GetFaxNCPI(Connection))) { printf(" Remote Station ID : %s\n", faxNCPI->id); printf(" Transfer-Rate : %d bps\n", faxNCPI->rate); printf(" Resolution : %s\n", faxNCPI->resolution ? "high" : "low"); printf(" Number of Pages : %d\n", faxNCPI->pages); } } } /*--------------------------------------------------------------------------*\ * Disconnect_h: high level Disconnect \*--------------------------------------------------------------------------*/ unsigned Disconnect_h(ConnectionID Connection) { ConnectionState State; if (Connection == INVALID_CONNECTION_ID) { fprintf(stderr, "Disconnect_h: ConnectionID is invalid\n"); return 0xFFFF; } State = GetState(Connection); if ((State == Disconnected) || (State == D_DisconnectPending)) return 0xFFFF; return Disconnect(Connection); } void InitQueue(void) { unsigned x; for (x=0; x 0) { t = Queue.Tail; do { if (Queue.Element[t].SENT == FALSE) { error = SendData(0, (void *)Queue.Element[t].DATA, Queue.Element[t].DATA_LENGTH, (unsigned short)t); if (error != 0) { fprintf(stderr, "Error during transfer: 0x%04X !!!\n",error); break; } Queue.Element[t].SENT = TRUE; } if (++t >= QueueSize) t = 0; } while (t != Queue.Head); } } unsigned SendFax(char *name) { int first; char mbuf[4]; unsigned count; B3_PROTO_FAXG3 B3conf; FILE *f; first = 1; reason = reason_b3 = 0; if (!strcmp(name, "-")) f = stdin; else f = fopen(name, "rb"); if (!f) { perror(name); exit(errno); } fread(mbuf, 4, 1, f); InitQueue(); SetupB3Config(&B3conf, (strncmp(mbuf, "Sfff", 4)) ? FAX_ASCII_FORMAT:FAX_SFF_FORMAT); if (Slot != INVALID_CONNECTION_ID) { fprintf(stderr, "Connection is already in use\n"); fclose(f); return 0xFFFF; } Connect(&Slot, CalledPartyNumber, CallingPartyNumber, SPEECH, B1PROTOCOL, B2PROTOCOL, B3PROTOCOL, (unsigned char *)&B3conf); do { Handle_CAPI_Msg(); if (Slot == INVALID_CONNECTION_ID) { fclose(f); return 2; } } while (GetState(Slot) != Connected); FileTransfer = TRUE; while (!feof(f)) { if (Queue.Fill < 7) { /* max. 7 outstanding blocks supported by CAPI */ if (first) { memcpy(&(Queue.Element[Queue.Head].DATA[0]), mbuf, 4); count = fread(&(Queue.Element[Queue.Head].DATA[4]), 1, SendBlockSize - 4, f); } else count = fread(&(Queue.Element[Queue.Head].DATA[0]), 1, SendBlockSize, f); if (count > 0) { if (first) count += 4; Queue.Element[Queue.Head].DATA_LENGTH = (unsigned short)count; if (++Queue.Head >= QueueSize) Queue.Head = 0; Queue.Fill++; } first = 0; } if (GetState(Slot) != Connected) break; TransferData(); Handle_CAPI_Msg(); } Disconnect_h(Slot); while ((Slot != INVALID_CONNECTION_ID) && (GetState(Slot) != Disconnected)) Handle_CAPI_Msg(); FileTransfer = FALSE; fclose(f); switch (reason) { case 0x3490: /* Normal call clearing */ case 0x349f: /* Normal, unspecified */ return reason_b3; default: return reason; } return 0; } void usage(void) { fprintf(stderr, "usage: capifax [-v] [-i stationID] [-h header] [-c callerNumber] phone file\n"); exit(1); } int main(int argc, char **argv) { int numController; int BChannels, Contr; int c; int ret; verbose = 0; while ((c = getopt(argc, argv, "vc:i:h:")) != EOF) { switch (c) { case 'v': verbose++; break; case 'c': CallingPartyNumber = strdup(optarg); break; case 'i': stationID = strdup(optarg); break; case 'h': headLine = strdup(optarg); break; case '?': usage(); } } if (argc < optind + 2) usage(); CalledPartyNumber = argv[optind++]; Slot = INVALID_CONNECTION_ID; MainDataConf_p = MainDataConf; MainStateChange_p = MainStateChange; if (!RegisterCAPI()) return -1; atexit (ReleaseCAPI); InitConnectionIDHandling(); if (!(numController = GetNumController())) { fprintf(stderr, "No CAPI controllers available\n"); return -2; } BChannels = 0; for (Contr=1; Contr<=numController; Contr++) BChannels += GetNumOfSupportedBChannels(Contr); if (!BChannels) { fprintf(stderr, "No B-Channels available\n"); return -3; } ret = SendFax(argv[optind]); if ((Slot != INVALID_CONNECTION_ID) && (GetState(Slot) != Disconnected) && (GetState(Slot) != D_DisconnectPending)) Disconnect(Slot); return ret; }