void SCCIAdapter::Write(const unsigned char *buff, int len)
{
  cMutexLock lock(&ciMutex);
  if (buff && len >= 5)
  {
    struct TPDU *tpdu = (struct TPDU *) buff;
    int slot = tpdu->slot;
    if (slots[slot])
    {
      Frame *slotframe = slots[slot]->getFrame();
      switch (tpdu->tag)
      {
      case T_RCV:
        {
          int s;
          unsigned char *d = slotframe->Get(s);
          if (d)
          {
            unsigned char *b;
            if ((b = frame.GetBuff(s + 6)))
            {
              TPDU(b, slot);
              memcpy(b + 2, d, s);
              slotframe->Del(); // delete from rb before Avail()
              SB_TAG(b + 2 + s, slotframe->Avail() > 0 ? 0x80 : 0x00);
              frame.Put();
            }
            else
              slotframe->Del();
          }
          break;
        }
      case T_CREATE_TC:
        {
          tcid = tpdu->data[0];
          unsigned char *b;
          static const unsigned char reqCAS[] = { 0xA0, 0x07, 0x01, 0x91, 0x04, 0x00, 0x03, 0x00, 0x41 };
          if ((b = slotframe->GetBuff(sizeof(reqCAS))))
          {
            memcpy(b, reqCAS, sizeof(reqCAS));
            b[2] = tcid;
            slotframe->Put();
          }
          if ((b = frame.GetBuff(9)))
          {
            TPDU(b, slot);
            TAG(&b[2], 0x83, 0x01);
            b[4] = tcid;
            SB_TAG(&b[5], slotframe->Avail() > 0 ? 0x80 : 0x00);
            frame.Put();
          }
          break;
        }
      case T_DATA_LAST:
        {
          slots[slot]->Process(buff, len);
          unsigned char *b;
          if ((b = frame.GetBuff(6)))
          {
            TPDU(b, slot);
            SB_TAG(&b[2], slotframe->Avail() > 0 ? 0x80 : 0x00);
            frame.Put();
          }
          break;
        }
      }
    }
  }
  else
    DEBUGLOG("%d: short write (buff=%d len=%d)", cardIndex, buff != 0, len);

  if (checkTimer.TimedOut())
  {
    OSCamCheck();
    checkTimer.Set(SOCKET_CHECK_INTERVAL);
  }
}
	//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
	// SendFrameArrivedMessage()
	//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
	void ClientStream::SendFrameArrivedMessage(mach_port_t& recipient, Frame& frame)
	{
		// Setup the message
		FrameArrivedMessage message =
		{
			{
				MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_COPY_SEND) | MACH_MSGH_BITS_COMPLEX,	// mHeader.msgh_bits
				sizeof(FrameArrivedMessage),												// mHeader.msgh_size
				recipient,																	// mHeader.msgh_remote_port
				MACH_PORT_NULL,																// mHeader.msgh_local_port
				0,																			// mHeader.msgh_reserved
				kFrameArrived																// mHeader.msgh_id
			},
			{
				1																			// mBody.msgh_descriptor_count
			},
			{
				// The mach_msg_ool_descriptor_t structure is layed out differently for 32 vs 64 bit architectures, so it is not intialiazed here
			},
			frame.GetFrameType(),															// mFrameType
			frame.GetHostTime(),															// mHostTime
			frame.GetTimingInfo(),															// mTimingInfo
			frame.GetDiscontinuityFlags() | GetDiscontinuityFlags(),						// mDiscontinuityFlags
			frame.GetDroppedFrameCount(),													// mDroppedFrameCount
			frame.GetFirstFrameTime()														// mFirstFrameTime
		};

		// Initialize the mach_msg_ool_descriptor_t portion of the message
		message.mDescriptor.address		= frame.Get();
		message.mDescriptor.size		= frame.Size();
		message.mDescriptor.deallocate	= false;
		message.mDescriptor.copy		= MACH_MSG_VIRTUAL_COPY;
		message.mDescriptor.type		= MACH_MSG_OOL_DESCRIPTOR;
		
		// If this frame or the ClientStream has any "hard" discontinuities in it, "extended duration" timing can't be used
		if (~kCMIOSampleBufferDiscontinuityFlag_DurationWasExtended & (frame.GetDiscontinuityFlags() | GetDiscontinuityFlags()))
			mExtendedFrameHostTime = 0;

		// Use the "extended duration" timing if needed (indicated by a non-zero value for mExtendedFrameHostTime)
		if (0 != mExtendedFrameHostTime)
		{
			// Use the extended frame host & cycle time
			message.mHostTime = mExtendedFrameHostTime;

			// Use the extended frame timing info & add the current frame's duration to the extended frame's duration
			message.mTimingInfo = mExtendedFrameTimingInfo;
			message.mTimingInfo.duration = CMTimeAdd(message.mTimingInfo.duration, frame.GetTimingInfo().duration);
	
			// Indicate this frame has an extended duration
			message.mDiscontinuityFlags |= kCMIOSampleBufferDiscontinuityFlag_DurationWasExtended;
		}

		// Send the message
		mach_msg_return_t err = mach_msg(&message.mHeader, MACH_SEND_MSG | MACH_SEND_TIMEOUT, message.mHeader.msgh_size, 0, MACH_PORT_NULL, 15, MACH_PORT_NULL);
		if (MACH_MSG_SUCCESS == err)
		{
			// The message was sent successfully, so clear the discontinuity flags
			SetDiscontinuityFlags(kCMIOSampleBufferNoDiscontinuities);
			
			// Reset the extended frame host time to 0 to signify that no extension is needed
			mExtendedFrameHostTime = 0;
		}
		else
		{
			DebugMessage("SendFrameArrivedMessage() - Error sending frame to port %d 0x%08X", recipient, err);

			// Something went wrong, so destroy the message so that all of its resources will be properly released
			mach_msg_destroy(&message.mHeader);
			
			// Update the recipient since it might have a new value since the send failed
			recipient = message.mHeader.msgh_remote_port;
			
			// Try and use "extended duration" timing if this frame and the ClientStream lacks any "hard" discontinuities
			if (~kCMIOSampleBufferDiscontinuityFlag_DurationWasExtended & (frame.GetDiscontinuityFlags() | GetDiscontinuityFlags()))
			{
				// The were hard discontinuities, so update the ClientStream's discontinuity flags so they logical sum can be passed on with the next frame
				SetDiscontinuityFlags(GetDiscontinuityFlags() | frame.GetDiscontinuityFlags() | kCMIOSampleBufferDiscontinuityFlag_StreamDiscontinuity);
			}
			else
			{
				// Rather than mark a "hard" discontinuity, remember the frame's duration and so the NEXT frame's can be extended accordingly
				if (0 == mExtendedFrameHostTime)
				{
					// This is the first extension needed, so use this frame's host & cycle time for the next frame
					mExtendedFrameHostTime = frame.GetHostTime();
					mExtendedFrameTimingInfo = frame.GetTimingInfo();
				}
				else
				{
					// An extension is already taking place, so simply add this frame's duration to the extended frame's duration
					mExtendedFrameTimingInfo.duration = CMTimeAdd(mExtendedFrameTimingInfo.duration, frame.GetTimingInfo().duration);
				}
			}
		}
		
		// Remove this client from the set of the native frame needs to message
		frame.RemoveClient(mClient);
	}