14. Infrared Communication Between Systems

This chapter describes communication between systems using the IR library, which allows applications to use the CTR infrared communications module.

Note:

CTR-SDK includes a development tool (IrCommunicationChecker) for checking the information communicated between systems by infrared communication.

Warning:

If you are developing an extended SNAKE application that uses infrared for communication between systems, make sure that you test communication between CTR and SNAKE to verify that the processing speed differences do not cause any communication problems.

Warning:

If you want to use infrared communication from an application while another feature is using infrared communication, you must end the other feature first.

Infrared communication is used by the following features.

  • Circle Pad Pro
  • NFP (Only CTR)

14.1. nn::ir::Communicator Class

Functions for communication between systems using the infrared communications module are all defined as static functions of the nn::ir::Communicator class.

Warning:

The nn::ir::Communicator class only supports 1:1 communication between systems. It cannot be used for communicating with devices other than 3DS systems, or for communicating among multiple CTR systems.

14.1.1. Security

The library handles encryption and guarantees completeness of transmitted data. Each sent packet also includes a sequence number that is incremented with each send to prevent resend attacks.

14.1.2. Packets

Packets transmitted by the IR library include an IR library header, which is used for communication control, and a system communication header, which includes security and other information.

Figure 14-1. IR Library Packet

IR Library Header System Communication Header User Data GetPacketSize(dataSize) CalculateBufferSizeToCommunicate(dataSize) dataSize

Get the size of the user data in a packet after appending header information using the following functions.

Code 14-1. Functions to Get User Data Size From Data With Header Information Appended
size_t nn::ir::Communicator::GetPacketSize(size_t dataSize);
size_t nn::ir::Communicator::CalculateBufferSizeToCommunicate(size_t dataSize); 

The user data size is specified in dataSize.

The GetPacketSize() function calculates the size required to save the packet, and the CalculateBufferSizeToCommunicate() function calculates the space required for send and receive buffers.

14.1.3. Support for Sleep Mode

When transitioning to Sleep Mode, connections are interrupted and any processing is forcibly stopped. Unsent packets and unreceived data are all discarded.

When a connection is interrupted due to Sleep Mode, no request to disconnect is sent to the partner. When sending a disconnect notification to the partner, perform this processing before transitioning to Sleep Mode, and only transition to Sleep Mode when the disconnection is confirmed.

14.1.4. Communication Range

The following table shows the infrared communication range for two 3DS systems placed with the infrared receivers (on the back of the units) directly facing each other.

Table 14-1. Communication Range for Infrared Communication

Item

Range

Distance

0 to 20 cm from the infrared receiver when the lower screen (Touch Screen) is horizontal

14.2. Initialization

The IR library is initialized by calling the Initialize() function of the nn::ir::Communicator class.

Code 14-2. IR Library Initialization Function
nn::Result nn::ir::Communicator::Initialize(
        void* pBuf, size_t bufSize,
        size_t receiveBufferDataSize, size_t receiveBufferManagementSize,
        size_t sendBufferDataSize, size_t sendBufferManagementSize); 

Specify the buffer used by the library to manage transmitted packets, the buffer size in pBuf, and bufSize. The buffer passed to the library must be allocated by the application beforehand. The starting address must be nn::ir::Communicator::BUFFER_ALIGNMENT (4096-byte) aligned, and its size must be a multiple of nn::ir::Communicator::BUFFER_UNITSIZE (4096-byte). Buffers allocated from device memory cannot be used.

Buffers allocated from device memory cannot be used. The buffer passed to the library is divided into several areas, and the sizes of each area are specified in receiveBufferDataSize, receiveBufferManagementSize, sendBufferDataSize, and sendBufferManagementSize. For more information about specifying sizes, see 14.2.1. Buffer Passed to the Library.

