2.3. Transactions

The packets transmitted on the bus are acknowledged by the receiving end unless they are broadcast packets (broadcast writes and isochronous packets). The acknowledge code contains an error code, which either signifies error, success or packet pending. In the first two cases the transaction completes, in the last a response packet will follow at a later time from the targetted node to the source node (this is called a split transaction). Only writes can succeed and complete in the ack code, reads and locks require a response. Error and packet pending can happen for every transaction. The response packets contain a response code (rcode) which signifies success or type of error.

For read and write there are two different types, quadlet and block. The quadlet types have all their payload (exactly one quadlet) in the packet header, the block types have a variable length data block appended to the header. Programs using libraw1394 don't have to care about that, quadlet transactions are automatically used when the data length is 4 bytes and block transactions otherwise.

The lock transaction has several extended transaction codes defined which choose the atomic operation to perform, the most used being the compare-and-swap (code 0x2). The transaction passes the data value and (depending on the operation) the arg value to the target node and returns the old value at the target address, but only when the transaction does not have an error. All three values are of the same size, either one quadlet or one octlet.

In the compare-and-swap case, the data value is written to the target address if the old value is identical to the arg value. The old value is returned in any case and can be used to find out whether the swap succeeded by repeating the compare locally. Compare-and-swap is useful for avoiding race conditions when accessing the same address from multiple nodes. For example, isochronous resource allocation is done using compare-and-swap, as described below. Since the old value is always returned, it more efficient to do the first attempt with the reset value of the target register as arg instead of reading it first. Repeat with the returned old value as new arg value if it didn't succeed.