From f0834d471c8d4dc0a692ef0b1621df4a1f7b9d6a Mon Sep 17 00:00:00 2001 From: Michael Jerris Date: Sun, 20 May 2007 10:30:51 +0000 Subject: [PATCH] implementations for the cross platform read and write in sangoma_tdm_api.h. move zap_wanpipe.c to use the read and write from sangoma_tdm_api.h. cleanups and lots of comments added to sangoma_tdm_api.h some fixes to sangoma_tdm_api.h to make the api more consistent between windows and nix. git-svn-id: http://svn.openzap.org/svn/openzap/trunk@51 a93c3328-9c30-0410-af19-c9cd2b2d52af --- libs/openzap/src/include/sangoma_tdm_api.h | 375 ++++++++++++++------- libs/openzap/src/zap_wanpipe.c | 162 +-------- 2 files changed, 271 insertions(+), 266 deletions(-) diff --git a/libs/openzap/src/include/sangoma_tdm_api.h b/libs/openzap/src/include/sangoma_tdm_api.h index 0d3f14da96..a795507c92 100644 --- a/libs/openzap/src/include/sangoma_tdm_api.h +++ b/libs/openzap/src/include/sangoma_tdm_api.h @@ -1,9 +1,14 @@ /***************************************************************************** - * sangoma_tdm_api.h Sangoma API Code Library + * sangoma_tdm_api.h Sangoma TDM API Portability functions * - * Author(s): David Rokhvarg + * Author(s): Anthony Minessale II + * Nenad Corbic + * Michael Jerris + * David Rokhvarg * - * Copyright: (c) 1984-2006 Sangoma Technologies Inc. + * Copyright: (c) 2006 Nenad Corbic + * Anthony Minessale II + * (c) 1984-2007 Sangoma Technologies Inc. * * ============================================================================ */ @@ -11,21 +16,59 @@ #ifndef _SANGOMA_TDM_API_H #define _SANGOMA_TDM_API_H +/* This entire block of defines and includes from this line, through #define FNAME_LEN probably dont belong here */ +/* most of them probably belong in wanpipe_defines.h, then each header file listed included below properly included */ +/* in the header files that depend on them, leaving only the include for wanpipe_tdm_api.h left in this file or */ +/* possibly integrating the rest of this file diretly into wanpipe_tdm_api.h */ #ifndef __WINDOWS__ #if defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32) #define __WINDOWS__ -#endif -#endif +#endif /* defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32) */ +#endif /* ndef __WINDOWS__ */ -#ifdef __WINDOWS__ -#ifdef _MSC_VER -/* disable warning for zero length array in a struct */ -/* this will cause errors on c99 and ansi compliant compilers and will need to be fixed in the wanpipe header files */ -#pragma warning(disable:4200 4201 4214) -#endif +#if defined(__WINDOWS__) +#if defined(_MSC_VER) +/* disable some warnings caused by wanpipe headers that will need to be fixed in those headers */ +#pragma warning(disable:4201 4214) + +/* sang_api.h(74) : warning C4201: nonstandard extension used : nameless struct/union */ + +/* wanpipe_defines.h(219) : warning C4214: nonstandard extension used : bit field types other than int */ +/* wanpipe_defines.h(220) : warning C4214: nonstandard extension used : bit field types other than int */ +/* this will break for any compilers that are strict ansi or strict c99 */ + +/* The following definition for that struct should resolve this warning and work for 32 and 64 bit */ +#if 0 +struct iphdr { + +#if defined(__LITTLE_ENDIAN_BITFIELD) + unsigned ihl:4, + version:4; +#elif defined (__BIG_ENDIAN_BITFIELD) + unsigned version:4, + ihl:4; +#else +# error "unknown byteorder!" +#endif + unsigned tos:8; + unsigned tot_len:16; + unsigned id:16; + unsigned frag_off:16; + __u8 ttl; + __u8 protocol; + __u16 check; + __u32 saddr; + __u32 daddr; + /*The options start here. */ +}; +#endif /* #if 0 */ + +#define __inline__ __inline +#endif /* defined(_MSC_VER) */ #include +/* do we like the name WP_INVALID_SOCKET or should it be changed? */ #define WP_INVALID_SOCKET INVALID_HANDLE_VALUE -#else +#else /* defined(__WINDOWS__) */ #define WP_INVALID_SOCKET -1 #include #include @@ -43,13 +86,16 @@ #define FNAME_LEN 50 +/* what should the returns from this function be?? */ +/* I dont think they are currently consistant between windows and *nix */ #ifdef __WINDOWS__ -static int tdmv_api_ioctl(sng_fd_t fd, wanpipe_tdm_api_cmd_t *tdm_api_cmd) +static __inline__ int tdmv_api_ioctl(sng_fd_t fd, wanpipe_tdm_api_cmd_t *tdm_api_cmd) { + /* can we make the structure passed for this on nix and windows the same */ + /* so we don't have to do the extra 2 memcpy's on windows for this ? */ wan_udp_hdr_t wan_udp; DWORD ln; unsigned char id = 0; - int err = 0; wan_udp.wan_udphdr_request_reply = 0x01; wan_udp.wan_udphdr_id = id; @@ -58,10 +104,7 @@ static int tdmv_api_ioctl(sng_fd_t fd, wanpipe_tdm_api_cmd_t *tdm_api_cmd) wan_udp.wan_udphdr_command = WAN_TDMV_API_IOCTL; wan_udp.wan_udphdr_data_len = sizeof(wanpipe_tdm_api_cmd_t); - //copy data from caller's buffer to driver's buffer - memcpy( wan_udp.wan_udphdr_data, - (void*)tdm_api_cmd, - sizeof(wanpipe_tdm_api_cmd_t)); + memcpy( wan_udp.wan_udphdr_data, (void*)tdm_api_cmd, sizeof(wanpipe_tdm_api_cmd_t)); if(DeviceIoControl( fd, @@ -73,67 +116,25 @@ static int tdmv_api_ioctl(sng_fd_t fd, wanpipe_tdm_api_cmd_t *tdm_api_cmd) (LPDWORD)(&ln), (LPOVERLAPPED)NULL ) == FALSE){ - //actual ioctl failed - err = 1; - return err; - }else{ - err = 0; + return 1; } if(wan_udp.wan_udphdr_return_code != WAN_CMD_OK){ - //ioctl ok, but command failed return 2; } - //copy data from driver's buffer to caller's buffer - memcpy( (void*)tdm_api_cmd, - wan_udp.wan_udphdr_data, - sizeof(wanpipe_tdm_api_cmd_t)); + memcpy( (void*)tdm_api_cmd, wan_udp.wan_udphdr_data, sizeof(wanpipe_tdm_api_cmd_t)); return 0; } #else -static int tdmv_api_ioctl(sng_fd_t fd, wanpipe_tdm_api_cmd_t *tdm_api_cmd) +static __inline__ int tdmv_api_ioctl(sng_fd_t fd, wanpipe_tdm_api_cmd_t *tdm_api_cmd) { return ioctl(fd, SIOC_WANPIPE_TDM_API, tdm_api_cmd); } #endif -static sng_fd_t tdmv_api_open_span_chan(int span, int chan) -{ - char fname[FNAME_LEN]; -#if defined(__WINDOWS__) - - //NOTE: under Windows Interfaces are zero based but 'chan' is 1 based. - // Subtract 1 from 'chan'. - _snprintf(fname , FNAME_LEN, "\\\\.\\WANPIPE%d_IF%d", span, chan - 1); - - //prn(verbose, "Opening device: %s...\n", fname); - - return CreateFile( fname, - GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, - (LPSECURITY_ATTRIBUTES)NULL, - OPEN_EXISTING, - FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH, - (HANDLE)NULL - ); -#else - int fd = WP_INVALID_SOCKET; - - snprintf(fname, FNAME_LEN, "/dev/wptdm_s%dc%d",span,chan); - - fd = open(fname, O_RDWR); - - if (fd < 0) { - fd = WP_INVALID_SOCKET; - } - - return fd; -#endif -} - -void tdmv_api_close_socket(sng_fd_t *sp) +void __inline__ tdmv_api_close_socket(sng_fd_t *sp) { if( *sp != WP_INVALID_SOCKET){ #if defined(__WINDOWS__) @@ -145,72 +146,78 @@ void tdmv_api_close_socket(sng_fd_t *sp) } } -#ifdef __WINDOWS__ - -// Blocking read command. If used after DoApiPollCommand(), -// it will return immediatly, without blocking. -static -USHORT -DoReadCommand( - sng_fd_t drv, - RX_DATA_STRUCT * pRx - ) +static __inline__ sng_fd_t tdmv_api_open_span_chan(int span, int chan) { - DWORD ln; + char fname[FNAME_LEN]; + sng_fd_t fd = WP_INVALID_SOCKET; +#if defined(__WINDOWS__) + DWORD ln; + wan_udp_hdr_t wan_udp; - if (DeviceIoControl( - drv, - IoctlReadCommand, - (LPVOID)NULL, - 0L, - (LPVOID)pRx, - sizeof(RX_DATA_STRUCT), - (LPDWORD)(&ln), - (LPOVERLAPPED)NULL - ) == FALSE){ - //check messages log - return 1; - }else{ - return 0; + /* NOTE: under Windows Interfaces are zero based but 'chan' is 1 based. */ + /* Subtract 1 from 'chan'. */ + _snprintf(fname , FNAME_LEN, "\\\\.\\WANPIPE%d_IF%d", span, chan - 1); + + fd = CreateFile( fname, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + (LPSECURITY_ATTRIBUTES)NULL, + OPEN_EXISTING, + FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH, + (HANDLE)NULL + ); + + /* make sure that we are the only ones who have this chan open */ + /* is this a threadsafe way to make sure that we are ok and will */ + /* never return a valid handle to more than one thread for the same channel? */ + + wan_udp.wan_udphdr_command = GET_OPEN_HANDLES_COUNTER; + wan_udp.wan_udphdr_data_len = 0; + + DeviceIoControl( + fd, + IoctlManagementCommand, + (LPVOID)&wan_udp, + sizeof(wan_udp_hdr_t), + (LPVOID)&wan_udp, + sizeof(wan_udp_hdr_t), + (LPDWORD)(&ln), + (LPOVERLAPPED)NULL + ); + + if((wan_udp.wan_udphdr_return_code) || (*(int*)&wan_udp.wan_udphdr_data[0] != 1)){ + /* somone already has this channel, or somthing else is not right. */ + tdmv_api_close_socket(&fd); } -} -// Blocking write command. If used after DoApiPollCommand(), -// it will return immediatly, without blocking. -static -UCHAR -DoWriteCommand( - sng_fd_t drv, - TX_DATA_STRUCT * pTx - ) -{ - DWORD ln; +#else + /* Does this fail if another thread already has this chan open? */ + /* if not, we need to add some code to make sure it does */ + snprintf(fname, FNAME_LEN, "/dev/wptdm_s%dc%d",span,chan); - if(DeviceIoControl( - drv, - IoctlWriteCommand, - (LPVOID)pTx, - (ULONG)sizeof(TX_DATA_STRUCT), - (LPVOID)pTx, - sizeof(TX_DATA_STRUCT), - (LPDWORD)(&ln), - (LPOVERLAPPED)NULL - ) == FALSE){ - return 1; - }else{ - return 0; + fd = open(fname, O_RDWR); + + if (fd < 0) { + fd = WP_INVALID_SOCKET; } -} #endif + return fd; +} #if defined(__WINDOWS__) -/* This is broken, we don't actually get real results for oob messages on windows */ +/* This might be broken on windows, as POLL_EVENT_TELEPHONY seems to be commented out in sang_api.h.. it should be added to POLLPRI */ #define POLLPRI (POLL_EVENT_LINK_STATE | POLL_EVENT_LINK_CONNECT | POLL_EVENT_LINK_DISCONNECT) #endif /* return -1 for error, 0 for timeout, or POLLIN | POLLOUT | POLLPRI based on the result of the poll */ -int tdmv_api_wait_socket(sng_fd_t fd, int timeout, int flags) +/* on windows we actually have POLLPRI defined with several events, so we could theoretically poll */ +/* for specific events. Is there any way to do this on *nix as well? */ + +/* a cross platform way to poll on an actual pollset (span and/or list of spans) will probably also be needed for analog */ +/* so we can have one analong handler thread that will deal with all the idle analog channels for events */ +/* the alternative would be for the driver to provide one socket for all of the oob events for all analog channels */ +static __inline__ int tdmv_api_wait_socket(sng_fd_t fd, int timeout, int flags) { #if defined(__WINDOWS__) DWORD ln; @@ -279,4 +286,140 @@ int tdmv_api_wait_socket(sng_fd_t fd, int timeout, int flags) #endif } +/* on windows right now, there is no way to specify if we want to read events here or not, we allways get them here */ +/* we need some what to select if we are reading regular tdm msgs or events */ +/* need to either have 2 functions, 1 for events, 1 for regural read, or a flag on this function to choose */ +/* 2 functions preferred. Need implementation for the event function for both nix and windows that is threadsafe */ +static __inline__ int tdmv_api_readmsg_tdm(sng_fd_t fd, void *hdrbuf, int hdrlen, void *databuf, int datalen) +{ + /* What do we need to do here to avoid having to do all */ + /* the memcpy's on windows and still maintain api compat with nix */ + int rx_len=0; +#if defined(__WINDOWS__) + static RX_DATA_STRUCT rx_data; + api_header_t *pri; + wp_tdm_api_rx_hdr_t *tdm_api_rx_hdr; + wp_tdm_api_rx_hdr_t *user_buf = (wp_tdm_api_rx_hdr_t*)hdrbuf; + DWORD ln; + + if(hdrlen != sizeof(wp_tdm_api_rx_hdr_t)){ + return -1; + } + + if(!DeviceIoControl( + fd, + IoctlReadCommand, + (LPVOID)NULL, + 0L, + (LPVOID)&rx_data, + sizeof(RX_DATA_STRUCT), + (LPDWORD)(&ln), + (LPOVERLAPPED)NULL + )){ + return -1; + } + + pri = &rx_data.api_header; + tdm_api_rx_hdr = (wp_tdm_api_rx_hdr_t*)rx_data.data; + + user_buf->wp_tdm_api_event_type = pri->operation_status; + + switch(pri->operation_status) + { + case SANG_STATUS_RX_DATA_AVAILABLE: + if(pri->data_length > datalen){ + break; + } + memcpy(databuf, rx_data.data, pri->data_length); + rx_len = pri->data_length; + break; + + default: + break; + } + +#else + struct msghdr msg; + struct iovec iov[2]; + + memset(&msg,0,sizeof(struct msghdr)); + + iov[0].iov_len=hdrlen; + iov[0].iov_base=hdrbuf; + + iov[1].iov_len=datalen; + iov[1].iov_base=databuf; + + msg.msg_iovlen=2; + msg.msg_iov=iov; + + rx_len = read(fd,&msg,datalen+hdrlen); + + if (rx_len <= sizeof(wp_tdm_api_rx_hdr_t)){ + return -EINVAL; + } + + rx_len-=sizeof(wp_tdm_api_rx_hdr_t); +#endif + return rx_len; +} + +static __inline__ int tdmv_api_writemsg_tdm(sng_fd_t fd, void *hdrbuf, int hdrlen, void *databuf, unsigned short datalen) +{ + /* What do we need to do here to avoid having to do all */ + /* the memcpy's on windows and still maintain api compat with nix */ + int bsent = 0; +#if defined(__WINDOWS__) + static TX_DATA_STRUCT local_tx_data; + api_header_t *pri; + DWORD ln; + + /* Are these really not needed or used??? What about for nix?? */ + (void)hdrbuf; + (void)hdrlen; + + pri = &local_tx_data.api_header; + + pri->data_length = datalen; + memcpy(local_tx_data.data, databuf, pri->data_length); + + if(!DeviceIoControl( + fd, + IoctlWriteCommand, + (LPVOID)&local_tx_data, + (ULONG)sizeof(TX_DATA_STRUCT), + (LPVOID)&local_tx_data, + sizeof(TX_DATA_STRUCT), + (LPDWORD)(&ln), + (LPOVERLAPPED)NULL + )){ + return -1; + } + + if (local_tx_data.api_header.operation_status == SANG_STATUS_SUCCESS) { + bsent = datalen; + } +#else + struct msghdr msg; + struct iovec iov[2]; + + memset(&msg,0,sizeof(struct msghdr)); + + iov[0].iov_len=hdrlen; + iov[0].iov_base=hdrbuf; + + iov[1].iov_len=datalen; + iov[1].iov_base=databuf; + + msg.msg_iovlen=2; + msg.msg_iov=iov; + + bsent = write(fd,&msg,datalen+hdrlen); + if (bsent > 0){ + bsent-=sizeof(wp_tdm_api_tx_hdr_t); + } +#endif + return bsent; +} + #endif /* _SANGOMA_TDM_API_H */ diff --git a/libs/openzap/src/zap_wanpipe.c b/libs/openzap/src/zap_wanpipe.c index fc7142dc48..013da15be5 100644 --- a/libs/openzap/src/zap_wanpipe.c +++ b/libs/openzap/src/zap_wanpipe.c @@ -311,135 +311,15 @@ static ZINT_COMMAND_FUNCTION(wanpipe_command) return ZAP_SUCCESS; } -#ifdef __WINDOWS__ - -static ZINT_READ_FUNCTION(wanpipe_read_windows) -{ - zap_size_t rx_len = 0; - zap_status_t status = ZAP_FAIL; - - /* should we just pass in abuffer big enough in the first place instead of having to use rx_data and memcpy here? */ - static RX_DATA_STRUCT rx_data; - - if(DoReadCommand(zchan->sockfd, &rx_data)) { - snprintf(zchan->last_error, sizeof(zchan->last_error), "Error: DoReadCommand() failed! Check messages log.\n"); - goto done; - } - - switch(rx_data.api_header.operation_status) - { - case SANG_STATUS_RX_DATA_AVAILABLE: - if(rx_data.api_header.data_length > *datalen){ - snprintf(zchan->last_error, sizeof(zchan->last_error), "Buffer overrun.\n"); - break; - } - memcpy(data, rx_data.data, rx_data.api_header.data_length); - rx_len = rx_data.api_header.data_length; - status = ZAP_SUCCESS; - break; - - case SANG_STATUS_TDM_EVENT_AVAILABLE: - memcpy(data, rx_data.data, rx_data.api_header.data_length); - rx_len = rx_data.api_header.data_length; - status = ZAP_SUCCESS; - break; - - case SANG_STATUS_RX_DATA_TIMEOUT: - snprintf(zchan->last_error, sizeof(zchan->last_error), "Error: Timeout on read.\n"); - break; - - case SANG_STATUS_BUFFER_TOO_SMALL: - snprintf(zchan->last_error, sizeof(zchan->last_error), "Error: Received data longer than buffer passed to API.\n"); - break; - - case SANG_STATUS_LINE_DISCONNECTED: - snprintf(zchan->last_error, sizeof(zchan->last_error), "Error: Line disconnected.\n"); - break; - - default: - snprintf(zchan->last_error, sizeof(zchan->last_error), "Rx:Unknown Operation Status: %d\n", rx_data.api_header.operation_status); - break; - } - -done: - *datalen = rx_len; - return status; -} - -static ZINT_WRITE_FUNCTION(wanpipe_write_windows) -{ - static TX_DATA_STRUCT local_tx_data; - - /* why don't we just provide the big enough buffer to start with so we can avoid the memcpy ? */ - memcpy(local_tx_data.data, data, *datalen); - - /* queue data for transmission */ - if(DoWriteCommand(zchan->sockfd, &local_tx_data)) { - snprintf(zchan->last_error, sizeof(zchan->last_error), "Error: DoWriteCommand() failed!! Check messages log.\n"); - *datalen = 0; - return ZAP_FAIL; - } - - if (local_tx_data.api_header.operation_status == SANG_STATUS_SUCCESS) { - return ZAP_SUCCESS; - } - - *datalen = 0; - - switch(local_tx_data.api_header.operation_status) - { - case SANG_STATUS_TX_TIMEOUT: - snprintf(zchan->last_error, sizeof(zchan->last_error), "****** Error: SANG_STATUS_TX_TIMEOUT ******\n"); - break; - - case SANG_STATUS_TX_DATA_TOO_LONG: - snprintf(zchan->last_error, sizeof(zchan->last_error), "****** SANG_STATUS_TX_DATA_TOO_LONG ******\n"); - break; - - case SANG_STATUS_TX_DATA_TOO_SHORT: - snprintf(zchan->last_error, sizeof(zchan->last_error), "****** SANG_STATUS_TX_DATA_TOO_SHORT ******\n"); - break; - - case SANG_STATUS_LINE_DISCONNECTED: - snprintf(zchan->last_error, sizeof(zchan->last_error), "****** SANG_STATUS_LINE_DISCONNECTED ******\n"); - break; - - default: - snprintf(zchan->last_error, sizeof(zchan->last_error), "Unknown return code (0x%X) on transmission!\n", local_tx_data.api_header.operation_status); - break; - } - - return ZAP_FAIL; -} - -#else - -static ZINT_READ_FUNCTION(wanpipe_read_unix) +static ZINT_READ_FUNCTION(wanpipe_read) { int rx_len = 0; - struct msghdr msg; - struct iovec iov[2]; wp_tdm_api_rx_hdr_t hdrframe; - memset(&msg, 0, sizeof(msg)); memset(&hdrframe, 0, sizeof(hdrframe)); - memset(iov, 0, sizeof(iov[0])*2); - iov[0].iov_len = sizeof(hdrframe); - iov[0].iov_base = &hdrframe; + rx_len = tdmv_api_readmsg_tdm(zchan->sockfd, &hdrframe, (int)sizeof(hdrframe), data, (int)*datalen); - iov[1].iov_len = *datalen; - iov[1].iov_base = data; - - msg.msg_iovlen = 2; - msg.msg_iov = iov; - - rx_len = read(zchan->sockfd, &msg, iov[1].iov_len + iov[0].iov_len); - - if (rx_len > 0) { - rx_len -= sizeof(hdrframe); - } - *datalen = rx_len; if (rx_len <= 0) { @@ -450,37 +330,24 @@ static ZINT_READ_FUNCTION(wanpipe_read_unix) return ZAP_SUCCESS; } -static ZINT_WRITE_FUNCTION(wanpipe_write_unix) +static ZINT_WRITE_FUNCTION(wanpipe_write) { int bsent; - struct msghdr msg; - struct iovec iov[2]; wp_tdm_api_tx_hdr_t hdrframe; - memset(&msg, 0, sizeof(msg)); + /* Do we even need the headerframe here? on windows, we don't even pass it to the driver */ memset(&hdrframe, 0, sizeof(hdrframe)); - memset(iov, 0, sizeof(iov[0])*2); - - iov[0].iov_len = sizeof(hdrframe); - iov[0].iov_base = &hdrframe; + bsent = tdmv_api_writemsg_tdm(zchan->sockfd, &hdrframe, (int)sizeof(hdrframe), data, (unsigned short)(*datalen)); - iov[1].iov_len = *datalen; - iov[1].iov_base = data; - - msg.msg_iovlen = 2; - msg.msg_iov = iov; - bsent = write(zchan->sockfd, &msg, iov[1].iov_len + iov[0].iov_len); - - if (bsent > 0){ - bsent -= sizeof(wp_tdm_api_tx_hdr_t); + /* should we be checking if bsent == *datalen here? */ + if (bsent > 0) { + *datalen = bsent; + return ZAP_SUCCESS; } - *datalen = bsent; - - return ZAP_SUCCESS; + return ZAP_FAIL; } -#endif static ZINT_WAIT_FUNCTION(wanpipe_wait) { @@ -527,13 +394,8 @@ zap_status_t wanpipe_init(zap_software_interface_t **zint) wanpipe_interface.close = wanpipe_close; wanpipe_interface.command = wanpipe_command; wanpipe_interface.wait = wanpipe_wait; -#ifdef __WINDOWS__ - wanpipe_interface.read = wanpipe_read_windows; - wanpipe_interface.write = wanpipe_write_windows; -#else - wanpipe_interface.read = wanpipe_read_unix; - wanpipe_interface.write = wanpipe_write_unix; -#endif + wanpipe_interface.read = wanpipe_read; + wanpipe_interface.write = wanpipe_write; *zint = &wanpipe_interface; return ZAP_SUCCESS;