The communication speed is fixed at 115200 bits per second. In 3DS infrared communication, a start bit and an end bit are added to each byte sent. Consequently, the transmission rate for raw data is the baud rate value multiplied by 0.8, which is 92160 bits per second (11520 bytes/s).

14.2.1. Buffer Passed to the Library

IR library transmission processes are implemented with asynchronous functions. The buffer passed to the library during initialization is used mainly for temporary storage of sent packets.

The buffer passed to the library is divided into the regions shown in the following figure.

Figure 14-2. Buffer Used by the IR Library

Send Packet Management Region Send Packet Save Region Receive Packet Management Region Receive Packet Save Region Unused Region pBuf bufSize sendBufferDataSize receiveBufferDataSize receiveBufferManagementSize sendBufferManagementSize Reserved Region GetReservedSize()

The size of each region is calculated as follows.

14.2.1.1. Reserved Region

This region is fixed size, and is required for operation of the library.

You can get its size with the following function.

Code 14-3 Function for Getting the Size of the Reserved Region
size_t nn::ir::Communicator::GetReservedSize(); 

14.2.1.2. Send and Receive Packet Management Region

This region is used for storing management information for transmitted packets.

Its size is calculated based on the maximum number of packets that can be maintained at any time. For connection processing, specify a size returned by GetManagementSize(1) for both send and receive.

Get the management information size using the following function.

Code 14-4. Function to Get the Packet Management Information Size
size_t nn::ir::Communicator::GetManagementSize(s32 dataNum); 

Specify the maximum number of packets maintained at any time in dataNum.

14.2.1.3. Send and Receive Packet Save Region

This region is used to store sent and received packets. For connection processing, the size must be at least 32 bytes for both send and receive.

If the data sizes are variable, specify a size equal to or greater than that calculated using the maximum packet size.

If the data sizes are fixed, specify the size of a single data packet multiplied by the maximum number of packets.

Calculate the packet size from the data size using the following function.

Code 14-5. Function to Get the Size of a Packet
size_t nn::ir::Communicator::GetPacketSize(size_t dataSize); 

Specify the size of the data in dataSize.

Notes About the Receive Packet Save Region

Due to noise, packets stored in the received packet storage area could be invalid. Because of this, we recommend allocating a larger area than the actual number of packets that will be received to ensure that normal packets received after such an invalid packet will be saved reliably. For example, one approach would be to allocate the unused region to the receive packet save region after deciding the sizes for the other regions.

The following code example sends and receives fixed-size data and maximizes the allocation of the receive packet save region.

Code 14-6. Code Sample for Calculating Region Sizes
// Buffer, send/receive data sizes, and number of packets to save.
static u8 buffer[4096] NN_ATRRIBUTE_ALIGN(4096);
size_t sendDataSize = 100;
size_t sendPacketNum = 10;
size_t recvDataSize = 50;
size_t recvPacketNum = 20;

// Calculate required sizes.
size_t sendBufferSize =
        nn::ir::Communicator::GetPacketSize(sendDataSize) * sendPacketNum;
size_t sendManagementSize =
        nn::ir::Communicator::GetManagementSize(sendPacketNum);
size_t receiveBufferSize =
        nn::ir::Communicator::GetPacketSize(recvDataSize) * recvPacketNum;
size_t receiveManagementSize =
        nn::ir::Communicator::GetManagementSize(recvPacketNum);
size_t reservedSize = nn::ir::Communicator::GetReservedSize();

// Check whether the total region size is less than the buffer size.
NN_ASSERT((sendBufferSize + sendManagementSize + receiveBufferSize +
           receiveManagementSize + reservedSize) <= sizeof(buffer));

// Add an unused region size to the size of the receive packet save region.
receiveBufferSize = sizeof(buffer) -
        (sendManagementSize + sendBufferSize +
         receiveManagementSize + reservedSize); 

14.3. Connections

