INVERSE MULTIPLEXING SYSTEM FOR ISDN COMMUNICATIONS

Abstract: A network interface card (NIC) (23) for improved inverse multiplexing of multichannel ISDN communications includes a controller (91) and a plurality of buffers (92), each storing data from a respective channel. The NIC generates outgoing pointers for each of the plurality of buffers and are offset based on differences between frame count data received from the channels and stored in the respective buffers. The NIC reads data from each of the plurality of buffers in accordance with the outgoing pointers, thereby correcting for interchannel delays in the data. The data is also read from the pointers in a sequence that compensates for channel crossing.

Description: INVERSE MULTIPLEXING SYSTEM FOR ISDN COMMUNICATIONS Background of the Invention This application claims priority from U.S. Provisional Appln. 60/089,172, filed 6/12/98, entitled "State-Based Bonding Technique For ISDN Communications Interface", from U.S. Provisional Appln 60/096,971, filed 8/18/98, entitled "Inverse Multiplexing System" and U.S.S.N. , filed 6/9/99, entitled "Inverse Multiplexing System For Isdn Communications" (Express Mail Label: EE776165410US). The teachings of the aforementioned applications are incorporated by reference herein. The present invention pertains to ISDN communications and, more particularly, to processing and generating "inverse multiplexed" ISDN streams. The invention has application in network interface cards or other apparatus that interface to ISDN communication lines. For convenience, such cards and other apparatus are referred to hereinafter, collectively, as network interface cards or NICs. As is generally known, ISDN networks can be configured to transfer a single data stream over multiple ISDN lines, each of which includes plural data channels. This is a boon to video teleconferencing and other data intensive applications which demand greater bandwidth than that provided by a single ISDN channel. Problems arise in multichannel ISDN communications, however, because transmissions on the respective lines can be delayed relative to one another, principally due to differences in the communications paths established by the lines. In addition, the realties of initiating multiple calls on the network often result in the channels appearing to "cross." Thus, for example, a channel defined as "channel 1" at one end of the network may appear as "channel 3" at another end, and vice versa. These difficulties are illustrated in Figure 1. There, test data comprising patterns of l's, 2's and 3's are input into respective channels at one side of an ISDN network. That data is both delayed and crossed en route through the network. Specifically, in the example shown, network setup results in data on two of the channels, i.e., those labeled 5 and 6, appearing to cross so that the pattern of "111111112222222233333333" transmitted by one node is received as "222222221111111133333333." In-transit delays, moreover, result in the pattern of "l's" originally sent on channel 6 being offset by two time slots 9 relative to the data on the other channels. Without compensation for interchannel delays and channel crossing, multichannel ISDN communications would be virtually impossible. For example, were data representing an image, e.g., of a video teleconference participant, transmitted over the channels shown in Figure 1, the illustrated effects would result in garble at the receiving end. To address these problems, a technique referred to as "inverse multiplexing" (sometimes referred to as "bonding," which is a particular type of inverse multiplexing) has been developed. This is a process for reconstituting or aggregating data transmitted via plural ISDN channels. It is typically implemented in accordance with a set of international protocols that detail how equipment (such as NICs) at the ends of the network signal each other to open the channels and to "train" one another for purposes of discerning interchannel delays and channel crossing. Implementation of training has traditionally required a complex arrangement of digital signal processors and buffers. These are used to generate training patterns that are transmitted from each end of the network and that are analyzed to determine the necessary parameters for overcoming interchannel delay and crossing. Once training is completed, those parameters are used to reorder and align data exchanged during the call. While conventional inverse multiplexing systems provide accurate data realignment and reordering, they can be costly to implement. In addition to high-speed, high-powered digital signal processors, implementation of conventional inverse multiplexing techniques demands a significant amount of fast program memory (e.g., 32 Kbytes or more) and of data memory (e.g., 120 Kbytes) for buffering ingoing and outgoing data streams. These components can significantly increase the cost of network interface cards or other equipment used at the terminal ends of the ISDN network. In view of the foregoing, an object of the invention is to provide improved methods and apparatus for ISDN communications. A more particular object of the invention is to provide improved methods and apparatus for processing inverse-multiplexed (or bonded) ISDN data, and for generating such data. Still further objects of the invention provide such methods and apparatus as can be implemented with conventional components and without undue consumption of monetary resources, operating power, or board "real estate". Summary of the Invention The present invention addresses the foregoing needs by providing, in one aspect, a network interface card (or other ISDN interface device) providing improved inverse multiplexing of data received from plural ISDN channels. The NIC stores training data received from the channels into different respective buffers and determines interchannel delays by comparing the relative positions in those buffers of numerical counts associated with the data. Subsequent to training, the NIC uses output pointers, which are assigned to the channels and offset in accord with the delays, to aggregate call data into a single stream or storage area. In a related aspect, the invention provides a NIC as described above that stores, to the buffers, frame counts or other such sequential data derived from training data in the respective channels. Interchannel delay is determined by comparing the counts contained in corresponding locations of the buffers. For example, if a frame count of 15 is stored at address 10 of the buffer associated with channel one, and a frame count of 17 is stored at that same address in the buffer associated with channel two, a NIC according to this aspect of the invention would determine that the interchannel delay is two, i.e., 17 - 15. Based on that determination, the output pointers for the buffers are offset by two to insure proper alignment during subsequent aggregation (i.e., inverse multiplexing) of call data. In a preferred aspect of the invention, a NIC as described above determines interchannel delay by comparing the counts contained in the buffers at or near their respective ingoing pointers. The differences between the values of the pointers themselves are taken into account in order to determine the delay. Thus, continuing the above example, if the value 15 is stored at the incoming pointer of the buffer associated with channel one, the value 18 is stored at the incoming pointer in the buffer associated with channel two, the value of the incoming pointer of channel one 10, and the value of the incoming pointer of channel two is 11, then a NIC according to this aspect of the invention would determine that the interchannel delay is two, i.e., (18 - 15) - (11 - 10). Further aspects of the invention provide a NIC as described above that tracks the buffer pointers, that stores data to buffers, and that retrieves data from the buffers utilizing the serial communications controller ("SCC") of a conventional communications microprocessor. A NIC as described above, according to still further aspects of the invention, reorders data in order to compensate for channel crossing. The NIC determines whether such crossing has occurred by examining channel data stored to the buffers, e.g., along with the frame counts. Reordering is accomplished by outputting data from the buffers in a sequence that compensates for any detected crossing. For example, if training data expected in channel three was instead received in channel two and stored to its associated buffer, while that expected in channel two was instead received in channel three and stored to its associated buffer, a NIC according to this aspect of the invention outputs data from the buffer for channel three prior to that of the buffer for channel two. Alternatively, the NIC can output data from buffer three to the area in the aggregate store otherwise associated with channel two, and vice versa. According to further aspects of the invention, a NIC as described above delays writes to the channel buffers in order to insure that the writes will fall on longword or other required boundaries. Thus, for example, the buffering of byte data received by the NIC and transmitted over its internal buses certain clock cycles is delayed one to three frames. This ensure writing to requisite boundaries within the buffers for devices having such a limitations. Still further aspects of the invention provide a NIC of the type described above that includes S/T interface chips or other devices for exchanging signals with the ISDN lines. The NIC can further include a field programmable gate array (FPGA) that routes each channel of data between the line interfaces and a communications microprocessor that maintains the aforementioned channel buffers. Such an FPGA can, further, move data between the buffers and a host in which the NIC resides. Yet still further aspects of the invention provide methods inverse multiplexing ISDN data corresponding to the operations described above. This brief summary has been provided so that the nature of the invention may be understood quickly. A more complete understanding of the invention and of its various aspects are evident in the detailed description that follows and in the attached drawings. Brief Description of the Drawings A more complete understanding of the invention may be attained by reference to the drawings, in which: Figure 1 depicts a multichannel ISDN communication with channel crossing and interchannel delay of the type corrected by the present invention; Figure 2 depicts the architecture of a NIC in accordance with the preferred embodiment of the invention; Figure 3 depicts a field programmable gate array (FPGA) that enables the NIC of Figure 2 to conduct parallel and serial data transfers with a host; Figure 4 depicts the FPGA of Figure 3 configured to effect serial transfers of data between the ISDN lines and the processor of the NIC, as well as between the NIC and the host; Figure 5 is a table showing the timing and routing of data on the IDL buses for the configuration shown in Figure 4; Figure 6 shows the timing of ISDN data transfer on a time-division multiplexed bus internal to the NIC of Figure 2; Figure 7 shows a training pattern used in a conventional inverse multiplexing process; Figures 8 - 12 show how buffers and pointers are used in the NIC of Figure 2 in order to effect inverse multiplexing; Figure 9 shows the buffers of Figure 8 with the first buffer filling with data; Figure 10 shows the buffers of Figure 8 with both buffers filling with data; Figure 11 shows the buffers of Figure 8 at a later point in time than is shown in Figure 10; Figure 12 shows the buffers of Figure 8 during output of data therefrom; Figure 13 depicts, abstractly, output of data from the buffers of Figure 8 to the output buffer; Figure 14 shows software tasks executing on the NICs processor to implement bonding in accordance with the present invention; Figure 15 shows transmit and receive buffers used in a preferred implementation of the invention; and Figure 16 shows transmit and receive buffers used in another implementation of the invention. Detailed Description of the Illustrated Embodiment 1 . Overview Figure 2 is a high level block diagram of a network interface card ("NIC") according to one embodiment of the invention. The illustrated card 20 provides an interface between an ISDN network and a host, e.g., video conferencing equipment or other apparatus. The invention is not limited to use in this context and can be incorporated into a variety of different environments. The card 20 can be mounted for direct physical and electrical connection with the host bus, power and other circuitry, though, it preferably "piggybacks" on another circuit board (e.g., an ISA or PCI board) that, itself, is in direct connection with the host. NIC 20 includes an interface 29 through which data and control signals are exchanged with the host. This interface preferably includes conductors that permit both parallel data transfer and serial data transfer between the NIC 20 and host. Illustrated NIC 20 also includes three S/T interface devices 25, 26 and 27 for interfacing with corresponding basic rate interface ("BRI") ISDN lines. Additional or fewer such devices may be provided, as desired. S/T interfaces 25, 26 and 27 are preferably standard circuit devices conforming to ANSI Tl.601-1992. An example of a chip which may be used for this purpose is the Motorola ® MC145574. Each S/T interface device terminates a corresponding ISDN line 25a, 26a and 27a, and passes data between that line and the NIC. In this regard, each ISDN line includes two bearer (or B) channels and a single data (or D channel). The B channels typically carry video, audio and other signals between the NIC and ISDN network nodes. The D channel carries line setup and other "administrative" data between the NIC and whomever controls the ISDN lines (e.g., the telephone company). The S/T interfaces 25-27 each include a TDM serial interface which supports data (e.g., video, HDLC and transparent) transfers to/from the NIC and, specifically, its 2.048 Mbps IDL bus 35. Those skilled in the art will, of course, appreciate that the invention is not dependent on the specific type of bus (DDL, or otherwise) used within the NIC. The S/T interfaces include one or more internal buffers (not shown) which can be configured to temporarily store and transfer data, e.g., with bus 35 in response to commands from processor 28. Each S/T interface chip also includes a serial control port ("SCP") bus interface for interfacing to SCP bus 37. SCP bus 37 provides information to SCP 40 on processor 28 concerning the number of ISDN lines from which the NIC is receiving data, among other things. This information is then provided to the processor's SCC 41 - a thirty-two- channel full duplex SCC, which then adjusts its processing (e.g., bonding) accordingly. Finally, the S/T interfaces include interrupt pins for driving interrupts to the processor 28. Processor 28 is preferably a Motorola ® MC68MH360 microprocessor operating at 25 MHZ; although other types of processors may be used. Processor 28 includes SCC 41, SCP port 40, and interrupt controller 42, among other things. SCC 41 receives up to 32 channels of ISDN data from IDL bus 35, and processes that data accordingly, for example, performing inverse multiplexing (i.e., bonding) on it. SCP port 40 receives data from the S/T interfaces regarding the ISDN lines connected thereto, and provides that data to the processor. Interrupt controller 42 receives interrupts from the S/T interfaces, as noted above, as well as to the FPGA. Processor 28 may also include one or more internal RAM caches (not shown) for storing ISDN and other data. Memory 31 comprises a DRAM in preferred embodiments of the invention. This memory is used by processor 28 to buffer data transferred between the ISDN lines and the host, as well as to store instructions for execution by the processor 28. Memory 30 comprises a 1 Mbyte flash EEPROM in preferred embodiments of the invention. This memory also stores instructions, e.g., host code, for execution by processor 28. Referring to Figure 3, FPGA 32 is comprised of programmable logic gates which are configured, e.g., in response to commands received from the processor 28, to achieve the transfer of bonded or unbonded ISDN data between the S/T interface chips and the serial or parallel portions of the host interface 29. The portion 32a of the FPGA to the right of the dashed line, routes ISDN D channel data between the S/T interfaces and the processor 28, while routing B channel data between those interfaces and the serial channel of the host interface 29 either directly (in the case of unbonded transfers) or indirectly via processor 28 (in the case of bonded transfers). This is described in Section 2. The portion 32b of the FPGA 32 to the left of the dashed line transfers both bonded and unbonded B channel data between the processor 28 and the parallel channel of the host interface 29. This is described in Section 3. 2. Serial Transfers with the Host Figure 4 shows FPGA 32 configured for routing ISDN data between the S/T interface chips 25-27 and the serial portion of host interface 29. In this regard, FPGA 32 is configured by the microprocessor 28 to emulate time-variant multiplexers 65, 66 and 67. In brief, multiplexer 65 transmits ISDN data received on IDL bus 35 to processor 28 through IDL bus 37. There, the data may be bonded (i.e., inversed multiplexed) in the conventional manner or, preferably, in the manner described in section 4 below. The bonded data is then transmitted back along IDL bus 37 to multiplexer 67, from which the data is transmitted to the host (or carrier card) 70 via IDL bus 36. Similarly, multiplexer 65 routes transmit data from IDL bus 36 to processor 28. There, the data may be bonded, and then transmitted back along IDL bus 37 to multiplexer 66, from which the data is transmitted to the ISDN lines. In cases where the NIC does not employ bonding, unbonded data is transmitted directly from the S/T interfaces along IDL bus 35 to multiplexer 67, from which it is transmitted directly to the host 70 via IDL bus 36. Similarly, unbonded data may be transmitted directly from the host 70 via IDL bus 36 to multiplexer 66, from which it is routed to the ISDN lines. In the illustrated NIC, the configurations of time- variant multiplexers 65, 66 and 67 are controlled by processor 28 in accord with the desired IDL bus routings which, in turn, depends on whether bonding is to be performed. This is governed, in turn, on commands received from the host. Of course, this information may be "hard coded" in the NIC or otherwise controlled by it. Figure 5 is a table which shows, in detail, where and when multiplexers 65, 66 and 67 route data among the IDL buses of the NIC. Columns in the table identify those buses. Thus, for example, the transmit and receive sides of bus 35 are identified as "Re from BRI" and "Tx to BRI." Rows of the table indicate time slots. This is both from the perspective of the NIC (here, referred to as "Brick") and from the perspective of the host (here, referred to as "Carrier"). As indicated by a row-wise comparison and as explained below, NIC time slot 3 is the same as host time slot 19; NIC time slot 4, host time slot 20; and so forth. The designations LI, L2 and L3 in the table identify ISDN lines 1, 2 and 3, respectively. The designations Bl, B2 and D refer to the first B channel, the second B channel and the D channel, respectively, of a line. Re and Tx designate the receive and transmit modes of a channel. Thus, for example, the designation LI D Re refers to received D channel data of ISDN line 1. Before turning to the substance of Figure 5, it will be appreciated that the illustrated NIC takes advantage of buffering capabilities of the S/T interface chips 25, 26 and 27 in order to output the B and D channel data from the ISDN lines in a preferred sequence. Thus, data from respective channels of the ISDN lines are buffered such that all D channel data is transferred early in the bus frame, followed by the chips B channel data (which is be transferred in the middle of the frame). More specifically, the S/T interface chips for ISDN lines 25 a (line 1), 26a (line 2), and 27a (line 3) buffer their respective D and their B channel data (labeled Bl and B2) so that the D channel data is transferred during slots 4, 5 and 6 of the frame shown in Figure 6, and so that the B channel data is transferred during slots 16 to 21 of that frame. Thus, for example, B channel data for line 1, i.e., LI Bl Re, LI B2 Re, LI Bl Tx, and LI B2 Tx, are transferred by S/T chip 25 during slots 16 and 17, respectively, of the frame; B channel data for line 2, i.e., L2 Bl Re, L2 B2 Re, L2 Bl Tx, and L2 B2 Tx, are transferred by line S/T chip 26 during slots 18 and 19, respectively, of the frame; and B channel data for line 3, i.e., L3 Bl Re, L3 B2 Re, L3 Bl Tx, and L3 B2 Tx, are transferred by S/T chip 27 during slots 20 and 21, respectively, of the frame. Of course, in cases where additional ISDN lines are used, additional slots of the frame (i.e., slots 22 et seq.) will be occupied with additional data. With this background, B and D channel data are routed by multiplexers 65, 66 and 67 of FPGA 32 in accordance with the table shown in Figure 5. For example, the LI Bl Re data on the "Re from BRI" portion of IDL bus 35 are routed, by multiplexer 65, to the "Re to 360" portion of IDL bus 35. From there, the data is routed to processor 28 for bonding. Thereafter, the data is transmitted from processor 28 to the "Bonded from 360" portion of IDL bus 35, whereafter the data is transmitted, via multiplexer 67, to the "RC to Carrier" portion of IDL bus 36. From there, the LI Bl Re data is transmitted to carrier card 70 (the host), which can comprise a video card or the like for processing the data. As shown in Figure 5, processed LI Bl Tx data may then be transferred from carrier card 70 to the "Tx from Carrier" portion of IDL bus 36. Thereafter, multiplexer 67 transmits that data to the "Tx to BRI" portion of IDL bus 35, from which the data is transmitted to an appropriate S/T interface chip (here chip 25) and thence to an ISDN line. A similar process is performed for the other data shown in Figure 5. The time-variant nature of the multiplexers will be readily appreciated upon inspection of Figures 4 and 5. In instances where communications over the ISDN lines are not bonded, multiplexer 65 is active only during the time slots that the S/T chips 25 - 27 are driving received D channel data onto bus 35; multiplexer 67 is active when those chips 25 - 27 are driving received B channel data onto that bus 35; and multiplexer 66 is active when the 28 communications microprocessor is driving transmit D channel data onto IDL bus 37, as well as when the host 70 is driving transmit B channel data onto IDL bus 36. In instances where communications over the ISDN lines are bonded, multiplexer 65 is active during time slots that the S/T chips are driving received D and B channel data onto IDL bus 35, as well as when the host 70 is driving transmit B channel data onto LDL bus 36; multiplexer 67 is active when the communications microprocessor 28 is driving unbonded B channel data onto IDL bus 37; and multiplexer 66 is active when the communications microprocessor 28 is driving transmit D channel data onto the IDL bus 37, as well as when it is driving bonded B channel data that bus 37. As noted above, B channel data from the various ISDN lines is transferred during time slots 16 et seq. of the frame shown in Figure 5. However, in general, the host's carrier card 70 expects to receive data beginning at the first slot of each frame. Accordingly, the illustrated system provides a way that essentially redefines when the frame begins for the carrier card and, therefore, the host. More specifically, as noted above, a frame on an IDL bus is comprised of a frame pulse followed by 32 time slots. The NIC redefines the frame shown in Figure 5 by generating an "artificial" frame pulse at time slot 16, i.e., to coincide with the output of B channel data from the first S/T interface chip. Circuitry, such as processor 28, may be used to generate this artificial frame pulse. By outputting this "artificial" frame pulse to coincide with time slot 16, the NIC essentially redefines the frame such that new time slot 0 corresponds to old time slot 16, new time slot 1 corresponds to old time slot 17, etc. (see the "Carrier Time Slot" column of Figure 5). As a result, carrier card 40 receives a redefined frame having B channel data beginning at its time slot 0, as is preferable. Of course, the "artificial" frame pulse may be output to coincide with any of the time slots shown in Figure 5, so long as this is acceptable both to the IDL bus and to the carrier card. Along these lines, in cases where the carrier card does not have a preference regarding where data is located in a frame, no redefinition need be provided at all. 3. Parallel Transfers with the Host NIC 20 is capable of transferring data over the host serial interface or host parallel interface. Parallel transfers are effected by issuing interrupts to the host, which performs a direct memory access or other block data transfer operation to move data to and from the NIC DRAM 31. Parallel transfers with the host generally proceed as described above. Unlike serial transfers, however, data moves between the NIC and the host are performed by the block I/O control circuitry shown to the left of the dashed line in Figure 3. More particularly, as the processor 28 fills DRAM 31 with received ISDN data, it sends an interrupt (designated LNTerrupt in the drawing) to the host, indicating that data is ready for transfer. Driver software (not shown) on the host can then initiate a block move or DMA operation by asserting addresses on the auxiliary control lines coupling the NIC and host. These include the lines designated CAR_ADDR, IORD- and IOWR- in Figure 3. Illustrated elements 45 - 61 of the FPGA 32 respond to such signaling by transferring data to and from requested locations in DRAM 31 via ISA bus 52 and host interface lines labeled CAR_DATA. Within NIC 20, parallel transfers are effected by routing all B channel data to processor 28 (and, more particularly, DRAM 31), regardless of whether the data is transferred to/from the ISDN lines in bonded or unbonded form. Thus, in a case that data is to be bonded, bonding is performed in the desired manner, whereafter the bonded B channel data is stored in DRAM 31. On the other hand, if bonding is not to be performed, processor 28 stores the unbonded B channel data directly to DRAM 31. Such unbonded B data is preferably stored in regions of the memory that correspond to the ISDN line and/or channel from whence the data came. The control circuitry 45-62 is used to access memory 31 and to control exchange of data between processor 28 and the host. To this end, FPGA 32 includes multiplexer 45 which controls data exchange with processor 28 via data bus 46. Also included in FPGA 32 are byte select multiplexers 47, read/write multiplexer 49, data latch 50, and chip configuration latch 51. Generally speaking, these components control transfer of data between bus 46 and a bus which interfaces to the host (i.e., ISA bus 52). Specifically, byte select multiplexers 47 select bytes to be written, and read/write multiplexer 49 writes the selected bytes to data latch 50, from which those bytes are transmitted either to bus 54 (and thence to processor 28) or to bus 52 (and thence to the host). Chip configuration latch 51 includes information, such as timing information or the like, which may affect such data transfers. Address information is provided over address bus 56. This bus is used to supply multiplexed addresses for host memory access from FPGA 32, as well as internal register decodes for bus access. These memory addresses are provided via internal DMA ("direct memory access") address registers 57 and 59. That is, as shown in the figure, the DMA address registers interface ISA bus 52 (which itself interfaces to a corresponding bus on the host) to address bus 56 via address multiplexer 60. As shown, I/O address decode blocks 61 and 62 are also provided in the FPGA for the exchange of control signals and the like with processor 28 and the host, respectively. To maintain isochronicity during parallel data transfers, the NIC can send an interrupt to the host for each ISDN frame defined by the ISDN lines. Alternatively, such interrupts are signaled less frequently, thus capitalizing on the efficiency of this form of data transfer. To this end, the FPGA 32 (or other circuitry in the NIC) can count the number of frame pulses, and thus the number of frames, that have been transmitted with the B channel data from FPGA 32 to processor 28. When a predetermined count has been reached, the FPGA or software in the processor 28 can assert an interrupt to the host. In preferred embodiments of the invention, this predetermined count (a.k.a., a "super frame pulse") comprises 160 frame pulses; although other counts may be used as well. Data transfer may be performed by processor 28 itself at the time of the interrupt, rather than via a DMA performed by the host. 4. Inverse Multiplexing (Bonding) The inverse multiplexing process begins prior to receipt of data from the ISDN lines. Specifically, to begin, in conjunction with the host (not shown) NIC 20 sets up a call to another network node/endpoint (not shown) over one or more ISDN lines. NIC 20 and the other node then negotiate over which "B" channels of ISDN lines 25 a, 26a and 27a data is to be exchanged. This is known as the negotiation phase of the inverse multiplexing process. The following assumes that the data is to be exchanged over only two "B" channels. .Of course, this need not be the case. For example, data may be exchanged over any number of "B" channels and over any number of ISDN lines. Once the negotiation phase is complete, the training phase of the inverse multiplexing process begins. It is during this phase that the inverse multiplexing parameters, e.g., interchannel delay and channel crossing are identified. In the illustrated embodiment, the training phase is implemented by computer- executable process steps executing in processor 28 (preferably in SCC 41); however, the invention is not limited as such. These process steps are preferably stored in a memory, such as memory 30, and include code (i) to store data — including frame counts — received from each of the ISDN channels to a respective one of a plurality of buffers; (ii) to determine time delays between the channels by comparing the frame counts stored in the buffers at (or near) their respective ingoing buffer pointers, (iii) to identify channel crossing by comparing channel information contained in the buffered data, and (iv) to read data out from the buffers in an order that compensates for channel crossing and using outgoing pointers that are offset to compensate for the interchannel delays. In more detail, SCC 41 operates in its transparent or QMC mode to track ingoing and outgoing pointers (e.g., row counts) for data buffers assigned to each of the respective ISDN channels. Those buffers can be contained within memory 31 or elsewhere, preferably, within in the NIC. In the illustrated embodiment, SCC 41 is shown as tracking ingoing and outgoing pointers for only two channels. SCC 41 sets the ingoing pointer for each channel based on the lower-order 14 bits of a master frame count generated, e.g., by processor 28, based frame pulses received from the ISDN lines. Specifically, SCC 41 sets each ingoing pointer to the value of those lower-order 14 bits at the time communications the respective channel are initiated. Thereafter, SCC 41 stores training data received from that channel at the location or address indicated by the respective ingoing pointer. With each write to the buffer, the pointer is incremented. The training data comprises a training pattern transmitted between network nodes involved in the multichannel call. In preferred embodiments of the invention, a conventional inverse multiplexing training pattern having a format as shown in Figure 7 is used; although it should be noted that the invention is not limited to using a training pattern having the particular configuration shown in Figure 7. A more complete description of the conventional inverse multiplexing training pattern can be found in "Interoperability Requirements for Nx56/64 kbit/s Calls", The Bonding Consortium, version 1.0 (September 1, 1992), the contents of which are hereby incorporated by reference into the subject application as if set forth herein in full. As shown in Figure 7, the preferred training pattern comprises a frame alignment word, followed by 63 bytes of data, an information channel message, followed by 63 bytes of data, a frame count, followed by 63 bytes of data, and a CRC (error checking) byte. The frame alignment word is used to determine the alignment of each frame of data, the information channel message identifies the channel from which the data originated, and the frame count comprises the relative number of the current frame of data. Upon receipt by the NIC, each training pattern is placed by an S/T interface chip into a respective slot of a frame on IDL bus 35. Multiplexer 65 in FPGA 32 then routes frames of training data from each channel to SCC 41 via the "Re to 360" channel of IDL bus 35. At this or a later time, processor analyzes the information channel message of the training data in each slot in order to determine whether the channels have been crossed by the network. Thus, for example, if processor determines that data expected for channel 3 has been received on channel 1, it stores an identifier to be used in reordering the data during aggregation. SCC 41 stores the received data into respective receiving buffers (e.g., designated buffers in memory 31) based on the ingoing pointers determined above. In this regard, in preferred embodiments of the invention, the processor only permits writing to longword boundaries within the buffers. Accordingly, when a given channel is set up, the ingoing pointer may indicate that data is to be written to a non-permitted boundary of the receiving buffer. To address this problem, the SCC may wait between one and three frames to begin writing the data into a receiving buffer, thereby, ensuring that the data is written to longword boundaries. As the data is being written into the respective receiving buffers, processor 28 confirms that its bytes contain the expected training data, i.e., the frame count, information channel message, etc. Thereafter, processor uses the frame count to determine the amount of delay of each channel. Specifically, processor determines the amount of channel delay based on the difference between the frame counts in the various slot buffers at the time data is initially input into each buffer. For example, taking the most simple case where data is received over only two channels, Figure 8 shows two receiving buffers 81 and 82, one for each channel. Initially, these two buffers are empty, as shown in Figure 8. Thereafter, data is input into buffer 81 from the first ISDN channel in accordance with input pointer 83, as shown in Figure 9. While this data is being input, buffer 82 starts to fill with data from the second ISDN channel. As shown in Figure 10, buffer 82 fills in accordance with input pointer 84. As is apparent from Figure 10, the two input pointers may be somewhat misaligned due SCC delays in incrementing pointers and writing to the buffers; however, they generally proceed through their respective buffers at substantially the same rate. Thus, the two buffers 81 and 82 fill at substantially the same rate, as shown in Figure 11. Once the training phase is over and the substantive portion of the multi-channel call begins, call data must be read out of the buffers in such a way as to compensate for interchannel delay. To this end, the illustrated system determines the delay, Δ, in accordance with the following equation: Δ - buffer y (I y ) - buffer χ (I χ ) - (I y - I χ ) , (1 ) where, buffer x (I x ) comprises the frame count at the outgoing pointer of buffer 81, buffer y (I y ) comprises the frame count at the outgoing pointer of buffer 82, and where I x and I y comprise pointer values in buffers 81 and 82, respectively. It should be noted that the differences determined in equation (1) above are with respect to a scale of 0 to 64, not a normal decimal scale. According to this scale, for example, 0 - 1 = 63, not -1. Thus, for the example shown in Figure 12, the delay, Δ, is determined as follows: Δ = 9 - 63 - (1046 - 1043) , (2) which works out to 7. Of course, in cases where there are more than just two channel buffers, the foregoing difference is determined between each buffer and a selected "reference" buffer, such as buffer 81. As shown in Figure 12, this Δ of 7 comprises the difference between data in both buffers at a single pointer value, such as 1043 (i.e., 6 - 63). Once this interchannel delay has been determined, SCC 41 uses this value to offset outgoing pointers that are used by the SCC to read data out of the buffers during the call in chief. Specifically, the outgoing buffer pointers are determined, for each non-reference buffer, by adding the amount of delay between frames to a selected outgoing pointer for that buffer. Stated mathematically, the outgoing pointer O y for buffer y is determined as follows: °y = ° X ~ Δ • (3) Thus, with reference to Figure 12, assuming that buffer 81 is buffer x, and knowing that the delay, Δ, is 7 based on equation (2) above, and that the outgoing pointer, O x , for buffer 81 is 1043, it is possible to determine a first outgoing pointer for buffer 82 (i.e., for buffer y) by subtracting Δ (7, in this case) from 1043. As shown in Figure 12, this corresponds to pointer 1036 which, like pointer 1043 in buffer 1, points to value "63". Thereafter, the outgoing pointers for buffers 81 and 82, starting from 1043 and 1036, respectively, are incremented to output data at substantially the same rate. Once the initial outgoing pointer values have been determined this way in the training phase, data may then be read from the buffers using these outgoing pointers following the training phase. Data read out of the receiving buffers using the calculated outgoing pointers is also reordered, if necessary, to correct for any "channel crossing". This is done during output from the buffers, preferably by storing data for specific channels into specific areas of an output buffer. This output buffer may be located, e.g., in memory 31 , or it may be internal to processor 28. From this output buffer, the data is transmitted to the host via IDL bus 35 (marked "Bonded from 360" portion thereof) and FPGA 32. The foregoing describes inverse multiplexing performed on data received from the ISDN lines. Multiplexing may also be performed in accordance with the invention on data to be transmitted by processor 28 to the ISDN lines. Specifically, in this context, multiplexing comprises segmenting or splitting the data so that it can be transmitted over more than one ISDN line. To implement this, NIC 20 may separate the data into data for plural channels. Thereafter, this data is stored into respective transmit buffers for each channel (i.e., the corollary of the receiving buffers described above). The data is then read out of the transmit buffers to the FPGA over IDL bus 35, from which it is transmitted, eventually, to different ISDN lines. 5. Software Architecture Several software tasks are executed on processor 28 to implement bonding in the preferred embodiment of the invention. A functional diagram of these tasks is shown in Figure 14. Bonding task 90 provides overall control of the bonding process. It handles setup, answer, negotiation, cross connects and interaction with call processing task 91. Bond 360 task 92 receives messages from bonding task 90 that would otherwise be transmitted to a digital signal processor ("DSP"). Bond 360 task 92 then coordinates processing of messages, initializes the SCC channels, executes framing state machines, generates and receives training and negotiation patterns, calculates frame phase delay, and sets up a DMA to provide for phase compensation. The Bond 360 task waits on a message queue for messages. Two types of messages are supported: bonding timer messages and messages from bonding task 90 to a "DSP" (which, in this case, is not used). The bonding timer messages are period messages initiated by the bond 360 task. These messages are used to initialize the "background" processes needed to implement bonding in accordance with the present invention. The search for master channel framing, training pattern framing, and alignment state machines are executed in response to this messages. The messages themselves contain no information. After receipt of the message, the bond 360 task restarts the timer. The bonding task DSP messages comprise the messages sent to control state machines to implement bonding. In this implementation of the invention, these messages are processed in a function called ProcessBondMessage(). Processing of these messages verifies that the message is valid for the current state of the channel. For each message, the proper actions are taken and the channel state is updated. Table 1 below lists all of the supported messages. All other messages are ignored. Table 1 The transparent channel of processor 28 in QMC mode facilitates bonding in accordance with the invention. In accordance with the invention, each channel is started independently and the phase alignment of the 16 DMA's (two for each of the eight B- channels) maintained. A function called StartChannel performs this service. The StartChannel function uses a timer processor 28 to count TDM bus frames. This count is used to synchronize channels when they are started. When a channel is started, the system gets the current count from the timer and masks it to 14 bits. This value is then used to compute the starting point within the Tx and Rx buffers in the manner described above. Processor 28 requires that the starting address for a receive buffer be on a long word boundary, multiple of 4. Therefore, the actual start count will be adjusted for criteria and the system will wait until this adjusted count is reached by the timer before starting the channels. In this regard, the Tx DMA can be started on any byte boundary. In the following example, depicted in Figure 15, two channels are started for master channel negotiation and/or training phases of the bonding process. Channel 1 : When Channel 1 is started, the frame synch counter (timer4) contains the value 0x4122. This value is rounded up to 0x4124 and masked to 14 bits to produce the buffer starting offset of 0x0124. The StartChannel function then waits until the frame synch counter reaches this value and starts the DMA. Channel 2: When Channel 2 is started, the frame synch counter (timer4) contains the value 0x4578. This value is already a multiple of 4; however, since the timeslot that is being processed is unknown, it is necessary to wait until alignment to a frame synch pulse. This occurs when the frame synch counter becomes 0x4579. To make this value be a multiple of 4 requires that it be rounded up to 0x457C. This value is then masked to 14 bits to produce the buffer starting offset value of 0x057C. The StartChannel function will then wait until the frame synch counter reaches this value and start the DMA. Since Channel l's DMA will now be offset at 0x057C, the channels will be synchronized. In the following example, depicted in Figure 16, two channels are started after the phase compensation has been determined. Channel 1 : When Channel 1 is started, the frame synch counter (timer4) contains the value 0x4122. This value is rounded up to 0x4124 and masked to 14 bits to produce the buffer starting offset of 0x124 for the receiver. Because of the order of operation within processor 28, the Tx and Rx DMA's must first be offset by at least two long words (8 bytes). Therefore, if a phase delay of zero is detected, an offset of 8 must be added. The Tx starting buffer offset will be computed by subtracting the computed phase delay plus 8 from the starting Rx offset. For this example, the computed phase delay on Channel 1 is two. Therefore, the Tx offset will be 0x334A. Channel 2: When Channel 2 is started, the frame synch counter (timer4) contains the value 0x0248. For this example, the computed phase delay on Channel 2 is zero. Therefore, the Tx offset will be 0x244 and the Rx offset will be 0x24C. Since the Tx DMA on Channel 1 is offset by 10 (8 for the SCP and 2 for the phase delay) and the channel Tx DMA on Channel 2 is offset by 8, a relative delay of 2 frames will be produced (i.e., 10- 8=2). Computer code to implement the preferred embodiment of the present invention is set forth in the Attachment hereto. The present invention has been described with respect to particular illustrative embodiments. It is to be understood that the invention is not limited to the above- described embodiments and modifications thereto, and that various changes and modifications may be made by those of ordinary skill in the art without departing from the spirit and scope of the invention. Thus, for example, it will be appreciated that the invention is not limited to inverse multiplexing data transmitted over IDL buses but, rather, may be used to inverse multiplex data transmitted over a host of other like buses. Moreover, it will be appreciated that the invention is not limited to the specific timing sequences shown in the drawings and described above. Still further, it will be appreciated that the invention can be used with fewer, greater, or different types and numbers of ISDN channels, S/T chips, buffers, and microprocessors. In view of the foregoing, what is claimed is: APPENDIX TO UNITED STATES PATENT APPLICATION FOR INVERSE MULTIPLEXING SYSTEM FOR ISDN COMMUNICATIONS /***************************************************************************** * * * File: bndchan.c Date: April 20,1996 * * * * (C) Copyright 1997 by VideoServer Inc. * * All Rights Reserved * * Engineer: Dave Oliveira * * * * Description: The file contains code to support Bonding on the * * 68 H360 * * * *****************************************************************************/ /***************************************************************************** * Include Files * *************************************************************************** #include <string.h> #include "codestd.h" #include "devkit.h" #include "imux_db.h" #include "bndqmc.h" ***************************************************************************** * External Data * *****************************************************************************/ extern ChannelData ChanData [MAX_BOND_CHA NELS] ; extern volatile Uint32 TimerTicks; extern Uint32 Where [] ; ***************************************************************************** * Global Data * *****************************************************************************/ PUBLIC Uint32 ulTxinit = 250; PUBLIC Uint32 ulTxfa = 250 PUBLIC Uint32 ulTxaddOl = 500 PUBLIC Uint32 ulTxdeq = 350 PUBLIC Uint32 ulTxadd02 = 500 PUBLIC Uint32 ulTxadd = 250 PUBLIC Uint32 ulTxdel = 250 PUBLIC Uint32 ulTxdisc = 250 PUBLIC Uint32 ulTxnull = 500 PUBLIC Uint32 ulTCID = 250 /***************************************************************************** * Function Prototypes * A****************************************************************************/ PRIVATE void Ans erState (Intlβ wPhysChan); PRIVATE void MasterState (Intlβ wPhysChan); PRIVATE void NoSyncState (Intlδ wPhysChan); PRIVATE void TrainingState (Intlβ wPhysChan); PRIVATE void LearningState (Intlβ wPhysChan); PRIVATE Boolean NegoiationComplete (Intlβ wPhysChan); /***************************************************************************** * Routine Name: Bond360ChannelState () * * * * Description: This function is called when the periodic bonding timer * * expires. We process the state machines here. * * * * Parameters: wPhysChan (I) The channel number of the channel to process * * * * Return Value: N/A * * * *****************************************************************************/ PUBLIC void Bond360ChannelState (Intlδ wPhysChan) { switch (ChanData [PhysToBuff (wPhysChan) ] . ubChanState) { case CS_NULL : break; case CS_ANSWER: AnswerState (wPhysChan) ; break; case CS_MASTER: MasterState (wPhysChan) ; break; case CS_N0SYNC: NoSyncState (wPhysChan) ; break; case CS_NEGODONE: /* Nothing to do in this state */ break; case CS TRAINING: TrainingState (wPhysChan) ; break; case CS_LEARNING: LearningState (wPhysChan) ; break; case CS_ALIGN: break; } } /***************************************************************************** * Routine Name: SetChannelTimer () * * * * Description: This function sets the timer for a channel to a new * * expiration value. * * * * Parameters: wPhysChan (I) The channel number of the channel to process * * ulTicks (I) The number of timerticks to wait * * Return Value : N/A * * * ***************************************************************************** PUBLIC void SetChannelTimer (Intlβ wPhysChan, Uint32 ulTicks) { if( ulTicks != 0 ) { ulTicks = TimerTicks + ulTicks; if (ulTicks == 0) { ulTicks = 1; } } ChanData [PhysToBuff (wPhysChan) ] .ulTimer = ulTicks; } /***************************************************************************** * Routine Name: CheckChannelTimer () * * * * Description: This function checks if the timer for a channel has expired * * * * Parameters: wPhysChan (I) The channel number of the channel to process * * * * Return Value: TRUE The timer has expired * * FALSE The timer is still running * * * ***************************************************************************** PUBLIC Boolean CheckChannelTimer (Intlβ wPhysChan) { register Intlβ wBuffChan; * * * The timer has expired if the all of following are true: * * 1. The timer is active, i.e. the timer value is not zero * * 2. The timer value and the current tick count both have the same * * msb . * * 3. The Tick count is greater than or equal to the timer value. * * */ wBuffChan = PhysToBuff (wPhysChan) ; return ( (Boolean) ( (ChanData [wBuffChan] .ulTimer != 0) && (((TimerTicks Λ ChanData [wBuffChan] .ulTimer) & 0x80000000) == 0) && (TimerTicks >= ChanData [wBuffChan] .ulTimer) ) ); } /***************************************************************************** * Routine Name: AnswerState () * * * * Description: This function is called when the channel is in the * * CS_ANSWΞR state. * * * * Parameters: wPhysChan (I) The channel number of the channel to process * * * * Return Value: N/A * * * *****************************************************************************/ PRIVATE void AnswerState (Intlβ wPhysChan) { register Intlβ wBuffChan; * * * Check for Master Channel Framing * * * / wBuffChan = PhysToBuff (wPhysChan) ; if ( IC_Sync (wPhysChan) ) { ChanData [wBuffChan] .ubChanState = CS_MASTER; SetChannelTimer (wPhysChan, ulTxinit) ; } * * * Check for Multi-Frame * * * else if ( MF_Sync (wPhysChan) ) { ChanData [wBuffChan] .ubChanState = CS TRAINING; ChanData [wBuffChan] .ulTimer = 0; ChanData [wBuffChan] .ubRxChanld = (ChanData [wBuffChan] .ubIC_Last [1] >> 1) & 0x3F; } * * * Check For Timeout * * * / else if( CheckChannelTimer (wPhysChan) ) { PostInfoMsg (wPhysChan, MCA_InSync, TimeoutError) ; TerminateChannel (wPhysChan) ; } } /★A*************************************************************************** * Routine Name: MasterState () * * * * Description: This function is called when the channel is in the * * CS_MASTER state. * * * * Parameters: wPhysChan (I) The channel number of the channel to process * * * * Return Value: N/A * * * ***************************************************************************** PRIVATE void MasterState (Intlβ wPhysChan) { register Intlβ wBuffChan; * . * * Check for Master Channel Messages * * * / wBuffChan = PhysToBuff (wPhysChan) ; if ( IC_Sync (wPhysChan) ) { if ( NegoiationComplete (wPhysChan) ) { ChanData [wBuffChan] .ubChanState = CS_NOSYNC; SetChannelTimer (wPhysChan, ulTxaddOl) ; } else { SetChannelTimer (wPhysChan, ulTxinit) ; } } /* * * Check For Timeout * * * / else if ( CheckChannelTimer (wPhysChan) ) { PostlnfoMsg (wPhysChan, MCA_InitSync, TimeoutError) ; TerminateChannel (wPhysChan) ; } } ***************************************************************************** * Routine Name: NoSyncState () * * * * Description: This function is called when the channel is in the * * CS_NOSYNC state. * * * * Parameters: wPhysChan (I) The channel number of the channel to process * * * * Return Value: N/A * * * ***************************************************************************** PRIVATE void NoSyncState (Intlβ wPhysChan) { register Intlβ wBuffChan; * * * Check for Master Channel Messages * * * wBuffChan = PhysToBuff (wPhysChan) ; IC_Sync (wPhysChan) ; if (ChanData [wBuffChan] .ubIC_State == IC_NOSYNC) { PostlnfoMsg (wPhysChan, MCA_InitSync, SyncLossError) ; ChanData [wBuffChan] .ubChanState = CS_NEGODONE ; ChanData [wBuffChan] .ulTimer = 0; } * * * Check For Timeout * * * / else if ( CheckChannelTimer (wPhysChan) ) { PostlnfoMsg (wPhysChan, MCA_InitSync, TimeoutError); TerminateChannel (wPhysChan) ; } } ***************************************************************************** * Routine Name: TrainingState () * * * * Description: This function is called when the channel is in the * * CS TRAINING state. * * Parameters : wPhysChan ( I) The channel number of the channel to process * * * * Return Value: N/A * * * ***************************************************************************** PRIVATE void TrainingState (Intlβ wPhysChan) { register Intlβ wBuffChan; /* * * Check for Multi-Frame * * * / if( MF_Sync (wPhysChan) ) { wBuffChan = PhysToBuff (wPhysChan) ,- ChanData [wBuffChan] .ubChanState = CS_LEARNING; ChanData [wBuffChan] .ulTimer = 0; ChanData [wBuffChan] .ubRxChanld = (ChanData [wBuffChan] .ubIC_Last [1] >> 1) & 0x3F; } /* * * Check For Timeout * * * / else if ( CheckChannelTimer (wPhysChan) ) { PostlnfoMsg (wPhysChan, MCA_InSync, TimeoutError); TerminateChannel (wPhysChan) ; } } ***************************************************************************** * Routine Name: LearningState () * * * * Description: This function is called when the channel is in the * * CS_LEARNING State. * * * * Parameters: wPhysChan (I) The channel number of the channel to process * * * * Return Value : N/A * * * ***************************************************************************** PRIVATE void LearningState (Intlβ wPhysChan) { / * * * Check For Timeout * * * / MF_Sync (wPhysChan) ; if( CheckChannelTimer (wPhysChan) ) { PostlnfoMsg (wPhysChan, MCA_InSync, TimeoutError); TerminateChannel (wPhysChan) ; ****************************************************************************^ * Routine Name: NegoiationComplete () * * Description: This function checks if the negoiation on a master channel * * has completed. * * * * Parameters: wChannel (I) The channel number of the channel to process * * * * Return Value: TRUE Negoiation has completed * * FALSE Negoiation is still in progress. * * * *****************************************************************************/ PRIVATE Boolean NegoiationComplete (Intlβ wPhysChan) { register Intlβ wBuffChan; wBuffChan = PhysToBuff (wPhysChan) ; return( ( (ChanData [wBuffChan] .ubIC_Last [1] >> 1) & 0x3F) != 0 ) ; } ***************************************************************************** * * * File: bndmsg.c Date: April 20,1996 * * * * (C) Copyright 1997 by VideoServer Inc. * * All Rights Reserved * * Engineer: Dave Oliveira * * * * Description: The file contains code to support Bonding on the * * 68MH360 * * * *****************************************************************************/ /***************************************************************************** * Include Files * *****************************************************************************/ #include <string.h> #include "codestd.h" #include "devkit.h" #include "imux_db.h" #include "bndqmc.h" ***************************************************************************** * Local Defines * *****************************************************************************/ #define TX_NULL_EXTENSION 250 /***************************************************************************** * External Data * *****************************************************************************/ extern Uint32 BondBuffer [MAX_BOND_CHANNELS] [MAX_DIR] [MAX_BUFFER_LONGS] ; extern ChannelData ChanData [MAX_BOND_CHANNELS] ; extern SessionData Sessions [MAX_BOND_SESSIONS] ; extern Uint32 Where [] ; extern Uint32 ulTxinit; extern Uint32 ulTxfa; extern Uint32 ulTxaddOl; extern Uint32 ulTxdeq; extern Uint32 ulTxadd02; extern Uint32 ulTxadd; extern Uint32 ulTxdel; extern Uint32 ulTxdisc; extern Uint32 ulTxnull; extern Uint32 ulTCID; ***************************************************************************** * Function Prototypes * ***************************************************************************** PRIVATE void ChanNull (IMUXMsg *pMsg) ; PRIVATE void ChanAnswer (IMUXMsg *pMsg) ; PRIVATE void ChanMaster (IMUXMsg *pMsg) ; PRIVATE void ChanTraining ( IMUXMsg *pMsg) ; PRIVATE void ChanLearning ( IMUXMsg *pMsg) ; PRIVATE void ChanAlign (IMUXMsg *pMsg) ; ************************************************************************ ***** * Routine Name: ProcessBondMsg () * * * * Description: This function is called when the periodic bonding timer * * expires. We process the state machines here. * * * * Parameters : None * * * * Return Value: N/A * * * *****************************************************************************/ PUBLIC void ProcessBondMsg (IMUXMsg *pMsg) { switch (ChanData [PhysToBuff (pMsg- >IMUXData . DspMsg . ubDsO ) ] . ubChanState) { case CS_NULL: ChanNull (pMsg) ; break; case CS_ANSWER: ChanAnswer (pMsg) ; break; case CS_MASTER: case CS_NOSYNC: case CS_NEGODONE: ChanMaster (pMsg) ; break; case CS_TRAINING: ChanTraining (pMsg) ; break; case CS_LEARNING: ChanLearning (pMsg) ; break; case CS_ALIGN: ChanAlign (pMsg) ; break; } } /A**************************************************************************** * Routine Name: ChanNull () * * * * Description: This function is called when the channel is in the * * CS_NULL state and a BondingDspMsg is sent by the Bonding * * Task. * * * * Parameters: pMsg (I) The message to be processed. * * * * Return Value: N/A * * * *****************************************************************************/ PRIVATE void ChanNull (IMUXMsg *pMsg) { Intlβ wStatus; Intlβ wPhysChan; Intlβ wBuffChan; Initialize Variables wStatus = SUCCESSFUL; wPhysChan = pMsg->IMUXData.DspMsg. ubDsO; wBuffChan = PhysToBuff (wPhysChan) ; . * * Process Message * * */ switch (pMsg->IMUXData.DspMsg.ubPrimcode) { case MCA_AnswerCall : memset ( (Uint8 *) &ChanData [wBuffChan] , 0, sizeof (ChannelData) ) ; memset ( (Uint8 *)BondBuffer [wBuffChan] [RX_BUFFER] , 0, BUFFER_BYTES) ; StartChannel (wPhysChan, (Uint8 *)BondBuffer [wBuffChan] [RX_BUFFER] , (Uint8 *)BondBuffer [wBuffChan] [TX_BUFFER] , 0) ; GenBufferNego (wPhysChan, (Uint8 *) &pMsg->IMUXData.DspMsg. data. info) ; ChanData [wBuffChan] .ublnterface = pMsg->IMUXData. DspMsg.ublnterface; ChanData [wBuffChan] .ubSession = pMsg->IMUXData. DspMsg.ubSession; ChanData [wBuffChan] .ubDSO = pMsg->IMUXData. DspMsg.ubDsO; SetChannelTimer (wPhysChan, ulTxnull) ; ChanData [wBuffChan] .ubChanState = CS_ANSWER; break; case MCA_InitCall : memset ( (Uint8 *) &ChanData [wBuffChan] , 0, sizeof (ChannelData) ) ; memset ( (Uint8 *)BondBuffer [wBuffChan] [RX_BUFFER] , 0, BUFFER_BYTES) ; StartChannel (wPhysChan, (Uint8 *)BondBuffer [wBuffChan] [RX_BUFFER] , (Uint8 *)BondBuffer [wBuffChan] [TX_BUFFER] , 0) ; GenBufferNego (wPhysChan, (Uint8 *) &pMsg->IMUXData.DspMsg. data. info) ; ChanData [wBuffChan] .ublnterface = pMsg- >IMUXData.DspMsg. ublnterface ; ChanData [wBuffChan] .ubSession = pMsg->IMUXData.DspMsg. ubSession; ChanData [wBuffChan] .ubDSO = pMsg->IMUXData.DspMsg.ubDs0; SetChannelTimer (wPhysChan, ulTxnull) ; ChanData [wBuffChan] .ubChanState = CS_MASTER; break; case MCA_Begin_Training : memset ( (Uint8 *) &ChanData [wBuffChan] , 0, sizeof (ChannelData) ) ; memset ( (Uint8 *)BondBuffer [wBuffChan] [RX_BUFFER] , 0, BUFFER_BYTES) ; StartChannel (wPhysChan, (Uint8 *)BondBuffer [wBuffChan] [RX_BUFFER] , (Uint8 *)BondBuffer [wBuffChan] [TX_BUFFER] , 0) ; GenBufferTraining (wPhysChan, (Uint8 * ) &pMsg- >IMUXData . DspMsg . data . info , , 0) ChanData [wBuffChan] .ublnterface = pMsg- >IMUXData. DspMsg .ublnterface ; ChanData [wBuffChan] .ubSession = pMsg- >IMUXData. DspMsg. ubSession; ChanData [wBuffChan] .ubDSO = pMsg- >IMUXData. DspMsg. ubDsO; SetChannelTimer (wPhysChan, ulTxnull+TX_NULL_EXTENSION) ; ChanData [wBuffChan] .ubChanState = CS TRAINING; break; case MCA_Align: wStatus = ERROR_INVALID_MSG_STATE; /* No Break, Fall into next case */ case MCA_Term_Channel : TerminateChannel (wPhysChan) ; break; default: break; /***************************************************************************** * Routine Name: ChanAnswerO * * * * Description: This function is called when the channel is in the * * CS_ANSWER state and a BondingDspMsg is sent by the Bonding * * Task. * * * * Parameters : pMsg (I) The message to be processed . * * * * Return Value: N/A * * * *****************************************************************************/ PRIVATE void ChanAnswer ( IMUXMsg *pMsg) { Intlβ wStatus; Intl6 wPhysChan; /* * * Initialize Variables * * */ wStatus = SUCCESSFUL; wPhysChan = pMsg- >IMUXData. DspMsg. ubDsO ; /* * * Process Message * * * / switch (pMsg- >IMUXData . DspMsg . ubPrimcode) { case MCA_Align: case MCA_InitCall: case MCA_Begin_Training : wStatus = ERROR_INVALID_MSG_STATE ; /* No Break, Fall into next case */ case MCA_Term_Channel : TerminateChannel (wPhysChan) ; break; case MCA_AnswerCall : /* Ignore */ break; default: break; } } ***************************************************************************** * Routine Name: ChanMasterO * * * * Description: This function is called when the channel is in the * * CS_MASTER state and a BondingDspMsg is sent by the Bonding * * Task. * * * * Parameters: pMsg (I) The message to be processed. * * * * Return Value: N/A * * * A****************************************************************************/ PRIVATE void ChanMaster (IMUXMsg *pMsg) { Intl6 wStatus; Intlβ wPhysChan; Intl6 wBuffChan; * * * Initialize Variables * * * / wStatus = SUCCESSFUL; wPhysChan = pMsg->IMUXData.DspMsg. ubDsO ; wBuffChan = PhysToBuff (wPhysChan) ; * * * Process Message * * * / switch (pMsg->IMUXData. DspMsg. ubPrimcode) { case MCA_InitCall : GenBufferNego (wPhysChan, (Uintθ *) &pMsg->lMUXData. DspMsg. data. info) ; ChanData [wBuffChan] .ublnterface = pMsg->IMUXData. DspMsg. ublnterface; ChanData [wBuffChan] .ubSession = pMsg->IMUXData. DspMsg. ubSession; ChanData [wBuffChan] .ubDSO = pMsg->IMUXData. DspMsg.ubDsO; SetChannelTimer (wPhysChan, ulTxinit) ; break; case MCA_Begin_Training: memset (ChanData [wBuffChan] .ubIC_Frame, 0, 3 * 16); memset (ChanData [wBuffChan] .ubIC_Last, 0, 16); GenBufferTraining (wPhysChan, (Uint8 *) &pMsg->IMUXData. DspMsg. data. info, , 0) ChanData [wBuffChan] .ublnterface = pMsg->IMUXData .DspMsg. ublnterface, ■ ChanData [wBuffChan] .ubSession = pMsg- >IMUXData.DspMsg.ubSession,- ChanData [wBuffChan] .ubDSO = pMsg->IMUXData. DspMsg. ubDsO; ChanData [wBuffChan] .ubIC_BuffIndex = 0; SetChannelTimer (wPhysChan, ulTxnull+TX_NULL_EXTENSION) ; ChanData [wBuffChan] .ubChanState = CΞ TRAINING; break; case MCA_Align: case MCA_AnswerCall : wStatus = ERROR_INVALID_MSG_STATE; /* No Break, Fall into next case */ case MCA_Term_Channel : TerminateChannel (wPhysChan) ; break; default: break; } } /***************************************************************************** * Routine Name: ChanTraining ( ) * * * * Description: This function is called when the channel is in the * * CS_TRAINING state and a BondingDspMsg is sent by the Bonding* * Task. * * Parameters: pMsg (I) The message to be processed. * * Return Value: N/A * * * ***************************************************************************** PRIVATE void ChanTraining (IMUXMsg *pMsg) { Intlβ wStatus; Intlβ wPhysChan; Intlβ wBuffChan; /* * * Initialize Variables * * * / wStatus = SUCCESSFUL; wPhysChan = pMsg->IMUXData.DspMsg. ubDsO ; wBuffChan = PhysToBuff (wPhysChan) ; /* * * Process Message * * */ switch (pMsg- >IMUXData . DspMsg . ubPrimcode) { case MCA_Begin_Training : memset (ChanData [wBuffChan] .ubIC_Frame, 0, 3 * 16); memset (ChanData [wBuffChan] .ubIC_Last, 0, 16); GenBufferTraining (wPhysChan, (Uint8 *) &pMsg->IMUXData. DspMsg. data. info, , 0); ChanData [wBuffChan] . ublnterface = pMsg- >IMUXData . DspMsg . ublnterf ace ; ChanData [wBuffChan] .ubSession = pMsg->IMUXData . DspMsg. ubSession; ChanData [wBuffChan] .ubDSO = pMsg->IMUXData. DspMsg. ubDsO; ChanData [wBuffChan] .ubIC_BuffIndex = 0; SetChannelTimer (wPhysChan, ulTxnull+TX_NULL_EXTENSION) ; break; case MCA_Align: case MCA_AnswerCall : case MCA_InitCall : wStatus = ERROR_INVALID_MSG_STATE; /* No Break, Fall into next case */ case MCA_Term_Channel : TerminateChannel (wPhysChan) ; break; default : break; /***************************************************************************** * Routine Name: ChanLearning ( ) * * * * Description: This function is called when the channel is in the * * CS_LEARNING state and a BondingDspMsg is sent by the Bonding* * Task. * * * * Parameters: pMsg (I) The message to be processed. * * * * Return Value: N/A * * * *****************************************************************************/ PRIVATE void ChanLearning ( IMUXMsg *pMsg) { Intlβ wStatus; Intlβ wPhysChan; Uint8 ubSession; * * * Initialize Variables * * * / wStatus = SUCCESSFUL; wPhysChan = pMsg->IMUXData.DspMsg. ubDsO; /* * * Process Message * * * / switch (pMsg- >IMUXData .DspMsg .ubPrimcode) { case MCA_InitCall : case MCA_AnswerCall : case MCA_Begin_Training : Status = ERROR_INVALID_MSG_STATE; /* No Break, Fall into next case */ case MCA_Term_Channel : TerminateChannel (wPhysChan) ; break; case MCA_Align: ubSession = pMsg- >IMUXData. DspMsg. ubSession; Sessions [ubSession] .ubState = AS_RMT_STATE ; Sessions [ubSession] .wBaseChan = wPhysChan; break; default : break; ***************************************************************************** * Routine Name: ChanAlignO * * * * Description: This function is called when the channel is in the * * CS_ALIGN state and a BondingDspMsg is sent by the Bonding * * Task . * * * * Parameters : pMsg (I) The message to be processed . * * * * Return Value: N/A * * * ***************************************************************************** PRIVATE void ChanAlign (IMUXMsg *pMsg) { Intlβ wStatus; Intl6 wPhysChan; * * * Initialize Variables * * */ wStatus = SUCCESSFUL; wPhysChan = pMsg->IMUXData.DspMsg. ubDsO; / * * * Process Message * * * / switch (pMsg->IMUXData. DspMsg. ubPrimcode) { case MCA_Align: case MCA_InitCall : case MCA_AnswerCall : case MCA_Begin_Training: wStatus = ERROR_INVALID_MSG_STATE; /* No Break, Fall into next case */ case MCA_Term_Channel : TerminateChannel (wPhysChan) ; break; default : break; } ***************************************************************************** * Routine Name: TerminateChannel () * * * * Description: This function is called when the a terminate channel msg * * is received. * * * * Parameters: wPhysChan (I) The channel number of the channel to process * * * * Return Value : N/A * * * *****************************************************************************/ PUBLIC void TerminateChannel (Intlβ wPhysChan) { Intlβ wBuffChan; UintS ubSession; Boolean boSessionDone; wBuffChan = PhysToBuff (wPhysChan) ; Bond360_RestoreChannel (wPhysChan) ; ubSession = ChanData [wBuffChan] .ubSession; memset (&ChanData [wBuffChan] , 0, sizeof (ChannelData) ) ; boSessionDone = TRUE; for(wBuffChan=0; wBuffChan<MAX_BOND_CHANNELS; wBuffChan++) { if ( (ChanData [wBuffChan] .ubSession == ubSession) && (ChanData [wBuffChan] .ubChanState != CS_NULL) ) { boSessionDone = FALSE; } } if (boSessionDone) { memset (&Sessions [ubSession] , 0, sizeof (SessionData) ) ; /***************************************************************************** * * * File: bndqmc.c Date: April 20,1996 * * * * (C) Copyright 1997 by VideoServer Inc. * * All Rights Reserved * * Engineer: Dave Oliveira * * * * Description: The file contains code to support Bonding on the * * 68MH360 * * * ***************************************************************************** ***************************************************************************** * Include Files * *****************************************************************************/ #include "codestd.h" #include "flags.h" #include "compiler. h" #include "gendef.h" #include "tune.h" #include "lif.h" #include "mc68360.h" #include "m360.h" #include "bndqmc.h" #ifdef BRICK #include "hbdt.h" #endif /***************************************************************************** * Local Defines * ***************************************************************************** #define QMC_INTR_RXB 0x01 #define QMC_INTR_TXB 0x02 #define PRESCALE_TMR1 8 ttdefine MAX_LOOPS 200 /* Max Number of loops to wait for FS */ #define MIN_DMA_SIZE 8 /* Minimum number of bytes to DMA */ /A**************************************************************************** * Local Data * *****************************************************************************/ PRIVATE Uint8 ubCurrentBondSeg [MAX_BOND_CHANNELS] [MAX_DIR] ; PRIVATE Uint8 *pubUpdateAddr[MAX_BOND_CHANNELS] [MAX_DIR] ; /***************************************************************************** * Local Function Prototypes * ***************************************************************************** PRIVATE void InitializeFsCounter (void) ; PRIVATE void SetupChannel (Intlβ wPhysChan, Uint8 *pRxBuffer, Uint8 *pTxBuffer) ; PRIVATE void SetTxBufferDesc (Intlβ wPhysChan, Intlβ wSegment, Intlβ wControl, void *pvData, Intlβ wLen) ; PRIVATE void SetRxBufferDesc (Intlβ wPhysChan, Intlβ wSegment, Intl6 wControl, void *pvData, Intlβ wLen) ; / ** * **** ** * ****** *** *** **** * ** * **** ** ** ** *************** * *** * *** ** ******** * *** * External Data * *****************************************************************************/ extern CPU_DATA CPU_Data [MAX_CPU] ; extern Uint32 Where [] ; /***************************************************************************** * Global Data * ***************************************************************************** /* * * Bonding Buffers. * * * * These are the buffers that the SCC DMAs will get and put the data for * * bonding. These buffers must be declared as Uint32s so that they will be * * aligned on long word boundries . * * * * Dimensions: BondBuffer [] [] [] * I I I * I I > Buffer Data * * I > Buffer Direction Tx or Rx * * > Logical Channel Number * * - * / PUBLIC Uint32 BondBuffer [MAX_BOND_CHANNELS] [MAX_DIR] [MAX_BUFFER_LONGS] ; PUBLIC Boolean boBondTimeslots [32] ; /***************************************************************************** * External Function Prototypes * *****************************************************************************/ extern Uint8 * _getMBAR (Uint32 processor); extern void QMC_IssueCmd(Uintl6 uwChannel, Uint8 ubOpCode) ; /***************************************************************************** * Routine Name: InitializeBond360 () * * * * Description: This routine initializes the system for Bonding on the MH360* * * * Parameters : None * * * * Return Value: N/A * * * *****************************************************************************/ PUBLIC void InitializeBond360 (void) { if (CPU_Data[0] . Controller Type == UP_QMC) { InitializeFsCounter () ; memset ( (char *) BondBuffer , 0, sizeof (BondBuffer) ) ; memset ((char *) boBondTimeslots, FALSE, sizeof (boBondTimeslots) ) ; #ifdef BRICK InitializeHBDTO ; #endif } } /***************************************************************************** * Routine Name: StartChannel () * * Description: This function starts a channel Tx and Rx synchronized to * the master counter. * Parameters : wChannel (I) The channel number to initialize wTxDelay (I) Transmit offset frame delay wMode (I) Channel mode, Training or Delay Compensation * Return Value : SUCCESSFUL The channel was successfully started ERROR_FS_TIMEOUT Timeout waiting for FS counter ERROR DMA TIMEOUT Timeout waiting for DMA *****************************************************************************, PUBLIC Intlβ StartChannel (Intlβ wPhysChan, UintS *pubRxBuffer, UintS *pubTxBuffer, Intlβ wTxDelay) register Intl6 wBuffChan; /* Buffer Channel Number */ register Intlβ wlndex; /* Loop Counter */ register P_BUF_DESC pRxBd; /* Pointer to Rx Buffer Descriptors * / register P_BUF_DESC pTxBd; /* Pointer to Tx Buffer Descriptors * / Intlβ wStatus ; * Return Status */ Intlβ wRxLen; /* Length of Initial Rx Buffer * / Intlβ wTxLen; /* Length of Initial Tx Buffer * / Uintlβ uwIntMask; /* Current value of interrupt priority mask */ Uintlβ uwRxMaster; Value of Master counter for Rx */ Uintlβ uwTxMaster; /* Adjusted value of Master counter for Tx */ Uintlβ uwRbPtr; /* New value for RBPTR in channel parameters */ Uintlβ uwTbPtr; /* New value for TBPTR in channel parameters */ Uintlβ uwRxSeg; Which buffer segment should be started for Rx */ Uintlβ uwTxSeg; Which buffer segment should be started for Tx */ UintS *pubRxAddr; /* Starting location in Rx buffer */ Uint8 *pubTxAddr; /* Starting location in Tx buffer */ Uint32 cCount ; /* Maximum loop count */ /' Setup Tx and Rx Buffer Descriptors wStatus = SUCCESSFUL; wBuffChan = PhysToBuff (wPhysChan) ; boBondTimeslots [wPhysChan] = TRUE; SetupChannel (wPhysChan, pubRxBuffer, pubTxBuffer) ; Get addresses of Tx and Rx Buffer Descriptors pRxBd = (P_BUF_DESC) ( IN32 (MCBASE_QMC) + IN16 (RBASE_QMC (wPhysChan) ) ) ; pTxBd = (P_BUF_DESC) ( IN32 (MCBASE_QMC) + IN16 (TBASE_QMC (wPhysChan) ) ) ; /*- * Disable Interrupts during this process uwIntMask == intr_set (7) ; * Wait for alignment with frame sync * uwRxMaster = IN16(TCN4); for (cCount=0; (cCount<MAX_LOOPS) && (uwRxMaster IN16(TCN4) cCount++) ; if (uwRxMaster == IN16(TCN4)) { wStatus = ERROR_FSl_TIMEOUT; } * Compute Master Timer Value at DMA Start * -*/ if (wStatus == SUCCESSFUL) { uwRxMaster = (IN16(TCN4) + 4) & 0x3FFC; wRxLen = BYTES_PER_SEGMENT - (uwRxMaster % BYTES_PER_SEGMENT) ; if (wRxLen < MIN_DMA_SIZE ) { uwRxMaster += MIN DMA SIZE; Compute Rx Starting Location uwRxSeg = uwRxMaster >> 11 ; uwRbPtr = uwRxSeg * sizeof (BUF_DESC) ; pubRxAddr = kpubRxBuf f er [uwRxMaster] ; wRxLen = BYTES_PER_SEGMENT - (uwRxMaster % BYTES_PER_SEGMENT) ; pubUpdateAddr [wBuffChan] [RX_BUFFER] = &pubRxBuf f er [uwRxSeg*BYTES_PER_SEGMENT] ; /*. * Compute Tx Starting Location uwTxMaster = (uwRxMaster - wTxDelay) & 0x3FFF; uwTxSeg = uwTxMaster >> 11 ; uwTbPtr = uwTxSeg * sizeof (BUF_DESC) ; pubTxAddr = &pubTxBuf f er [uwTxMaster] ; wTxLen = BYTES_PER_SEGMENT - (uwTxMaster % BYTES_PER_ΞEGMΞNT) ; pubUpdateAddr [wBuffChan] [TX_BUFFER] = &pubTxBuf f er [uwTxSeg*BYTES_PER_SEGMENT] ; Setup Initial buffer OUT32 (RxPtr_QMC (wPhysChan) , 0x00000000) ; OUT16 (RBPTR_QMC (wPhysChan) , INI6 (RBASE_QMC (wPhysChan) ) + uwRbPtr) 0UT16 (TBPTR_QMC (wPhysChan) , IN16 (TBASΞ_QMC (wPhysChan) ) + uwTbPtr) OUT16 (TMRBLR_QMC (wPhysChan) , wRxLen) ,- OUT16 (BDFLAGS_QMC (wPhysChan) 0x0000) ; OUT32 (TUPACK_QMC (wPhysChan) , 0x00000000) ; OUT32 (RPACK_QMC (wPhysChan) , 0x00000000) ; pRxBd [uwRxSeg] .pData = pubRxAddr; pTxBd [uwTxSeg] . pData = pubTxAddr ; pTxBd [uwTxSeg] . length = wTxLen; ubCurrentBondSeg [wBuffChan] [RX_BUFFER] = (Uint8 ) uwRxSeg ; ubCurrentBondSeg [wBuffChan] [TX_BUFFER] = (Uintβ ) uwTxSeg; * * Wait for frame sync alignment to a long word boundry * for(cCount=0; (cCount<MAX_LOOPS* (4+MIN_DMA_SIZE) ) && (uwRxMaster != (IN16(TCN4) & 0x3FFF) ) ; cCount++) ; if (uwRxMaster != (IN16(TCN4) & 0x3FFF) ) { wStatus = ERR0R_FS2_TIME0UT; } * Set Poll bit * if (wStatus == SUCCESSFUL) { OUT32 (ZDSTATE_QMC (wPhysChan) , 0x18000080) ; OUT32 (RSTATE_QMC (wPhysChan) , 0x39000000) ; OUT16 (TSAT_QMC (wPhysChan) , (IN16 (TSAT_QMC (wPhysChan) ) | 0x8000) ) ; 0UT16 (CHAMR_QMC (wPhysChan) , (IN16 (CHAMR_QMC (wPhysChan) ) j 0x0100) ) ; /* * Turn on R & E bits for all buffer descriptors .*/ for (wlndex=0; wIndex<BUFFER_SEGMENTS ; wlndex++) pRxBd [wlndex] . sc |= E_BIT; pTxBd [wlndex] . sc |= R_BIT; } /* * * Wait for DMA cycle before changing Max Receive Buffer Length * * * / for (cCount=0; (cCount<MAX_L00PS*8) && ( IN32 (RxPtr_QMC (wPhysChan) ) == 0); cCount++) ; 0UT16 (TMRBLR_QMC (wPhysChan) , BYTES_PER_SEGMENT) ; if( IN32 (RxPtr_QMC (wPhysChan) ) == 0) { wStatus = ERROR_DMA_TIMEOUT; } * * * Re -Enable Interrupts * * */ intr_set (uwIntMask) ; return (wStatus) ; ***************************************************************************** * Routine Name: SetupChannel () * * Description : This function initializes a channel for Bonding operation * * * * Parameters: wChannel (I) The channel number to initialize * * * * Return Value: N/A * * * *****************************************************************************/ PRIVATE void SetupChannel (Intlβ wPhysChan, Uint8 *pRxBuffer, Uint8 *pTxBuffer) { Intlβ wBuffChan; /* Buffer Channel Number */ Intlβ wlndex; /* Loop counter */ Uintlβ uwFrCount; /* Current Value for Frame counter */ Uintlβ uwIntMask; /* Current value of interrupt priority mask */ Uint32 cCount; /* Maximum loop count */ /* * * Initialize Variables * * */ wBuffChan = PhysToBuff (wPhysChan) ; /* * * Disable Interrupts during this process * * * / uwIntMask = intr_set (7) ; /* * * Clear the Valid bit in the Timeslot table. * * * / OUT16 (TSAT_QMC (wPhysChan) , (INI6 (TSAT_QMC (wPhysChan) ) & -0x8000)); * * * Wait for alignment with frame sync * * * / uwFrCount = (IN16(TCN4) + 3) & OxFFFC; for (cCount=0; (cCount<MAX_LOOPS* (4+MIN_DMA_SIZE) ) && (uwFrCount != IN16(TCN4)); cCount++) ; * * * Stop the Transmitter and Receiver * * * QMC_IssueCmd (wPhysChan, 0x01); QMC_IssueCmd (wPhysChan, 0x00); /* * * Configure the Channel Specific Parameters for Transparent Mode * * * / /* * Set the RISC State Machines * OUT32 (TSTATE_QMC (wPhysChan) , 0x38000000) ; OUT32 (ZISTATE_QMC (wPhysChan) , 0x00000100) ; /* * Set the TMRBLR (same offset as MFLR) */ OUT16 (TMRBLR_QMC (wPhysChan) , BYTES_PER_SEGMENT) ; /* * Channel Mode Register (see page 2-12) * * MODE = 0 - Transparent Mode * RD = 1 - Reverse Bit order, send MSB first * = 1 - Bit 13 is set to 1 in Transparent mode * ENT = 1 - Enable Transmit * SYNC = 0 - Not a multichannel operation * POL = 0 - Disable Polling of the transmit BDs */ OUT16 (CHAMR_QMC (wPhysChan) , 0x7000) ; /* * Setup the buffer descriptors for Bonding * /* Initialize all but the last buffer descriptor */ for (wlndex=0; wIndex<BUFFER_SEGMENTS-l; wlndex++) { SetTxBufferDesc (wPhysChan, wlndex, (I_BIT | CM_BIT) , pTxBuffer, BYTES_PER_SEGMENT) ; SetRxBufferDesc (wPhysChan, wlndex, (I_BIT | CM_BIT) , pRxBuffer, 0x0000) ; pTxBuffer += BYTES_PER_SEGMENT; pRxBuffer += BYTES_PER_SEGMENT; } /* Initialize the last buffer descriptor with the wrap bit set */ SetTxBufferDesc (wPhysChan, wlndex, (I_BIT | CM_BIT | W_BIT) , pTxBuffer, BYTES_PER_SEGMENT) ; SetRxBufferDesc (wPhysChan, wlndex, (I_BIT | CM_BIT | W_BIT) , pRxBuffer, 0x0000) ; * Re-Enable Interrupts * . intr set (uwIntMask) ; ***************************************************************************** * Routine Name: SetTxBufferDesc () * * * * Description: This function initializes a transmit buffer descriptor. * * Parameters: wChannel (I) The logical channel number * * wSegment (I) Which segment to initialize * * wControl (I) Control data for segment * * pvData (I) buffer pointer * * wLen (I) length * * * * Return Value: N/A * * * ***************************************************************************** PRIVATE void SetTxBufferDesc (Intlβ wPhysChan, Intlβ wSegment, Intl6 wControl, void *pvData, Intl6 wLen) { P_BUF_DESC pBd; /* Buf descriptor pointer */ pBd = (P_BUF_DESC) ( IN32 (MCBASE_QMC) + IN16(TBASE_QMC( WPhysChan )) + (wSegment * sizeof (BUF_DESC) ) ); pBd->pData = pvData; pBd->length = wLen; pBd->sc = wControl; } /***************************************************************************** * Routine Name: SetRxBufferDesc () * * * * Description: This function initializes a receive buffer descriptor. * * * * Parameters: wChannel (I) The logical channel number * * wSegment (I) Which segment to initialize * * wControl (I) Control data for segment * * pvData (I) buffer pointer * * wLen (I) length * * * * Return Value: N/A * * * *****************************************************************************/ PRIVATE void SetRxBufferDesc (Intlβ wPhysChan, Intlβ wSegment, Intlβ wControl, void *pvData, Intlβ wLen) { P_BUF_DESC pBd; /* Buf descriptor pointer */ pBd = (P_BUF_DESC) ( IN32 (MCBASE_QMC) + IN16 (RBASE_QMC( wPhysChan )) + (wSegment * sizeof (BUF_DESC) ) ) ; pBd->pData = pvData; pBd->length = wLen; pBd->sc = wControl; } ***************************************************************************** * Routine Name: Bond360_QMC_Interrupt ( ) * * * * Description: This routine processes interrupts for bonding channels * * * * Parameters: wPhysChan (I) This is the physical channel the * * interrupt occured on. * * ublnterruptFlags (I) This is the interrupt bit flags * * indicating the cause of the interrupt * * * * Return Value : None * * * ***************************************************************************** PUBLIC void Bond360_QMC_Interrupt (Intlβ wPhysChan, Uintβ ublnterruptFlags) { Uint16 uwSeg; Uintlβ wBuffChan; P_BUF_DESC pBd; /* * * Initailize Variables * * * / wBuffChan = PhysToBuff (wPhysChan) ; /* * * Check For Receive Frame Complete * * * / if ( ublnterruptFlags & QMC_INTR_RXB) { pBd = (P_BUF_DESC) (IN32 (MCBASEjDMC) +IN16 (RBASE_QMC (wPhysChan) )) ; uwSeg = ubCurrentBondSeg [wBuffChan] [RX_BUFFER] ; UbCurrentBondSeg [wBuffChan] [RX_BUFFER] = (uwSeg+1) % BUFFER_SEGMENTS ; if ( pubUpdateAddr [wBuffChan] [RX_BUFFER] != NULL ) { pBd[uwSeg] .pData = pubUpdateAddr [wBuffChan] [RX_BUFFER] ; pubUpdateAddr [wBuffChan] [RX_BUFFER] = NULL; } } * * * Check For Transmit Frame Complete * * * / if ( ublnterruptFlags & QMC_INTR_TXB) { pBd = (P_BUF_DESC) (IN32 (MCBASE_QMC) +IN16 (TBASE_QMC (wPhysChan) )) ; uwSeg = ubCurrentBondSeg [wBuffChan] [TX_BUFFER] ; ubCurrentBondSeg [wBuffChan] [TX_BUFFER] = (uwSeg+1) % BUFFER_SEGMENTS ; if ( pubUpdateAddr [wBuffChan] [TX_BUFFER] != NULL ) { pBdfuwSeg] .pData = pubUpdateAddr [wBuffChan] [TX_BUFFER] ; pBd [uwSeg] . length = BYTES_PER_SEGMENT; pubUpdateAddr [wBuffChan] [TX_BUFFER] = NULL; } } /* * * Check For Other Interrupts * * */ if ( ublnterruptFlags & - (QMC_INTR_TXB | QMC_INTR_RXB) ) { } } /***************************************************************************** * Routine Name: InitializeFsCounter () * * * * Description: Timer 4 on the 360 is connected to frame sync. Initialize * * it to count frame sync pulses. We will use this as our * * master counter to which all bonding data is synchronized * * * * Parameters : None * * * * Return Value: N/A * * * ***************************************************************************** PRIVATE void InitializeFsCounter (void) { /' Configure Timer 4 to count frame sync pulses * .*/ OUT16(TGCR, IN16(TGCR) & (TGCR_MASK1 | TGCR_MASK2 | TGCR_MASK3) OUT16 (TMR4, (TMR_CE_DISABLE | TMR_OM_PULSE | TMR_ORI_DISABLE TMR_FFR_FREERUN j TMR_ICLK_TIN j TMR_GE_IGNORE ) OUT16(TCN4, 0x0000) ; /* Set Initial Count */ OUT16 (TRR4, 0x0000) ; /* Set Reference */ OUT16(TER4, OxFFFF) ; /* Clear Events */ OUT16(TGCR, INlβ(TGCR) I TGCR RST4); /* Enable the timer */ / * * * * * ** *** * ******* * * * * *** *** * ********* * *** * * ** ********** * * * ** * *************** * * * File: bndstate.c Date: April 20,1996 * * * * (C) Copyright 1997 by VideoServer Inc. * * All Rights Reserved * * Engineer: Dave Oliveira * * * * Description: The file contains code to support Bonding on the * * 68MH360 * * * *****************************************************************************/ /***************************************************************************** * Include Files * *****************************************************************************/ #include <mriext.h> #include <string.h> #include "flags.h" #include "codestd.h" #include "compiler. h" #include "tune.h" #include "gendef.h" #include "exec.h" #include "vrtx.h" #include "vrtxil.h" #include "devkit.h" #include "amdev.h" #include "imux_db.h" #include "bndqmc . h" #include "mc68360.h" ttinclude "m360.h" #include "hd_proto.h" #include "hituser.h" ***************************************************************************** * Local Defines * ***************************************************************************** #define MIN_TX_OFFSET 8 #define ALIGN_DELAY 88L /***************************************************************************** * External Data * *****************************************************************************/ extern Uint32 BondBuffer [MAX_BOND_CHANNELS] [MAX_DIR] [MAX_BUFFER_LONGS] ; extern ChannelData ChanData [MAX_BOND_CHANNELS] ; extern SessionData Sessions [MAX_BOND_SESSIONS] ; extern CPU_DATA CPU_Data [MAX_CPU] extern volatile Uint32 TimerTicks extern BondingParms BondingData [] extern Uint32 Where [] ; /***************************************************************************** * Global Data * **** ********* ** ***************** * ********************************** . ********* PRIVATE MAILBOX *pMbox; /** *** *** ****** * ****** * ****************************** * ************************ * Function Prototypes * **** ********** * ******** * ******************** ** ********* * ****** ** ************* / PRIVATE void ProcessBondTimer (void) ; PRIVATE void Bond360AlignState (UintS ubSession); PRIVATE void AlignRemoteReady (Uintβ ubSession) ; PRIVATE void AlignDelay (UintS ubSession); PRIVATE void AlignClearA(Uint8 ubSession); PRIVATE Intl6 ComputeAlignment (Uint8 ubSession) ; PRIVATE Intlβ PostAlignComplete (Uint8 ubSession, Intlβ wMaxDelay, Uintδ ubRetCode); PRIVATE void SetSessionTimer (Uint8 ubSession, Uint32 ulTicks); PRIVATE Boolean CheckSessionTimer (UintS ubSession); **** *** * ****** ** ******* ******* ** ** * *************** ** ****** ** ** ******* ** *** *** * External Function Prototypes * *** *************************************** **** ***** * ******* * *** * ************ */ extern IMUXMsg *GetIMUXblock(Intl6 * ret_code) ; extern void ReleaselMUXblock (IMUXMsg *pMsg) ,- extern MAILBOX *QID_to_MTEX(Intlβ qid, Intlβ *errp) ; extern void _LOADDS trap (Intlβ, Intlβ, void FAR *) ; /** * *** *** ******** ******** *** * ******* ********* * * * *********** * ** * ****** * ***** * * * Routine Name: Bond360_Tx_Task() * * * * Description: This function implements the "transmit" tasl for the * * 68MH360 implementation of bonding, i.e. no DSP. * * * * Parameters : None * * * * Return Value: N/A * * * ** ** * * * ** ******* * * **************** * **** **** **** ******** * *** ** ********* * ** * * **/ PUBLIC void Bond360_Tx_Task(void) { Intlβ wStatus; IMUXMsg *pMsg; TIMER_BLOCK *pTID; SEQNO SeqNum; EXT_MEM ExtMem; /* * * Start the periodic Timer * * * pMbox = QID_tθ_MTEX (IMUX_Tx_Queue, &wStatUS) ; if (wStatus == Ret_Ok ) { pMsg = GetlMUXbloc &wStatus) ; if (wStatus == Ret_Ok) { memset (pMsg, 0, sizeof (IMUXMsg) ) ; pMsg->ubPrimcode = BondingTi erTick; ExtMem.Addr = (BYTE *)pMsg; SetTimer (pMbox, &ExtMem, 5, &pTID, SSeqNum, 0); } /* * * Process the Message * . * / for(;;) { pMsg = (IMUXMsg *) sc_qpend (IMUX_Tx_Queue , 0L, &wStatus) ; if( wStatus == Ret_Ok ) { switch (pMsg->ubPrimcode) { case BondingTimerTick: ProcessBondTimer () ; SetTimer (pMbox, &ExtMem, 5, &pTID, &SeqNum, 0) ; break; case BondingDspMsg: ProcessBondMsg (pMsg) ; ReleaseΙMUXblock(pMsg) ; break; default : break; else { trap(SC_QPEND, wStatus, NULL); } } } ***************************************************************************** * Routine Name: ProcessBondTimer () * * * * Description: This function is called when the periodic bonding timer * * expires. We process the state machines here. * * * * Parameters : None * * * * Return Value: N/A * * * ***************************************************************************** PRIVATE void ProcessBondTimer (void) { Intlβ wBuffChan; Uint8 ubSession; /* * * Process the Channel State Machines * * */ for (wBuffChan=0 ; wBuf f Chan<MAX_BOND_CHANNELS ; wBuffChan++ ) { Bond360ChannelState (Buf fToPhys (wBuffChan) ) ; } * Process the Aligner State Machines * . for (ubSession=0; ubSession<MAX_BOND_SESSIONS; ubSession++) { Bond360AlignState (ubSession) ; } } ***************************************************************************** * Routine Name: Bond360AlignState () * * * * Description: This function is called when the periodic bonding timer * * expires. We process the alignment state machine here * * * * Parameters: ubSession (I) This is the Session number of the session * * to process . * * * * Return Value: N/A * * * *****************************************************************************/ PRIVATE void Bond360AlignState (UintS ubSession) { switch (Sessions [ubSession] .ubState) { case AS_NULL: break; case AS_RMT_STATE : AlignRemoteReady (ubSession) ; break; case AS_DELAY: AlignDelay (ubSession) ; break; case AS_CLEAR_A: AlignClearA (ubSession) ; break; case AS_ALIGNED: break; default : break; } } ***************************************************************************** * Routine Name: AlignRemoteReady () * * * * Description: This function checks if all of the channels for a session * * are in the proper state and the RI bit of the received * * Info channels are set. * * * * Parameters: ubSession (I) This is the Session number of the session * to process. * Return Value: N/A *****************************************************************************/ PRIVATE void AlignRemoteReady (Uint8 ubSession) { Intlβ wBuffChan; Boolean boReady; * * * Check if all of the channels in this session are ready * * * , boReady = TRUE; for(wBuffChan=0; wBuffChan<MAX_BOND_CHANNELS ; wBuffChan++) { if ( (ChanData [wBuffChan] .ubSession == ubSession) && (ChanData [wBuffChan] .ubChanState != CS_NULL) ) { if ( (ChanData [wBuffChan] .ubChanState != CS_LEARNING) || ( (ChanData [wBuffChan] .ubIC_Frame [0] [6] & 0x40) == 0)) { boReady = FALSE; break; } * * If the Channels Are Ready, Clear the A Bit and change the state * if (boReady) { for(wBuffChan=0; wBuffChan<MAX_BOND_CHANNELS; wBuffChan++) { if ( (ChanData [wBuffChan] .ubSession == ubSession) && (ChanData [wBuffChan] .ubChanState != CS_NULL) ) { Set_A_Bit (BuffToPhys (wBuffChan) , 0) ; } } Sessions [ubSession] .ubState = AS_DELAY; SetSessionTimer (ubSession, ALIGN DELAY) ; / ** ** ** ***** ** *** * *** ******* ***** *** **** ****************** * ******************* * Routine Name: AlignDelayO * * * * Description: This function checks if delay timer has expired. * * * * Parameters: ubSession (I) This is the Session number of the session * * to process. * * * * Return Value: N/A * * * *****************************************************************************/ PRIVATE void AlignDelay (UintS ubSession) { /* * * Check if the timer has expired * * * if ( CheckSessionTimer (ubSession) ) { Sessions [ubSession] .ubState = AS_CLEAR_A; AlignClearA (ubSession) ; } } /***************************************************************************** * Routine Name: AlignClearA () * * * * Description: This function checks if the inbound A bit is cleared or the * * frame is nolonger in sync. * * * * Parameters: ubSession (I) This is the Session number of the session * * to process. * * * * Return Value: N/A * * * *****************************************************************************/ PRIVATE void AlignClearA (UintS ubSession) { Intlβ wBuffChan; Intl6 wMaxDelay Boolean boReady / * * * Check if all the channels have the Rx A bit cleared or have lost sync * * */ boReady = TRUE; #if 0 for(wBuffChan=0; wBuffChan<MAX_BOND_CHANNELS; wBuffChan++) { if ( (ChanData [wBuffChan] .ubSession == ubSession) && (ChanData [wBuffChan] .ubChanState != CS_NULL) ) { if ( (ChanData [wBuffChan] .ubMF_State != 0) || ( (ChanData [wBuffChan] .ubMF_RxCrc & 0x40) != 0) ) { boReady = FALSE; break; } } } #else wBuffChan = wBuffChan; #endif /* * * If all the channels are ready do the alignment * * * / if (boReady) { wMaxDelay = ComputeAlignment (ubSession) ; PostAlignComplete (ubSession, wMaxDelay, 0) ; Sessions [ubSession] .ubState = AS_ALIGNED; } } /***************************************************************************** * Routine Name: AlignChannels () * * * * Description: This function computes the frame alignment * * * * Parameters: ubSession (I) This is the Session number of the session * * to process. * * * * Return Value: N/A * * * A****************************************************************************/ PRIVATE Intlβ ComputeAlignment (Uint8 ubSession) { Intlβ wBuffChan; Intl6 wMaxDelay; Intlβ wMinDiff; Intlβ wDiff; Intlβ wStart; Intl6 wBase; Intl6 wFrameO; Uint8 ubOrder [MAX_BOND_CHANNELS] ; Boolean boTDMbus; /* * * Find a channel in the session to use as a start channel * * * for(wBuffChan=0; wBuffChan<MAX_BOND_CHANNELS; wBuffChan++) { if ( (ChanData [wBuffChan] .ubSession == ubSession) && (ChanData [wBuffChan] .ubChanState != CS_NULL) ) { wStart = wBuffChan; break; } * Find the Base Channel, i.e. Channel with longest delay * . * / wMinDiff = 32000; wFrameO = ChanData [wStart] .wMF_FrameOIndex - ChanData [wStart] . ubRxChanld; for(wBuffChan=0; wBuffChan<MAX_BOND_CHANNELS ; wBuffChan++) { if ( (ChanData [wBuffChan] .ubSession == ubSession) && (ChanData [wBuffChan] .ubChanState != CS_NULL) ) { wDiff = wFrameO - (ChanData [wBuffChan] .wMF_Frame0Index - ChanData [wBuffChan] .ubRxChanld) ; if( abs(wDiff) > 8192 ) { wDiff A = OxCOOO; } if (wDiff < wMinDiff) { wMinDiff = wDiff; wBase = wBuffChan; } } } /* * Compute the Delay Offsets for Each Channel * * wMaxDelay = -32000; wFrameO = ChanData [wBase] .wMF_FrameOIndex - ChanData [wBase] . ubRxChanld; for(wBuffChan=0; wBuffChan<MAX_BOND_CHANNELS; wBuffChan++) { if ( (ChanData [wBuffChan] .ubSession == ubSession) && (ChanData [wBuffChan] .ubChanState != CS_NULL) ) { ChanData [wBuffChan] .wOffset = wFrameO - (ChanData [wBuffChan] .wMF_FrameOIndex - ChanData [wBuffChan] .ubRxChanld); if ( abs (ChanData [wBuffChan] .wOffset) > 8192 ) { ChanData [wBuffChan] .wOffset Λ = OxCOOO; } wMaxDelay = max (wMaxDelay, ChanData [wBuffChan] .wOffset) ; } * Determine the Channel Order * boTDMbus = (BondingData [ubSession] .ubBChanProtocol != HOST TRANSPAR) ; if(boTDMbus) { ChannelOrder (ubOrder, ubSession) ; * Re-Start Each Channel with the computed Delays J if (boTDMbus) { for (wBuffChan=0, wBase = 0; wBuffChan<MAX_BOND_CHANNELS; wBuffChan++) { if ( (ChanData [wBuffChan] .ubSession == ubSession) && (ChanData [wBuffChan] .ubChanState != CS_NULL) ) { wStart = ubOrder [wBase++] ; StartChannel (BuffToPhys (wBuffChan) , (Uint8 *) BondBuffer [wBuffChan] [RX_BUFFER] , (Uint8 *) BondBuffer [wStart] [RX_BUFFER] , ChanData [wStart] . wOffset+MIN_TX_OFFSET) ; ChanData [wBuffChan] .ubChanState = CS_ALIGN; } } } * For HBDT calls, Clear the Tx buffer and change the channel state * * */ if (! boTDMbus) { for (wBuffChan=0, wBase = 0; wBuffChan<MAX_BOND_CHANNELS; wBuffChan++) { if ( (ChanData [wBuffChan] .ubSession == ubSession) && (ChanData [wBuffChan] .ubChanState != CS_NULL) ) { memset ( (Uintβ *)BondBuffer [wBuffChan] [TX_BUFFER] , OxFF, BUFFER_BYTES) ; ChanData [wBuffChan] .ubChanState = CS_ALIGN; } } } return (wMaxDelay) ; } / **** * ****** * *** ***** ***** * ***** ** * * ********** ** *** * ***** ******** **** * * * ***** * * Routine Name: ChannelOrder () * * * * Description: This function determines the channel order * * * * Parameters: pubOrder (I) The array to place the ordered list into. * * ubSession (I) The session number of the session to process * * * * Return Value : None * * * **** ** **** ********** ** * ********* ***** **** * *** **** *** * ***** ** * ** ** *** * ***** ** * / PUBLIC void ChannelOrder (Uintδ *pubOrder, Uint8 ubSession) { Intlβ wBuffChan; Intlβ wBase; * * * Initialize the Order Array * * * / memset (pubOrder, -1, MAX_BOND_CHANNELS) ; / * * * Fill in the Channel Order Array * * * / for(wBuffChan=0; wBuffChan<MAX_BOND_CHANNELΞ; wBuffChan++) { if ( (ChanData [wBuffChan] .ubSession == ubSession) && (ChanData [wBuffChan] .ubChanState != CS_NULL) ) { pubOrder [ ChanData [wBuffChan] .ubRxChanld - 1 ] = wBuffChan; } } /* * * Compress the Channel Order Array * * */ for (wBase=0 ; wBase<MAX_BOND_CHANNELS ; wBase++) { if ( pubOrder [wBase] == OxFF ) { memcpy ( &p ubOrder [wBase] , kpubOrder [wBase+1] , (MAX_BOND_CHANNELS - wBase ) - 1 ) ; } } } /***************************************************************************** * Routine Name: PostAlignComplete () * * * * Description: This function sends an Align Complete Message * * * * Parameters: ubSession (I) The session number of the session to process * * wMaxDelay (I) The computed maximum relative delay * * * * Return Value: RET_OK The message was sent * * * ***************************************************************************** PRIVATE Intl6 PostAlignComplete (Uint8 ubSession, Intl6 wMaxDelay, Uint8 ubRetCode) { Intlβ wPhysChan; Intl6 wBuffChan; Intlβ wStatus; IMUXMsg *pMsg; * * * Get Block to send message in * * */ pMsg = GetΙMUXblock(SwStatus) ; if ( wStatus == Ret_0k ) { memset (pMsg, 0, sizeof (IMUXMsg) ) ; wPhysChan = Sessions [ubSession] .wBaseChan; wBuffChan = PhysToBuff (wPhysChan) ; * * * Fill in the Data for the Message * * * / pMsg->IMUXData.DspMsg. ubLength = 22; pMsg->IMUXData.DspMsg.ubPrimcode = MCA_AlignComplete; pMsg->IMUXData. DspMsg.ubDsO = wPhysChan; pMsg->IMUXData.DspMsg.ublnterface = ChanData [wBuffChan] .ublnterface; pMsg->IMUXData.DspMsg. ubSession = ubSession; pMsg->IMUXData.DspMsg.ubRetcode = ubRetCode; pMsg->ubPrimcode = BondingDspMsg; pMsg->ubSession = ubSession; pMsg->ubDspTSlot = wPhysChan; pMsg->ubStartDs0 = PhysToBuff (wPhysChan) & 0x01; pMsg->ubInterface = PhysToBuff (wPhysChan) >> 1; pMsg->IMUXData.DspMsg. data.uwMaxDelay = (Uintlβ) SwapWord (wMaxDelay) ; * * * Send the Message to the Bonding Task * * * sc_qpost (IMUX_Rx_Queue, (Uint8 *)pMsg, &wStatus) ; if ( wStatus != Ret_0k ) { ReleaseΙMUXblock(pMsg) ; trap(SC_QPOST, wStatus, NULL) ; } return (wStatus) ; /A**************************************************************************** * Routine Name: SetSessionTimer () * * * * Description: This function sets the timer for a session to a new * * expiration value . * * Parameters: ubSession (I) The session number of the session to process * * ulTicks (I) The number of timerticks to wait * * * * Return Value: N/A * * * ***************************************************************************** PRIVATE void SetSessionTimer (UintS ubSession, Uint32 ulTicks) { if ( ulTicks != 0 ) { ulTicks = TimerTicks + ulTicks; if (ulTicks == 0) { ulTicks = 1; } } Sessions [ubSession] .ulTimer = ulTicks; } /***************************************************************************** * Routine Name: CheckSessionTimer () * * * * Description: This function checks if the timer for a session has expired * * * * Parameters: ubSession (I) The session number of the session to process * * * * Return Value: TRUE The timer has expired * * FALSE The timer is still running * * * *****************************************************************************/ PRIVATE Boolean CheckSessionTimer (UintS ubSession) { * * * The timer has expired if the all of following are true: * * 1. The timer is active, i.e. the timer value is not zero * * 2. The timer value and the current tick count both have the same * * msb . * * 3. The Tick count is greater than or equal to the timer value. * * * / return ( (Boolean) ( (Sessions [ubSession] .ulTimer != 0) && (((TimerTicks A Sessions [ubSession] .ulTimer) & 0x80000000) == 0) && (TimerTicks >= Sessions [ubSession] .ulTimer) ) ) ; } ***************************************************************************** * Routine Name: Bond360ChannelData () * * * * Description: This function formats and displays the ChannelData for a * * channel. * * * * Parameters: wChannel (I) The channel number of the channel to process * * * * Return Value : None * * * *****************************************************************************/ PUBLIC Bond360ChannelData(Intl6 wPhysChan) { Intl6 wFrame; Intl6 wlndex; Intlβ wBuffChan; wBuffChan = PhysToBuff (wPhysChan) ; printf ("Channel : %d %p\n", wPhysChan, &ChanData [wBuffChan] ) ; printfC ChanState : %d Session: %d Interface: %d DSO: %d\n", ChanData [wBuffChan] .ubChanState, ChanData [wBuffChan] .ubSession, ChanData [wBuffChan] .ublnterface, ChanData [wBuffChan] .ubDSO) ; printf (" Offset: %4d TxChanld: %2d RxChanld: %2d Timer: %08lX\n\n", ChanData [wBuffChan] .wOffset, ChanData [wBuffChan] .ubTxChanld, ChanData [wBuffChan] .ubRxChanld, ChanData [wBuffChan] .ulTimer) ; printf (" MF_State: %d Index: %04X Frame 0: %04X FrameCount : %02X\n", ChanData [wBuffChan] .ubMF_State, ChanData [wBuffChan] .wMF_Index, ChanData [wBuffChan] .wMF_FrameOIndex, ChanData [wBuffChan] .ubMF_FrameCounter) ; printf (" FawLoss: %d FcLoss : %d Infolndex: %2d RxCrc: %02X\n\n", ChanData [wBuffChan] .ubMF_FawLoss, ChanData [wBuffChan] .ubMF_FcLoss, ChanData [wBuffChan] .ubMF_InfoIndex, ChanData [wBuffChan] .ubMF_RxCrc) ; printfC IC_State: %d Index: %04X Bufflndex: %d Ticks: %081X\n", ChanData [wBuffChan] .ubIC_State, ChanData [wBuffChan] .wIC_Index, ChanData [wBuffChan] .ubIC_BuffIndex, TimerTicks) ; for (wFrame=0; wFrame<3; wFrame++) { printfC Frame [%d] : ", wFrame); for (wlndex=0; wlndex<16; wlndex++) { printfC %02X", ChanData [wBuffChan] .ubIC_Frame [wFrame] [wlndex]); } printf ("\n") ; } printfC Last: "); for (wlndex=0 ,- wlndex<16; wlndex++) { printf (" %02X" , ChanData [wBuffChan] .ubIC_Last [wlndex] ) ; } printf ("\n") ; } /***************************************************************************** * Routine Name: Bond360SessionData () * * * * Description: This function formats and displays the SessionData for a * * session. * * * * Parameters: ubSession (I) The session number of the session to process * * * * Return Value: None * * * *****************************************************************************/ PUBLIC void Bond360SessionData(Uint8 ubSession) { printf ("Session: %d State: %d BaseChan: %d Timer: %081X\n", ubSession, Sessions [ubSession] .ubState, Sessions [ubSession] .wBaseChan, Sessions [ubSession] .ulTimer) ; /***************************************************************************** * * * File: bndsync.c Date: April 20,1996 * * * * (C) Copyright 1997 by VideoServer Inc. * * All Rights Reserved * * Engineer: Dave Oliveira * * * * Description: The file contains code to support Bonding on the * * 68MH360 * * * *****************************************************************************/ #define USE_ASM /***************************************************************************** * Include Files * *****************************************************************************/ #include <string.h> #include "codestd.h" #include "flags.h" #include "compiler. h" #include "gendef.h" #include "tune.h" ttinclude "lif.h" #include "vrtx.h" #include "vrtxil.h" #include "devkit.h" #include "imux_db.h" #include "bndqmc . h" #include "mc68360.h" ttinclude "m360.h" /***************************************************************************** * External Data * *****************************************************************************/ extern Uint32 BondBuffer [MAX_BOND_CHANNELS] [MAX_DIR] [MAX_BUFFER_LONGS] ; extern CPU_DATA CPU_Data [MAX_CPU] ; extern Uint32 Where!]; /***************************************************************************** * Global Data * *****************************************************************************/ ChannelData ChanData [MAX_BOND_CHANNELS] ; SessionData Sessions [MAX_BOND_SESSIONS] ; /***************************************************************************** * External Function Prototypes * *****************************************************************************/ extern IMUXMsg *GetIMUXblock (Intlβ * ret_code) ; extern void ReleaselMUXblock (IMUXMsg *pMsg) ; extern void _L0ADDS trap (Intlβ, Intlβ, void FAR *) ; extern void *ret_addr (void) ; /***************************************************************************** * Routine Name: IC_Sync() * * * * Description: This routine is the state machine for synchronizing to * * the information channel frame. * * * * Parameters: wPhysChan (I) Channel number to operate on * * * * Return Value: N/A * * * ***************************************************************************** PUBLIC Boolean IC_Sync (Intlβ wPhysChan) { register Intlβ wlndex; register Intl6 wCpylndex; register Intlδ wCpyCount; register Intlβ wBytesLeft; register UintS *pubBuffer; register ChannelData *pCdPtr; register Uint8 *pubFrame; Uint8 *pubEndPtr; Boolean boMsgSent; * * * Initialize Variables * * * / pubBuffer = (Uintβ * ) BondBuffer [PhysToBuff (wPhysChan) ] [RX_BUFFER] ; pCdPtr = &ChanData [PhysToBuff (wPhysChan) ] ; wlndex = pCdPtr- >wIC_Index; boMsgSent = FALSE ; * * * Compute Number of Bytes to Scan * * * pubEndPtr = (Uint 8 * ) ( IN32 (RxPtr_QMC (wPhysChan) ) ) ; if ( pubEndPtr < &pubBuf fer [wlndex] ) { wBytesLeft = (BUFFER_BYTES - wlndex) + (pubEndPtr - pubBuffer) ; } else { wBytesLeft = pubEndPtr - &pubBuf fer [wlndex] ; } * * * Scan the Buffer * * * / while ( wBytesLeft > 0 ) { switch ( pCdPtr->ubIC_State) { /* * * No Sync Bytes have been found, Check for FAW * * * / case IC_N0SYNC: while ( wBytesLeft > 0 ) { if (pubBuffer [wlndex] == IFAW) { pCdPtr->ubIC_State = IC_IFAW_FOUND; wlndex = (wlndex+l) & 0x3FFF; wBytesLeft-- ; break; }• wlndex = (wlndex+l) & 0x3FFF; wBytesLeft--; } break; /* * An IFAW has been for Check if Bit 1 is set * case IC_IFAW_FOUND: if ( (pubBuffer [wlndex] & 0x81) == 0x81 ) { pCdPtr->ubIC_State = IC_BITl_FOUND; wlndex = (wlndex+l) & 0x3FFF; wBytesLeft-- ; break; } else { pCdPtr->ubIC_State = IC_NOSYNC; break; } break; /* * Check if a IFAW appears at the end of this frame * "/ case IC_BIT1_F0UND: if (wBytesLeft >= 14 ) { if (pubBuffer [ (wlndex+14) & 0x3FFFF ] == IFAW) { /* * We are insync, copy the Info channel data */ pubFrame = pCdPtr->ubIC_Frame [pCdPtr->ubIC_BuffIndex] , • for (wCpyCount=0, wCpyIndex=0; wCpyCount<16; wCpyCount++) { *pubFrame++ = pubBuffer [wCpylndex] ; wCpylndex = (wCpylndex+l) & 0x3FFF; } pCdPtr->ubIC_State = IC_INSYNC; wlndex = (wlndex+14) & 0x3FFF; wBytesLeft -= 14; boMsgSent |= CheckNewMessage (wPhysChan, MCA_InitSync) ; } else { pCdPtr->ubIC_State = IC_N0SYNC; } } else { wBytesLeft = 0 ; } break; /* * * We have declared sync, look for Info Channel Msgs * * * / case IC_INSYNC: case IC_NO_IFAW_l : case IC_NO_IFAW_2 : while (wBytesLeft > 16) { if ( pubBuffer [wlndex] != IFAW ) { pCdPtr- >ubIC_State++ ; if( pCdPtr->ubIC_State >= IC_NO_IFAW_3) { pCdPtr->ubIC_State = IC_NOSYNC; break; } } pubFrame = pCdPtr->ubIC_Frame [pCdPtr->ubIC_BuffIndex] ; wCpylndex = wlndex; for (wCpyCount=0; wCpyCount<16; wCpyCount++) { *pubFrame++ = pubBuffer [wCpylndex] ; wCpylndex = (wCpylndex+l) & 0x3FFF; } boMsgSent |= CheckNewMessage (wPhysChan, MCA_InitSync) ; wlndex = (wlndex+16) & 0x3FFF; wBytesLeft -= 16; } wBytesLeft = 0 ; break; default : /* We Should NEVER Get Here */ break; } } pCdPtr->wIC_Index = wlndex; return (boMsgSent) ; } /*** * ** * * * *** * * * * * ***** * * * * * * * * * * * ***** * * * * * * * * * * * **** * * * * * * ** * * * * * * ********* * * Routine Name: MF_Sync() * * * * Description: This routine is the state machine for synchronizing to * * the multi-frame signal. * * * * Parameters: wPhysChan (I) Channel number to operate on * * * * Return Value: N/A * * * ***************************************************************************** PUBLIC Boolean MF_Sync (Intlβ wPhysChan) { register Intlβ wlndex; register Intlβ wBytesLeft; register Uint8 *pubBuffer; register ChannelData *pCdPtr; Uint8 *pubEndPtr; Uint8 ubNextFc; Boolean boMsgSent; /* * * Initialize Variables * * */ pubBuffer = (Uintβ *) BondBuffer [PhysToBuff (wPhysChan) ] [RX_BUFFER] ; pCdPtr = &ChanData [PhysToBuff (wPhysChan) ] ; wlndex = pCdPtr->wMF_Index; boMsgSent = FALSE; /* * * Compute Number of Bytes to Scan * * * / pubEndPtr = (Uintβ *) (IN32 (RxPtr_QMC (wPhysChan) )) ; if ( pubEndPtr < &pubBuffer [wlndex] ) { wBytesLeft = (BUFFER_BYTES - wlndex) + (pubEndPtr - pubBuffer) ; } else { wBytesLeft = pubEndPtr - &pubBuf f er [wlndex] ; } / * * * Scan the Buffer * * * / while ( wBytesLeft > 0 ) { switch ( pCdPtr->ubMF_State) { * * * No Sync Bytes have been found, Check for FAW * * * / case MF_N0SYNC: while ( wBytesLeft > 0 ) { if (pubBuffer [wlndex] == FAW) { pCdPtr->ubMF_State = MF_FAW_FOUND; wlndex = (wlndex+l) & 0x3FFF; wBytesLeft--; break; } wlndex = (wlndex+l) & 0x3FFF; wBytesLeft-- ; } break; /* * * An FAW has been for Check if Bit 1 is set * * * / case MF_FAW_FOUND : if (wBytesLeft >= 128 ) { if( (pubBuffer [ (wIndex+127) & 0x3FFFF ] & 0x81) == 0x81) { pCdPtr->ubMF_State = MF_BIT1_F0UND; pCdPtr->ubMF_FrameCounter = pubBuffer [ (wIndex+127) & x3FFFF] wlndex = (wIndex+127) & 0x3FFF; wBytesLeft -= 127; break; } else { pCdPtr->ubMF_State = MF_NOSYNC; break; } } else { wBytesLeft = 0; } break; /* * Check if a FAW appears at the end of this frame * case MF_BIT1_F0UND: if (wBytesLeft >= 128 ) { if ( pubBuffer! (wlndex+128) & 0x3FFFF ] == FAW) { pCdPtr->ubMF_State = MF_INSYNC_DATA_1 ; pCdPtr->ubMF_FawLoss = 0; pCdPtr->ubMF_FcLoss = 0; pCdPtr->ubMF_InfoIndex = 0; pCdPtr->wMF_FrameOSample = OxFFFF; wlndex = (wlndex+128) & 0x3FFF; wBytesLeft -= 128; Set_A_Bit (wPhysChan, 1) ; break; } else { pCdPtr->ubMF_State = MF_NOSYNC; break; } else { wBytesLeft = 0; } break; * Frame Sync Aquired, Looking for FAW * case MF_INSYNC_DATA_0 : if (wBytesLeft >= 64 ) { pCdPtr->ubMF_State = MF_INSYNC_DATA_1 ; wlndex = (wIndex+64) & 0x3FFF; wBytesLeft -= 64; if ( pubBuffer [wlndex] != FAW) { pCdPtr- >ubMF_FawLoss++ ; if (pCdPtr->ubMF_FawLoss >= 3) { pCdPtr->ubMF_State = MF_NOSYNC; Set_A_Bit (wPhysChan, 0); } else { pCdPtr->ubMF_FawLoss = 0 ; } } else { wBytesLeft = 0; } break; /* * * Frame Sync Aquired, Looking for Info Channel * * * / case MF_INSYNC_DATA_1 : if (wBytesLeft >= 64 ) { pCdPtr->ubMF_State = MF_INSYNC_DATA_2 ; wlndex = (wIndex+64) & 0x3FFF; wBytesLeft -= 64; pCdPtr->ubIC_Frame [pCdPtr->ubIC_BuffIndex] [pCdPtr->ubMF_InfoIndex++] = pubBuffer [wlndex] ; pCdPtr->ubMF_InfoIndex &= OxOF; if ( pubBuffer [wlndex] == IFAW ) { pCdPtr->ubMF_InfoIndex = 1; pCdPtr->ubIC_Frame [pCdPtr->ubIC_BuffIndex] [0] = IFAW; if ( CheckNewMessage (wPhysChan, MCA_InSync) ) { boMsgSent |= TRUE; pCdPtr->wMF_FrameOIndex = pCdPtr->wMF_FrameOSample; } } } else { wBytesLeft = 0; } break; / * . * Frame Sync Aquired, Looking for Frame Counter * * * / case MF_INSYNC_DATA_2 : if (wBytesLeft >= 64 ) { pCdPtr->ubMF_State = MF_INSYNC_DATA_3 ; wlndex = (wIndex+64) & 0x3FFF; wBytesLeft -= 64; ubNextFc = pCdPtr->ubMF_FrameCounter+2 | 0x81; pCdPtr->ubMF_FrameCounter = pubBuffer [wlndex] ; if ( ubNextFc != pubBuffer [wlndex] ) { pCdPtr->ubMF_FcLoss++; if (pCdPtr->ubMF_FcLoss >= 3) { pCdPtr->ubMF_State = MF_NOSYNC; Set_A_Bit (wPhysChan, 0); } } else { if (pCdPtr->wMF_Frame0Sample == OxFFFF) { pCdPtr->wMF_FrameOSample = ( (wlndex-128) - (( (pubBuffer [wlndex] >> 1) & 0x3F) * 256)) & 0x3FFF; } pCdPtr->ubMF_FcLoss = 0 ; Set_RI_Bit (wPhysChan, 1); } } else { wBytesLeft = 0 ; } break; / * . * Frame Sync Aquired, Looking for CRC * * * / case MF_INSYNC_DATA_3 : if (wBytesLeft >= 64 ) { wBytesLeft -= 64; wlndex = (wIndex+64) & 0x3FFF; pCdPtr->ubMF_State = MF_INSYNC_DATA_0 ; pCdPtr->ubMF_RxCrc = pubBuffer [wlndex] ; } else { wBytesLeft = 0; } break; default: /* We Should NEVER Get Here */ break; } } pCdPtr->wMF_Index = wlndex; return (boMsgSent) ; } ***************************************************************************** * Routine Name: CheckNewMessage () * * * * Description: This function checks if a vaild new message has been * * received on the info channel . * * * * Parameters: wPhysChan (I) Channel number to operate on * * ubMsgld (I) Prim Code to send if a Info Chan Msg found * * * * Return Value: N/A * * * A****************************************************************************/ PUBLIC Boolean CheckNewMessage (Intlβ wPhysChan, Uint8 ubMsgld) { register Boolean boSend; register Intlβ wlndex; register Uintδ *pubFrame; register Uintδ *pubLast; register UintS *pubVote; ChannelData *pCdPtr; Uintδ ubVote [16] ; Initialize Variables boSend = TRUE; pCdPtr = &ChanData [PhysToBuff (wPhysChan) ] wlndex = wlndex; /*- * Is this a valid frame? pubFrame = pCdPtr->ubIC_Frame [pCdPtr->ubIC_BuffIndex] ; pubLast = pCdPtr->ubIC_Last; /* Notes: * 1. We cast the pointer to longs and the do the mask and compare * with longs. This code is ugly but it is about 8 times faster * than the equivalent loop . * 2. This code only works on Big Endian machines . For Little Endian machines, byte swap the masks and compare values. * if ( ( ( ( ( (Uint32 *) pubFrame) [0] ) & 0xFF818181) = 0X7F818181) ( ( ( ( (Uint32 *) pubFrame) [1] ) & 0x81818181) = 0x81818181) ( ( ( ( (Uint32 *) pubFrame) [2] ) & 0X81Ξ1E1E1) = 0X81E1E1E1) ( ( ( ( (Uint32 *) pubFrame) [3] ) & OxElElElEl) = OxElElElEl) ) { boSend = FALSE; /*- * Is this message the same as the last? * . if ( boSend ) { pubLast = pCdPtr->ubIC_Last; /* Note: We cast the pointers to longs and the do the and compare with longs. This code is ugly but it is about 3 times faster than the equivalent loop . The state of the RI bit is ignored. */ if ( ( ( ( (Uint32 *) pubFrame) [0] ) = = (((Uint32 *) ubLast) [0] ) ) && ( ( ( (Uint32 *) pubFrame) [1] & OxFFFFBFFF) == ( ( (Uint32 *) pubLast) [1] OxFFFFBFFF) ) && ( ( ( (Uint32 *) pubFrame) [2] ) == ( ( (Uint32 *) pubLast) [2] ( ( ( (Uint32 *) pubFrame) [3] ) == ( ( (Uint32 *) pubLast) [3] ) { boSend = FALSE; pCdPtr->ubIC_BuffIndex = 0; We have received three messages, do the vote if ( boSend) { if (pCdPtr->ubIC_BuffIndex >= 2) pCdPtr->ubIC_BuffIndex = 0; pubFrame = pCdPtr->ubIC_Frame [0] ; pub Vote = ubVote; /* Notes: * 1. This code is ugly but it is about 4 times faster * than the equivalent "C" code. * 2. For the following code to work, wlndex, pubFrame and * pubVote must be register varaibles . */ asm ( ' clr.l "wlndex" ") asm(' clr.l "boSend" ") asm(' Vote Loop : move .1 ("pubFrame " , "wlndex") ,D0 ") asmC and.l (16, "pubFrame", "wlndex") DO ") asmC move .1 (16, "pubFrame", "wlndex") Dl ") asmC and.l (32, "pubFrame", "wlndex") Dl ") asmC or.l D0,D1 ") asmC move .1 ("pubFrame " , "wlndex") ,D0 ") asmC and.l (32 , " pubFrame" , "wlndex") DO ") asmC or.l D1,D0 ") asmC move .1 DO, ("pubVote " , "wlndex") ") asmC sub.1 ("pubLast" , "wlndex") ,D0 ") asm( ' or.1 DO, "boSend " ") asm( addq .1 # , "wlndex" ") asm( cmpi .1 #16, "wlndex" ") asm( bit VoteLoop ") asm( tst.l "boSend" ") asm( sne "boSend" ") asm( andi .1 #1, "boSend" ") 1 else { pCdPtr- >ub I C_Bu f f I ndex+ + ; boSei id = FALSE ι Send the Message to the Bonding Process if ( boSend ) { memcpy (pubLast, pubVote, 16); PostlnfoMsg (wPhysChan, ubMsgld, 0) ; } return (boSend) ; } ***************************************************************************** * Routine Name: PostlnfoMsg () * * * * Description: This routine formats and sends a message to the bonding * * task with the most recently found info channel message. * * Parameters : wPhysChan (I) Channel number to operate on * ubMsgld (I) Prim Code to send if a Info Chan Msg found * ubRetCode (I) Return Code for Message * * Return Value: N/A * * * *****************************************************************************/ PUBLIC void PostlnfoMsg (Intlβ wPhysChan, Uint8 ubMsgld, Uint8 ubRetCode) { register ChannelData *pCdPtr; register IMUXMsg *pMsg; Intlβ wStatus ; pCdPtr = &ChanData [PhysToBuff (wPhysChan) ] ; pMsg = GetIMUXblock(&wStatus) ; if ( wStatus == Ret_Ok ) { memset (pMsg, 0, sizeof (IMUXMsg) ) ; memcpy (&pMsg->IMUXData. DspMsg. data. info, pCdPtr->ubIC_Last, 16); pMsg->IMUXData. DspMsg. ubLength = 22; pMsg->IMUXData.DspMsg. ubPrimcode = ubMsgld; pMsg->IMUXData. DspMsg. ubDsO = pCdPtr->ubDS0; pMsg->IMUXData. DspMsg. ublnterface = pCdPtr->ubInterface; pMsg->IMUXData. DspMsg. ubSession = pCdPtr->ubSession; pMsg->IMUXData. DspMsg. ubRetcode = ubRetCode; pMsg->ubPrimcode = BondingDspMsg; pMsg->ubSession = pCdPtr->ubSession; pMsg->ubDspTSlot = wPhysChan; pMsg->ubStartDsO = PhysToBuff (wPhysChan) & 0x01; pMsg->ubInterface = PhysToBuff (wPhysChan) >> 1; sc_qpost (IMUX_Rx_Queue , (Uint8 *)pMsg, &wStatus) ; if ( wStatus != Ret_Ok ) { ReleaseΙMUXblock(pMsg) ; trap(SC_QPOST, wStatus, NULL) ; } /***************************************************************************** * Routine Name: GenBufferNego () * * * * Description: This function fills the Tx buffer with the master channel * * info channel frame. * * * * Parameters: wPhysChan (I) The channel to generate the Data for * * publnfoChannel (I) The info channel Data * * * * Return Value: N/A * * * ***************************************************************************** PUBLIC void GenBufferNego (Intlβ wPhysChan, Uint8 *publnfoChannel) { Uintθ *pTxBuffer; /* Pointer to the Tx Buffer */ pTxBuffer = (Uintβ *) BondBuffer [PhysToBuff (wPhysChan) ] [TX_BUFFER] ; memcpy (pTxBuffer, publnfoChannel, 16); memcpy (pTxBuffer+16, pTxBuffer, BUFFER_BYTES-16) ; ChanData [PhysToBuff (wPhysChan) ] .ubTxChanld = (publnfoChannel [1] >> 1) & 0x3F; } /***************************************************************************** * Routine Name: GenBufferTraining () * * * * Description: This function fills the Tx buffer with the training pattern * * * * Parameters: wPhysChan (I) The channel to generate the Data for * * publnfoChannel (I) The info channel Data * * ubA_Bit (I) The state to set the "A" bit to. * * ubE_Bit (I) The state to set the "E" bit to. * * * * Return Value: N/A * * * *****************************************************************************/ PUBLIC void GenBufferTraining (Intlβ wPhysChan, UintS *pubInfoChannel, UintS ubA Bit, Uintβ ubE Bit) { * * * Fill buffer with all ones and generate training pattern * * * / memset (BondBuffer [PhysToBuff (wPhysChan) ] [TX_BUFFER] , OxFF, BUFFER_BYTES) ; UpdateBufferTraining (wPhysChan, publnfoChannel , ubA_Bit , ubE_Bit , TX_BUFFER) ; } /***************************************************************************** * Routine Name: UpdateBufferTraining () * * * * Description: This function updates the training pattern in the tx buffer * * * * Parameters: wPhysChan (I) The channel to generate the Data for * * publnfoChannel (I) The info channel Data * * ubA_Bit (I) The state to set the "A" bit to. * * ubE_Bit (I) The state to set the "E" bit to. * * ubDir (I) This indicates which buffer to put the * * data into. * * Return Value: N/A * * * * Notes: The ubDir argument should normally always be set to TX_BUFFER. * * This argument allows us to generate the buffer for debug and test. * * * ***************************************************************************** PUBLIC void UpdateBufferTraining (Intlβ wPhysChan, UintS *pubInfoChannel, Uintβ ubA_Bit, Uint8 ubE_Bit, Uint8 ubDir) { Uint8 *pTxBuffer; /* Pointer to the Tx Buffer */ UintS ubChanld; /* Channel ID for the buffer */ UintS ubCrc; /* CRC, A & E Bits */ Uint8 ubFrameCount ; /* Current Frame Counter */ Intlβ wlnfolndex; /* Index into Info Channel Data */ Intlβ wPatlndex; /* Index into Multi-Frame Training Pattern * Intl6 wCounter; /* Loop Counter * /*. * Initialize variables wlnfolndex 0 ; ubFrameCount 0x81 ; ubCrc ( (ubA_Bit == 0 ) ? 0 : 0x40 ) | ( (ubE_Bit == 0) ? 0 : 0x20) j 0X9F; ubChanld (publnfoChannel [1] >> 1 ) & 0x3F; pTxBuffer (UintS * ) BondBuffer [PhysToBuff (wPhysChan) ] [ubDir] ChanData [PhysToBuff (wPhysChan) ] . ubTxChanld = ubChanld; Fill in the Frame Structure . * , for (wCounter=0; wCounter<BUFFER_BYTES ; wCounter+=256) { wPatlndex = (wCounter+ubChanld) % BUFFER_BYTES ; pTxBuffer [wPatlndex + 0 ] = OxlB; pTxBuffer [wPatlndex + 64 ] = publnfoChannel [wInfoIndex++] , • pTxBuffer [wPatlndex + 128] = ubFrameCount; pTxBuffer [wPatlndex + 192] = ubCrc; wlnfolndex &= OxOOOF; ubFrameCount += 2 ; } } ***************************************************************************** * Routine Name: Set_A_Bit() * * * * Description: This function updates the training pattern in the tx buffer * * * * Parameters: wPhysChan (I) The channel to generate the Data for * * ubA_Bit (I) The state to set the "A" bit to. * * * * Return Value : N/A * * * ***************************************************************************** PUBLIC void Set_A_Bit (Intlβ wPhysChan, Uintδ ubA_Bit) { Uintβ *pTxBuffer; /* Pointer to the Tx Buffer */ Uintβ ubChanld; /* Channel ID for the buffer */ Uintβ ubCrc; /* CRC, A & E Bits */ Intlβ wPatlndex; /* Index into Multi-Frame Training Pattern */ Intl6 wCounter; /* Loop Counter */ * * * Initialize variables * * */ ubA_Bit = ( (ubA_Bit == 0) ? 0 : 0x40); pTxBuffer = (Uint8 * ) BondBuffer [PhysToBuff (wPhysChan) ] [TX_BUFFER] ; ubChanld = ChanData [PhysToBuff (wPhysChan) ] . ubTxChanld ; ubCrc = pTxBuffer [ubChanld + 192 ] & OxBF | ubA_Bit ; /* * * Fill in the Frame Structure * * * / for (wCounter=0; wCounter<BUFFER_BYTES; wCounter+=256) { wPatlndex = (wCounter+ubChanld) % BUFFER_BYTES ; pTxBuffer [wPatlndex + 192] = ubCrc; } } /***************************************************************************** * Routine Name: Set_RI_Bit() * * * * Description: This function updates the training pattern in the tx buffer * * * * Parameters: wPhysChan (I) The channel to generate the Data for * * ubRI_Bit (I) The state to set the "A" bit to. * * * * Return Value : N/A * * * *****************************************************************************/ PUBLIC void Set_RI_Bit (Intlβ wPhysChan, Uint8 ubRI_Bit) { Uintβ *pTxBuffer; /* Pointer to the Tx Buffer */ Uint8 ubRev; /* New value for "rev" byte in Info Channel */ Uintβ ubChanld; /* Channel ID for the buffer */ Intl6 wPatlndex; /* Index into Multi-Frame Training Pattern */ Intl6 wCounter; /* Loop Counter */ /* * * Initialize variables * * */ ubRI_Bit = ( (ubRI_Bit == 0) ? 0 : 0x40); pTxBuffer = (UintS * ) BondBuffer [PhysToBuff (wPhysChan) ] [TX_BUFFER] ; ubChanld = ChanData [PhysToBuff (wPhysChan) ] . ubTxChanld ; wPatlndex = ( 1600+ubChanId) % BUFFERJ3YTES ; ubRev = pTxBuffer [wPatlndex] & OxBF | ubRI_Bit ; /* * * Fill in the Frame Structure * * * / for (wCounter=0; wCounter<BUFFER_BYTES ; wCounter+=4096) { wPatlndex = (wCounter+lβOO+ubChanld) % BUFFER_BYTES ; pTxBuffer [wPatlndex] = ubRev; } } Exchange .147760_1. pd

Claims: CLAIMS 1. A method of performing inverse multiplexing on plural channels of data, the method comprising the steps of: receiving a plurality of channels of data, each channel including counts in its data; storing each channel of data in a respective one of a plurality of buffers; determining offsets for outgoing pointers for each of the plurality of buffers based on the counts in the channels of data; and reading out data from each of the plurality of buffers in accordance with the outgoing pointers. 2. A method according to Claim 1 , wherein the determining step determines the outgoing pointers for a buffer based on a difference between a count for a channel of data in the buffer and a count for a channel of data in a reference buffer. 3. A method according to Claim 2, further comprising generating ingoing pointers for the plurality of buffers and storing data received from each channel at a location corresponding to the ingoing pointer for that channel. 4. A method according to Claim 3, wherein an initial value of the ingoing pointer for each buffer corresponds to a master frame count during which data is initially received from the corresponding channel. 5. A method according to Claim 1, wherein the reading step comprises reading out the data from the plurality of buffers into an output buffer, the data in the output buffer having a property of being inverse multiplexed so as to compensate for delay between channels of data. 6. A method according to Claim 5, further comprising determining whether data received from the channels have crossed; and wherein, in a case that two or more channels of data have crossed, reading data out from the plurality of buffers in a sequence that reorders the data by channel so as to correct for channel crossing. 7. A method according to Claim 6, wherein the reading step reorders the data by storing data from respective channels in predetermined locations of the output buffer. 8. A method according to Claim 1, wherein the storing step includes waiting at least one frame prior to storing data from a channel in a respective one of the plurality of buffers. 9. A network interface card (NIC) which performs inverse multiplexing on plural channels of data, the NIC comprising: a plurality of buffers, each buffer for storing one channel of data, each channel including counts in its data; and a controller which executes process steps so as (i) to determine outgoing pointers for each of the plurality of buffers based on the counts in the channels of data; and (ii) to read out data from each of the plurality of buffers in accordance with the outgoing pointers; wherein the outgoing pointers are offset to compensate for interchannel delays. 10. A NIC according to Claim 9, wherein the controller comprises a serial communications controller of a microprocessor. 11. A NIC according to Claim 10, wherein the controller further executes process steps so as to determine ingoing pointers for the plurality of buffers. 12. A NIC according to Claim 11, wherein an initial value of the ingoing pointer for each buffer corresponds to a master frame count during which data is initially received from the coπesponding channel. 13. A NIC according to Claim 9, further comprising an output buffer; wherein the controller reads out the data from the plurality of buffers into the output buffer, the data in the output buffer having the property of being inverse multiplexed so as to compensate for delay between channels of data. 14. A NIC according to Claim 13, wherein the controller executes process steps to determine whether channels of data have crossed; and wherein, in a case that two or more channels of data have crossed, when reading out the data from the plurality of buffers, the controller reorders the data by channel so as to correct for channel crossing. 15. A NIC according to Claim 14, wherein the controller reorders the data by storing data from respective channels in predetermined locations of the output buffer. 16. A NIC according to Claim 9, wherein, upon receiving each channel of data, the controller waits one or more frames prior to storing the data in a respective one of the plurality of buffers. 17. A NIC according to Claim 9, further comprising: a plurality of interfaces which interface to an ISDN line corresponding to each channel of data; and a field programmable gate array (FPGA) which routes each channel of data to the SCC from a bus interfaced between the plurality of interface chips and the FPGA. 18. Computer-executable process steps stored on a computer-readable medium, the computer-executable process steps to perform inverse multiplexing on plural channels of data, the computer-executable process steps comprising: code to receive a plurality of channels of data, each channel including counts in its data; code to store each channel of data in a respective one of a plurality of buffers; code to determine outgoing pointers for each of the plurality of buffers based on the counts in the channels of data; and code to read out data from each of the plurality of buffers in accordance with the outgoing pointers; wherein the determining code determines the outgoing pointers so as to compensate for interchannel delay. 19. Computer-executable process steps according to Claim 18, further comprising code to generate ingoing pointers for the plurality of buffers. 20. Computer-executable process steps according to Claim 20, wherein an initial value of ingoing pointer for each buffer corresponds to a master frame count during which data is initially received from the coπesponding channel. 21. Computer-executable process steps according to Claim 18, wherein the reading code reads out the data from the plurality of buffers into an output buffer, the data in the output buffer having the property of being inverse multiplexed so as to compensate for delay between channels of data. 22. Computer-executable process steps according to Claim 21, further comprising code to determine whether channels of data have crossed; and wherein, in a case that two or more channels of data have crossed, when reading out the data from the plurality of buffers, the reading code reorders the data by channel so as to correct for channel crossing. 23. Computer-executable process steps according to Claim 27, wherein the reading code reorders the data by storing data from respective channels in predetermined locations of the output buffer. 24. Computer-executable process steps according to Claim 30, wherein, upon receiving each channel of data by the receiving code, the storing code waits one or more frames of data in a channel before storing the data in a respective one of the plurality of buffers. 25. Computer-executable process steps according to Claim 18, wherein the receiving code receives each channel of data from a corresponding ISDN line. 26. Computer-executable process steps according to Claim 18, wherein the receiving, storing, determining, and reading code is executed, at least in part, on a serial communications controller (SCC) of a microprocessor on a network interface card (NIC). 27. A method according to Claim 18, wherein the receiving, storing, determining, and reading steps are executed, at least in part, on a serial communications controller (SCC) of a microprocessor on a network interface card (NIC).

Inventors: OLIVEIRA DAVID P, OLIVEIRA, DAVID, P.
Applicants: VIDEOSERVER INC, VIDEOSERVER, INC.
Application year: 1999
Main classification: H04J3
IPC codes: H04Q11