Create and send a bluetooth command frame to Cocoa

I am using Cocoa's IOBluetooth infrastructure to communicate with a bluetooth device. So far I have lost the whole process of discovering the device and its services, contacted it, connected to it, and now I want to send some actual commands, but I am having problems with it. Below is a graph from the spec for the AVRCP profile I am trying to use. You can view the pdf here .

I think I need to write a 5 byte value as shown in the picture:

alt text

Here is the method I have right now when writing data:

- (void)l2capChannelOpenComplete:(IOBluetoothL2CAPChannel*)l2capChannel status:(IOReturn)error {
    NSLog(@"Open Complete");
    NSMutableData *playData = [[NSMutableData alloc] initWithCapacity:5];

    unsigned char ctype = 0x0;
    unsigned char subunit = 0x90;
    unsigned char opcode = 0x7C;
    unsigned char opid = 0x44;
    unsigned char opdata = 0x0;

    [playData appendBytes:&ctype length:8];
    [playData appendBytes:&subunit length:8];
    [playData appendBytes:&opcode length:8];
    [playData appendBytes:&opid length:8];
    [playData appendBytes:&opdata length:8];

    usleep(1000);

    [l2capChannel writeAsync:[playData mutableBytes] length:40 refcon:nil];
}

      

When this function is triggered, the devices respond to the next hex value 0x400010.

  • I'm not even sure I'm approaching this correctly!
  • Are the values ​​that I am correctly passing the example in the image?
  • Any help in my endeavor to find out here would be much appreciated!
+2


source to share


2 answers


If you are going to work a lot with AV / C frames and not create a structure (which won't help with partial packing), you should create a class AVCFrame

that makes it easy to customize those frames, sanity checks the values ​​you give them, has a debug description, and will handle all the details for you.

Your code might look like this:

AVCFrame *frame = [AVCFrame frameWithCommandType:AVCCommandTypePlay
                                     subunitType:mySubunitType
                                       subunitID:mySubunitID];
// You likely won't actually be writing to the L2CAPChannel. See below.
[l2capChannel writeAsync:[frame mutableBytes] length:[frame length] refcon:nil];

      

This is not the best interface. You will want to read the general specification of the AV / C digital interface suite.

As far as packing bytes (and it should happen eventually), you will want to use something like:



// Returns |subunitType| shifted and masked appropriately for bit_oring
// with subunit ID to create an address octet.
inline UInt8
AVRCAddressSubunitType(UInt8 subunitType) {
   const UInt8 kLeastThreeBytes = 0x07;
   UInt8 shiftedType = (subunitType << 3) & ~kLeastThreeBytes;
   return shiftedType;
}

// Returns |subunitID| masked appropriately for bit_oring with subunit type
// to create an address octet.
inline UInt8
AVRCAddressSubunitID(UInt8 subunitID) {
   const UInt8 kLeastThreeBytes = 0x07;
   UInt8 maskedID = subunitID & kLeastThreeBytes;
   if (subunitID & ~kLeastThreeBytes) {
      NSLog(@"*** %s: subunit ID %#hhx > 0x07 cannot be represented "
            "in the 3 bits allotted. Truncating to %#hhx.",
            __PRETTY_FUNCTION__, subunitID, maskedID);
   }
   return maskedID;
}

- (void)l2capChannelOpenComplete:(IOBluetoothL2CAPChannel *)l2capChannel
                          status:(IOReturn)error {
  /* might be worth looking at the error... */
  NSLog(@"%s: open complete - "
        "error: (system: %#x; subsystem: %#x; code: %#x)",
         __PRETTY_FUNCTION__,
         err_get_system(error), err_get_sub(error), err_get_code(error));

  /* to send, first pack your data into byte-sized variables */
  // some variables...
  // address byte layout is [3:7] = 9 = PANEL; [0:2] = 0 = subunit ID
  UInt8 address = (AVRCAddressSubunitType(0x09) | AVRCAddressSubunitID(0x00));
  // some more variables...

  /* create a mutable data and append the bytes in sequence */
  // some appending...
  [playData appendBytes:&address length:sizeof(address)];
  // more appending...

  /* finally, send all the bytes */
  [l2capChannel writeAsync:[playData mutableBytes]
                    length:[playData length]
                    refcon:NULL];
}

      

For more details IOWhatever

see the extensive IOKit documentation. At least in 10.5, the referenced docs (as opposed to programming reference books) in the docset were disgusting, so it might be helpful for you to look at the headers themselves.

You will need to consult additional documentation than you have seen so far. The AV / C command frame, whose diagram you included is actually the payload (received in the command / response information field) of the AVCTP frame that you should really send over the L2CAP transport. The AVCTP specification will outline a rudimentary API in Appendix A, Upper AVCTP Interface.

You need to either find or write yourself an AVCTP library to send frames of AV / C commands. You will want the AVCTP library to wrap the L2CAP channel so that you actually send your command frames through it and get your command frames out of it. Good luck! Interacting with equipment can be a lot of fun and you will learn a lot.

+3


source


It's a lot of work to fill the byte array. Also, you are trying to bind eight bytes to playData with each of the -appendBytes: length: messages.



In a situation like this, I simply declare a structure for your BT command frame. NSData doesn't really offer you much here.

+1


source







All Articles