To start infrared communications between 3DS systems, the systems must authenticate each other as communication devices. The called functions are divided into those for the system waiting for a connection request, and those for the system sending a connection request. The waiting system must call the WaitConnection() function, and the sending system must call the RequireConnection() function. Both of these functions are asynchronous calls, so control returns before connection processing has completed.

Code 14-7. Connection Processing Functions
static nn::Result nn::ir::Communicator::WaitConnection();
static nn::Result nn::ir::Communicator::WaitConnection( nn::fnd::TimeSpan sendReplyDelay);
static nn::Result nn::ir::Communicator::RequireConnection();
static nn::ir::ConnectionStatus nn::ir::Communicator::GetConnectionStatus();
static nn::ir::TryingToConnectStatus nn::ir::Communicator::GetTryingToConnectStatus(); 

Processing is asynchronous, so the GetConnectionStatus() function is provided to confirm the overall state of the library and whether connection processing has completed. It returns CONNECTION_STATUS_CONNECTED if connection processing has completed. The GetTryingToConnectStatus() function provides processing state details. For other possible return values, see 14.7. Getting the Connection.

The following table shows a list of return values for the GetTryingToConnectStatus() function.

Table 14-2. GetTryingToConnectStatus Function Return Values

Return Value

Description

TRYING_TO_CONNECT_STATUS_NONE

Not processing a connection.

TRYING_TO_CONNECT_STATUS_SENDING_REQUEST

Sending a connection request.

TRYING_TO_CONNECT_STATUS_WAITING_REPLY

Waiting for a reply to a connection request.

TRYING_TO_CONNECT_STATUS_WAITING_REQUEST

Waiting for a connection request.

TRYING_TO_CONNECT_STATUS_SENDING_REPLY

Sending a reply to a connection request.

14.3.1. Connecting Automatically

When it is not clear which of two 3DS systems to use to receive a connection request, such as when IR communications is requested from the menu on both systems at the same time, use automatic connection.

With automatic connection, both systems switch automatically between waiting for a connection and requesting a connection with different timing, in order to connect. As a result, the connection relationship changes based on the combination when it occurs.

Code 14-8. Automatic Connection Functions
static nn::Result nn::ir::Communicator::AutoConnection();
static nn::Result nn::ir::Communicator::AutoConnection(
        nn::fnd::TimeSpan sendReplyDelay,
        nn::fnd::TimeSpan waitRequestMin, nn::fnd::TimeSpan waitRequestMax,
        nn::fnd::TimeSpan waitReplyMin, nn::fnd::TimeSpan waitReplyMax); 

The following figure shows an example of the operation of automatic connection.

Figure 14-3. Operation of Automatic Connection

waitRequestMin waitRequestMax waitReplyMin waitReplyMax AutoConnection() sendReplyDelay AutoConnection() Send connection reply packet CTR A CTR B Receive connection request packet

If called without parameters, values to be used are calculated by the library based on the baud rate set at initialization. Automatic connection may not succeed in some cases, when it is called without specifying parameters, due to other processing by the application. In such cases, specify the parameters while considering the following points.

sendReplyDelay is the interval between receiving a connection request packet and sending a connection reply packet. The default value is 3 ms. The value must be at least 3 ms, to allow the partner to switch between sending and receiving.

waitRequestMin and waitRequestMax are the minimum and maximum times for sending a connection request packet and waiting to receive a connection reply packet. waitRequestMin must be larger than the total of the connection request packet send time, the connection reply packet receive time, and the time for switching between sending and receiving (3 ms).

waitReplyMin and waitReplyMax are the minimum and maximum times to wait for a connection request packet and to send a connection reply packet. waitReplyMin must be greater than the total of the time to receive a connection request packet, the time to send a connection reply packet, and the interval for switching between sending and receiving (3 ms).

The size of a connection request packet is defined by CONNECTION_REQUEST_PACKET_SIZE (8 bytes), and the size of a connection reply packet is defined by CONNECTION_REPLY_PACKET_SIZE (5 bytes). The time required to send or receive can be calculated from the actual data transmission speed. Packet sizes are defined in bytes. Use care when calculating from transmission speeds, which are in bits.

14.3.2. Connection Roles

You can obtain connection roles using the GetConnectionRole() function. Operation of the IR library does not differ based on connection role. Use them as the policy that determines the role within infrared communication.

Code 14-9. Function for Getting a Connection Role
static nn::ir::ConnectionRole nn::ir::Communicator::GetConnectionRole(void); 

The following table shows the return values for the GetConnectionRole() function.

Table 14-3. GetConnectionRole Function Return Values

Return Value

Description

CONNECTION_ROLE_NONE

The connection role is not set because connection processing has not started or is in progress.

CONNECTION_ROLE_TO_REQUIRE

Connected after requesting a connection.

The system has received a connection reply packet after a RequireConnection or AutoConnection() function call.

CONNECTION_ROLE_TO_WAIT

Connected after receiving a connection request.

The system has sent a connection reply packet after a WaitConnection or AutoConnection function call.

14.4. Confirming the Communication ID

To prevent communication with any other application, each system must confirm the communication ID of the other system after connection processing has completed. Any send or receive-related functions that are called before confirming communication IDs results in an error.

Use the CreateCommunicationId() function to get a communication ID to use for confirmation.

Code 14-10. Function for Getting a Communication ID
static bit32 nn::ir::Communicator::CreateCommunicationId(
        bit32 uniqueId, bool isDemo = false); 

Specify the unique ID allocated by Nintendo for each title for uniqueId. When communicating between different titles, specify the unique ID for either one of them.

To prevent infrared communication between the retail version and the downloaded demo version of a title, specify true for isDemo in the demo version. Always set this parameter to false in the retail version, regardless of whether you want to communicate with demo versions.

Note:

For test programs and titles that use infrared communication and have not been allocated a unique ID by Nintendo, specify a prototype software code in the range from 0xFF000 to 0xFF3FF for uniqueId. In retail software, you must always specify the unique ID assigned by Nintendo.

To confirm communication IDs, one 3DS calls the RequireToConfirmId() function and the other calls the WaitToConfirmId() function. Both of these functions complete successfully if the communication IDs, communication mode IDs, and passphrases specified by each system match. Send and receive functions can only be used after this point.

Code 14-11. Functions for Confirming Communication IDs
static nn::Result nn::ir::Communicator::RequireToConfirmId(
        u8 subId, bit32 communicationId, 
        const char passphrase[], size_t passphraseLength);
static nn::Result nn::ir::Communicator::WaitToConfirmId(
        u8 subId, bit32 communicationId, 
        const char passphrase[], size_t passphraseLength, 
        nn::fnd::TimeSpan timeout);
static bool nn::ir::Communicator::IsConfirmedId(); 

Use the communication ID generated by CreateCommunicationId for communicationId.

When an application using infrared communication has multiple scenes, communication between different scenes can be prevented by specifying a communication mode identifier in subId. Specify a different value for each scene.

The passphrase specified in passphrase is used as the key for encrypting infrared communications packets. Avoid using strings that are easy to guess. The length of a passphrase must be in the range between IR_PASSPHRASE_LENGTH_MIN and IR_PASSPHRASE_LENGTH_MAX.

Specify the length of the passphrase in passphraseLength.

Note that both the RequireToConfirmId() and WaitToConfirmID() functions implicitly consume one of the nn::os::Event class instances assigned to the application.

Communication IDs must be reconfirmed after a disconnect and reconnection, even if they have been confirmed earlier.

The IsConfirmedId() function can be used to check whether ID confirmation has already been completed.

14.5. Transmission

Data can be sent by infrared communications using the Send() function after completing confirmation of communication IDs.

Code 14-12. Functions for Transmitting Data
static nn::Result nn::ir::Communicator::Send(
        void *pBuffer, size_t dataSize, size_t bufferSize, 
        bool restore = false);
static void nn::ir::Communicator::GetSendEvent(nn::os::Event* pEvent);
static nn::Result nn::ir::Communicator::GetLatestSendErrorResult(bool clear);
static void nn::ir::Communicator::GetSendSizeFreeAndUsed(
        size_t *pSizeFree, s32 *pCountFree, 
        size_t *pSizeUsed, s32 *pCountUsed); 

Specify the send buffer and its size in pBuffer and bufferSize respectively. The send buffer must be at least the size obtained by passing the size of the raw data to be sent to the CalculateBufferSizeToCommunicate() function. In addition, this buffer must have a starting address that is nn::ir::Communicator::SEND_BUFFER_ALIGNMENT (4 bytes) aligned.

Specify the data size in dataSize. Data in the range from 0 to 16316 bytes can be sent.

The data starting at the beginning of pBuffer and of length dataSize is sent. After data is sent, the buffer contains the encrypted data with headers appended. If the original data is needed after it is sent, specify true for the restore parameter. Note that this consumes more processing time than only sending.

The actual transmission is done asynchronously and the Send() function returns control to the caller after the packet has been saved in the buffer being used by the library. An event notifies when the data is actually sent, and can be obtained using the GetSendEvent() function. The event passed to pEvent is initialized as an automatically resetting event. The Result class instance returned by the GetLatestSendErrorResult() function indicates whether a send error occurred. If called with the clear parameter set to true, the error in the library’s internal Result class instance is cleared.

The library monitors the state of the buffer, and transmits send packets as quickly as possible through the IR communications module. If the send function is called repeatedly in a short period of time, the library could temporarily store multiple packets in the buffer at the same time. These packets are sent in the order they were saved in the buffer.

If the total size of packets saved exceeds the size of the buffer when sending large packets or sending packets repeatedly, the library discards any further packets without saving them. For this reason, we recommend using GetSendSizeFreeAndUsed to check the available space in the send packet management region and the save region before sending a packet. In particular, note that available space in the save region of at least the packet size obtained from the GetPacketSize() function is required in order to save the packet.

14.6. Receipt

Data can be received by calling the Receive() function after completing confirmation of the communications IDs. After the connection process, the library can continue to receive data unless it is maintaining a packet to be sent. So strictly speaking, the Receive() function is not a function that performs receipt. It retrieves data that has already been received from the buffer. It is not possible to determine whether received data is valid until it has been retrieved using the Receive() function.

Code 14-13. Functions Using Receive Processes
static nn::Result nn::ir::Communicator::Receive(
        void* pDst, size_t size, size_t *pReceiveSize, 
        s32 *pRemainCount);
static void nn::ir::Communicator::GetReceiveEvent(nn::os::Event* pEvent);
static nn::Result nn::ir::Communicator::GetLatestReceiveErrorResult(
        bool clear);
static nn::Result nn::ir::Communicator::GetNextReceiveDataSize(size_t *pSize);
static nn::Result nn::ir::Communicator::DropNextReceiveData(s32 *pRemainCount);
static void nn::ir::Communicator::GetReceiveSizeFreeAndUsed(
        size_t *pSizeFree, s32 *pCountFree, 
        size_t *pSizeUsed, s32 *pCountUsed); 

Specify the receive buffer and buffer size in pDst and size respectively. The receive buffer’s starting address must be nn::ir::Communicator::RECEIVE_BUFFER_ALIGNMENT (4 bytes) aligned. The data is temporarily saved in encrypted form with appended headers (and so forth) in the buffer, so the buffer must be at least as large as the size calculated by the CalculateBufferSizeToCommunicate() function (the size of the raw data to be received) when passed. The required buffer size can be retrieved beforehand using the GetNextReceiveDataSize() function.

One packet of data is retrieved for each call to the Receive() function. The size of the retrieved data is stored in pReceiveSize. When storing the data, the Receive() function stores the amount remaining to be retrieved in pRemainCount. To read all of the stored data, call the Receive() function repeatedly until 0 (zero) bytes are remaining.

The GetReceiveEvent() function returns an event containing notification of when received packets were saved in the buffer, so it can be used if data must be retrieved as soon as it has been received. The event passed to pEvent is initialized as an automatically resetting event. The GetLatestReceiveErrorResult() function returns an instance of the Result class, indicating whether an error has occurred during receive processing. If called with the clear parameter set to true, the error in the library’s internal Result class instance is cleared.

If the GetNextReceiveDataSize() function returns a data size that is clearly different from the expected data (such that the data in the next packet is not needed), one packet’s worth of data can be discarded by using the DropNextReceiveData() function.

If packets are received more frequently than data is retrieved and the total size of packets to be saved exceeds the size of the buffer, the library discards any further packets without saving them. Use the GetReceiveSizeFreeAndUsed function to determine the available space for the receive packet management region and the save region, and use it to manage receipt.

If the current connection is interrupted while received data remains, the remaining data can be received in the interval until the next connection begins.

14.7. Getting the Connection Status

Use the GetConnectionStatus() function to get the status of the connection. In addition, the GetConnectionStatusEvent() function returns an event that indicates when the connection status changed. The event passed to pEvent is initialized as an automatically resetting event.

Code 14-14. Functions Used to Get the Connection Status
static void nn::ir::Communicator::GetConnectionStatusEvent(
        nn::os::Event* pEvent);
static nn::ir::ConnectionStatus nn::ir::Communicator::GetConnectionStatus(); 

The following table shows the return values from the GetConnectionStatus() function.

Table 14-4. GetConnectionStatus Return Values

Return Value

Description

CONNECTION_STATUS_STOPPED

The infrared module is stopped.

CONNECTION_STATUS_TRYING_TO_CONNECT

Indicates that a connection is being established.

CONNECTION_STATUS_CONNECTED

A connection has been established.

CONNECTION_STATUS_DISCONNECTING

Indicates that a connection is being disconnected.

CONNECTION_STATUS_FATAL_ERROR

A fault or other fatal error has occurred.

14.7.1. Handling Faults

When the connection status is CONNECTION_STATUS_FATAL_ERROR, a fault may have occurred in the infrared communication module. In this state, all functions that return a result except for Finalize always return nn::ir::ResultFatalError. Infrared communication cannot be used in any further processing. Immediately finalize the IR library.

This state could indicate a fault in the infrared module, but power-cycling the system may also recover the module. We recommend displaying a message to the user indicating the possibility of a fault and how to handle it.

14.8. Disconnecting

To disconnect infrared communication, call the Disconnect() function.

Code 14-15. Functions Using the Disconnect Processes
static nn::Result nn::ir::Communicator::Disconnect(void);
static nn::Result nn::ir::Communicator::ClearSendBuffer(void);
static nn::Result nn::ir::Communicator::ClearReceiveBuffer(void); 

The Disconnect() function sends a disconnect request to the communication partner. If a connection is being established, the process is interrupted. Disconnection is handled asynchronously, so control is returned before disconnection processing has completed.

If Disconnect is called while some send packets are still unprocessed, the send packets are discarded and the connection is disconnected. If necessary, confirm that all packets have been sent before calling this function. To explicitly discard packets not sent, call the ClearSendBuffer() function.

Packets received before disconnection can be retrieved using the Receive() function after disconnecting. Note, however, that they will be discarded when processing the next connection and connection authentication begins. To explicitly discard all received packets, call the ClearReceiveBuffer() function.

14.9. Finalization

To finalize use of the IR library, call the Finalize() function.

Code 14-16. Functions Using the Finalization Processes
static nn::Result nn::ir::Communicator::Finalize(void); 

If this function is called before the library is initialized, the nn::ir::ResultNotInitialized() function returns an error.


CONFIDENTIAL