www.pudn.com > 蓝牙协议源代码.zip > rfcomm.c


/*
 * Copyright (c) 2003 EISLAB, Lulea University of Technology.
 * All rights reserved. 
 * 
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission. 
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
 * OF SUCH DAMAGE.
 *
 * This file is part of the lwBT Bluetooth stack.
 * 
 * Author: Conny Ohult 
 *
 */

/*-----------------------------------------------------------------------------------*/
/* rfcomm.c
 *
 * Implementation of the RFCOMM protocol. A subset of the ETSI TS 07.10 standard with 
 * some Bluetooth-specific adaptations.
 */
/*-----------------------------------------------------------------------------------*/

#include "netif/lwbt/l2cap.h"
#include "netif/lwbt/rfcomm.h"
#include "netif/lwbt/lwbt_memp.h"
#include "netif/lwbt/fcs.h"
#include "lwbtopts.h"
#include "lwip/debug.h"

struct rfcomm_pcb_listen *rfcomm_listen_pcbs; /* List of all RFCOMM PCBs listening for 
					      an incomming connection on a specific
					      server channel */
struct rfcomm_pcb *rfcomm_active_pcbs;  /* List of all active RFCOMM PCBs */
struct rfcomm_pcb *rfcomm_tmp_pcb;

/* Forward declarations */
struct rfcomm_pcb *rfcomm_get_active_pcb(u8_t cn, struct bd_addr *bdaddr);

/*-----------------------------------------------------------------------------------*/
/* 
 * rfcomm_init():
 * 
 * Initializes the rfcomm layer.
 */
/*-----------------------------------------------------------------------------------*/
void
rfcomm_init(void)
{
  /* Clear globals */
  rfcomm_listen_pcbs = NULL;
  rfcomm_active_pcbs = NULL;
  rfcomm_tmp_pcb = NULL;
}
/*-----------------------------------------------------------------------------------*/
/*
 * rfcomm_tmr():
 *
 * Called every 1s and implements the command timer that
 * removes a DLC if it has been waiting for a response enough
 * time.
 */
/*-----------------------------------------------------------------------------------*/
void
rfcomm_tmr(void)
{
  struct rfcomm_pcb *pcb, *tpcb;
  err_t ret;
  
  /* Step through all of the active pcbs */
  for(pcb = rfcomm_active_pcbs; pcb != NULL; pcb = pcb->next) {
    if(pcb->to != 0) {
      --pcb->to;
      LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_tmr: %d\n", pcb->to));
      if(pcb->to == 0) {
	/* Timeout */
	if(pcb->cn == 0) {
	  /* If DLC 0 timed out, disconnect all other DLCs on this multiplexer session first */
	  for(tpcb = rfcomm_active_pcbs; tpcb != NULL; tpcb = tpcb->next) {
	    if(tpcb->cn != 0 && bd_addr_cmp(&(tpcb->l2cappcb->remote_bdaddr), &(pcb->l2cappcb->remote_bdaddr))) {
	      //RFCOMM_RMV(&rfcomm_active_pcbs, tpcb); /* Remove pcb from active list */
	      tpcb->state = RFCOMM_CLOSED;
	      RFCOMM_EVENT_DISCONNECTED(tpcb,ERR_OK,ret); /* Notify upper layer */
	    }
	  }
	}
	/* Disconnect this DLC */
	LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_tmr: Timeout! Disconnect this DLC. State = %d\n", pcb->state));
	//RFCOMM_RMV(&rfcomm_active_pcbs, pcb); /* Remove pcb from active list */
	pcb->state = RFCOMM_CLOSED;
	RFCOMM_EVENT_DISCONNECTED(pcb,ERR_OK,ret); /* Notify upper layer */
      }
    }
  }
}
/*-----------------------------------------------------------------------------------*/
/*
 * rfcomm_lp_disconnected():
 *
 * Called by the application to indicate that the lower protocol disconnected. Closes
 * any active PCBs in the lists
 */
/*-----------------------------------------------------------------------------------*/
err_t
rfcomm_lp_disconnected(struct l2cap_pcb *l2cappcb)
{
  struct rfcomm_pcb *pcb, *tpcb;
  err_t ret = ERR_OK;

  pcb = rfcomm_active_pcbs;
  while(pcb != NULL) {
    tpcb = pcb->next;
    if(bd_addr_cmp(&(l2cappcb->remote_bdaddr), &(pcb->l2cappcb->remote_bdaddr))) {
      pcb->state = RFCOMM_CLOSED;
      RFCOMM_EVENT_DISCONNECTED(pcb,ERR_OK,ret); /* Notify upper layer */
    }
    pcb = tpcb;
  }

  return ret;
}
/*-----------------------------------------------------------------------------------*/
/* 
 * rfcomm_new():
 *
 * Creates a new RFCOMM protocol control block but doesn't place it on
 * any of the RFCOMM PCB lists.
 */
/*-----------------------------------------------------------------------------------*/
struct rfcomm_pcb *
rfcomm_new(struct l2cap_pcb *l2cappcb) 
{
  struct rfcomm_pcb *pcb;

  pcb = lwbt_memp_malloc(MEMP_RFCOMM_PCB);
  if(pcb != NULL) {
    memset(pcb, 0, sizeof(struct rfcomm_pcb));
    pcb->l2cappcb = l2cappcb;
    
    pcb->cl = RFCOMM_CL; /* Default convergence layer */
    pcb->n = RFCOMM_N; /* Default maximum frame size */ 
    
    pcb->state = RFCOMM_CLOSED;
    return pcb;
  }
  LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_new: Could not allocate a new pcb\n"));
  return NULL;
}
/*-----------------------------------------------------------------------------------*/
/* 
 * rfcomm_close():
 *
 * Closes the RFCOMM protocol control block.
 */
/*-----------------------------------------------------------------------------------*/
void
rfcomm_close(struct rfcomm_pcb *pcb) 
{
#if RFCOMM_FLOW_QUEUEING
  if(pcb->buf != NULL) {
    pbuf_free(pcb->buf);
  }
#endif
  if(pcb->state == RFCOMM_LISTEN) {
    RFCOMM_RMV((struct rfcomm_pcb **)&rfcomm_listen_pcbs, pcb);
    lwbt_memp_free(MEMP_RFCOMM_PCB_LISTEN, pcb);
  } else {
    RFCOMM_RMV(&rfcomm_active_pcbs, pcb);
    lwbt_memp_free(MEMP_RFCOMM_PCB, pcb);
  }
  pcb = NULL;
}
/*-----------------------------------------------------------------------------------*/
/* 
 * rfcomm_reset_all():
 *
 * Closes all active and listening RFCOMM protocol control blocks.
 */
/*-----------------------------------------------------------------------------------*/
void
rfcomm_reset_all(void) 
{
  struct rfcomm_pcb *pcb, *tpcb;
  struct rfcomm_pcb_listen *lpcb, *tlpcb;

  for(pcb = rfcomm_active_pcbs; pcb != NULL;) {
    tpcb = pcb->next;
    rfcomm_close(pcb);
    pcb = tpcb;
  }

  for(lpcb = rfcomm_listen_pcbs; lpcb != NULL;) {
    tlpcb = lpcb->next;
    rfcomm_close((struct rfcomm_pcb *)lpcb);
    lpcb = tlpcb;
  }
  
  rfcomm_init();
}
/*-----------------------------------------------------------------------------------*/
/* 
 * rfcomm_get_multiplexer():
 *
 * Return the active PCB with the matching Bluetooth address and channel number.
 */
/*-----------------------------------------------------------------------------------*/
struct rfcomm_pcb *
rfcomm_get_active_pcb(u8_t cn, struct bd_addr *bdaddr) {
  struct rfcomm_pcb *pcb;
  for(pcb = rfcomm_active_pcbs; pcb != NULL; pcb = pcb->next) {
    if(pcb->cn == cn && bd_addr_cmp(&(pcb->l2cappcb->remote_bdaddr), 
				    bdaddr)) {
      break;
    }
  }
  return pcb;
}
/*-----------------------------------------------------------------------------------*/
/* 
 * rfcomm_dm():
 *
 * Sends a RFCOMM disconnected mode frame in response to a command when disconnected.
 */
/*-----------------------------------------------------------------------------------*/
static err_t
rfcomm_dm(struct l2cap_pcb *pcb, struct rfcomm_hdr *hdr) 
{
  struct pbuf *p;
  struct rfcomm_hdr *rfcommhdr;
  err_t ret;

  LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_dm\n"));

  if((p = pbuf_alloc(PBUF_RAW, RFCOMM_DM_LEN, PBUF_RAM)) == NULL) {
    /* Could not allocate memory for pbuf */
    LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_dm: Could not allocate memory for pbuf\n"));
    return ERR_MEM;
  }
  rfcommhdr = p->payload;
  rfcommhdr->addr = hdr->addr & 0xFB; /* Set direction bit to 0 for the response */
  rfcommhdr->ctrl = RFCOMM_DM;
  rfcommhdr->len = 1; /* EA bit set to 1 to indicate a 7 bit length field */
  ((u8_t *)p->payload)[RFCOMM_HDR_LEN_1] = fcs8_crc_calc(p, RFCOMM_CRC_CHECK_LEN);
  
  ret = l2ca_datawrite(pcb, p);
  pbuf_free(p);
  return ret;
}
/*-----------------------------------------------------------------------------------*/
/* 
 * rfcomm_connect():
 *
 * Sends a RFCOMM start asynchronous balanced mode frame to startup the channel. Also
 * specify the function to be called when the channel has been connected.
 */
/*-----------------------------------------------------------------------------------*/
err_t
rfcomm_connect(struct rfcomm_pcb *pcb, u8_t cn, err_t (* connected)(void *arg,
								    struct rfcomm_pcb *tpcb,
								    err_t err))
{
  struct rfcomm_hdr *hdr;
  struct pbuf *p;
  err_t ret;
  struct rfcomm_pcb *tpcb;

  LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_connect\n"));

  pcb->connected = connected;
  pcb->cn = cn;
  pcb->rfcommcfg |= RFCOMM_CFG_IR; /* Set role to initiator */ 

  /* Create multiplexer session if one does not already exist */
  if(cn != 0) {
    tpcb = rfcomm_get_active_pcb(0, &pcb->l2cappcb->remote_bdaddr);
    
    if(tpcb == NULL) {
      pcb->state = W4_RFCOMM_MULTIPLEXER;
      RFCOMM_REG(&rfcomm_active_pcbs, pcb);
      pcb = rfcomm_new(pcb->l2cappcb);
      pcb->rfcommcfg |= RFCOMM_CFG_IR; /* Set role to initiator */
    } 
  } 

  if((p = pbuf_alloc(PBUF_RAW, RFCOMM_SABM_LEN, PBUF_RAM)) == NULL) {
    /* Could not allocate memory for pbuf */
    LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_connect: Could not allocate memory for pbuf\n"));
    return ERR_MEM;
  }

  hdr = p->payload;
  hdr->addr = (1 << 0) | ((pcb->rfcommcfg & RFCOMM_CFG_IR) << 1) | (((pcb->rfcommcfg & RFCOMM_CFG_IR) ^ 1) << 2) | (pcb->cn << 3);
  hdr->ctrl = RFCOMM_SABM;
  hdr->len = (1 << 0) | (0 << 1);
  ((u8_t *)p->payload)[RFCOMM_HDR_LEN_1] = fcs8_crc_calc(p, RFCOMM_CRC_CHECK_LEN);

  if((ret = l2ca_datawrite(pcb->l2cappcb, p)) == ERR_OK) {
    pcb->state = W4_RFCOMM_SABM_RSP;
    pcb->to = 5*RFCOMM_TO; /* Set acknowledgement timer, 50-300s (5*10-60s) */
  }

  if((tpcb = rfcomm_get_active_pcb(pcb->cn, &pcb->l2cappcb->remote_bdaddr)) == NULL) {
    RFCOMM_REG(&rfcomm_active_pcbs, pcb);
  }

  pbuf_free(p);
  return ret;
}
/*-----------------------------------------------------------------------------------*/
/* 
 * rfcomm_disconnect():
 *
 * Sends a RFCOMM disconnect frame to close the channel.
 */
/*-----------------------------------------------------------------------------------*/
err_t
rfcomm_disconnect(struct rfcomm_pcb *pcb)
{
  struct rfcomm_hdr *hdr;
  struct pbuf *p;
  err_t ret;

  LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_disconnect\n"));

  if((p = pbuf_alloc(PBUF_RAW, RFCOMM_DISC_LEN, PBUF_RAM)) == NULL) {
    /* Could not allocate memory for pbuf */
    LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_disconnect: Could not allocate memory for pbuf\n"));
    return ERR_MEM;
  }
  
  p = pbuf_alloc(PBUF_RAW, RFCOMM_DISC_LEN, PBUF_RAM);
  hdr = p->payload;
  hdr->addr = (1 << 0) | ((pcb->rfcommcfg & RFCOMM_CFG_IR) << 1) | (((pcb->rfcommcfg & RFCOMM_CFG_IR) ^ 1) << 2) | (pcb->cn << 3);
  hdr->ctrl = RFCOMM_DISC;
  hdr->len = (1 << 0) | (0 << 1);
  ((u8_t *)p->payload)[RFCOMM_HDR_LEN_1] = fcs8_crc_calc(p, RFCOMM_CRC_CHECK_LEN);
  pcb->state = W4_RFCOMM_DISC_RSP;

  if((ret = l2ca_datawrite(pcb->l2cappcb, p)) == ERR_OK) {
    pcb->to = RFCOMM_TO; /* Set acknowledgement timer, 10-60s */
  }
  pbuf_free(p);
  return ret;
}
/*-----------------------------------------------------------------------------------*/
/* 
 * rfcomm_ua():
 *
 * Sends a RFCOMM unnumbered acknowledgement to respond to a connection request.
 */
/*-----------------------------------------------------------------------------------*/
static err_t //RESPONDER
rfcomm_ua(struct l2cap_pcb *pcb, struct rfcomm_hdr *hdr) 
{
  struct pbuf *p;
  struct rfcomm_hdr *rfcommhdr;
  err_t ret;
  
  LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_ua\n"));

  if((p = pbuf_alloc(PBUF_RAW, RFCOMM_UA_LEN, PBUF_RAM)) == NULL) {
    /* Could not allocate memory for pbuf */
    LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_ua: Could not allocate memory for pbuf\n"));
    return ERR_MEM;
  }

  rfcommhdr = p->payload;
  rfcommhdr->addr = hdr->addr & 0xFB; /* Set direction bit to 0 for the response */
  rfcommhdr->ctrl = RFCOMM_UA;
  rfcommhdr->len = 1; /* EA bit set to 1 to indicate a 7 bit length field */
  ((u8_t *)p->payload)[RFCOMM_HDR_LEN_1] = fcs8_crc_calc(p, RFCOMM_CRC_CHECK_LEN);
  
  ret = l2ca_datawrite(pcb, p);
  pbuf_free(p);
  return ret;
}
/*-----------------------------------------------------------------------------------*/
/* 
 * rfcomm_pn():
 *
 * Sends a RFCOMM parameter negotiation multiplexer frame to negotiate the parameters
 * of a data link connection. Also specify the function to be called when a PN 
 * response is received
 */
/*-----------------------------------------------------------------------------------*/
err_t
rfcomm_pn(struct rfcomm_pcb *pcb, 
	  err_t (* pn_rsp)(void *arg, struct rfcomm_pcb *pcb, err_t err))
{
  struct pbuf *p;
  struct rfcomm_msg_hdr *cmdhdr;
  struct rfcomm_pn_msg *pnmsg;
  err_t ret;
  struct rfcomm_pcb *opcb;
  
  LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_pn\n"));

  opcb = rfcomm_get_active_pcb(0, &pcb->l2cappcb->remote_bdaddr);

  if((p = pbuf_alloc(PBUF_RAW, RFCOMM_MSGHDR_LEN + RFCOMM_PNMSG_LEN, PBUF_RAM)) == NULL) {
    /* Could not allocate memory for pbuf */
    LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_pn: Could not allocate memory for pbuf\n"));
    return ERR_MEM;
  }
  
  /* Set multiplexer parameter negotiation command header */
  cmdhdr = p->payload;
  cmdhdr->type = RFCOMM_PN_CMD;
  cmdhdr->len = 1 | (RFCOMM_PNMSG_LEN << 1);

  /* Set multiplexer parameter negotiation command paramenters */
  pnmsg = (void *)(((u8_t *)p->payload) + RFCOMM_MSGHDR_LEN);
  pnmsg->dlci = (((opcb->rfcommcfg & RFCOMM_CFG_IR) ^ 1) << 0) | (pcb->cn << 1);
  pnmsg->i_cl = 0 | (RFCOMM_CL << 4);
  pnmsg->p = 0;
  pnmsg->t = 0;
  pnmsg->n = RFCOMM_N;
  pnmsg->na = 0;
  pnmsg->k = RFCOMM_K;
  
  if((ret = rfcomm_uih(opcb, 0, p)) == ERR_OK) {
    pcb->pn_rsp = pn_rsp;
    opcb->to = RFCOMM_TO; /* Set acknowledgement timer, 10-60s */
  }
  pbuf_free(p);
  return ret;
}
/*-----------------------------------------------------------------------------------*/
/* 
 * rfcomm_test():
 *
 * .
 */
/*-----------------------------------------------------------------------------------*/
err_t
rfcomm_test(struct rfcomm_pcb *pcb, err_t (* test_rsp)(void *arg, struct rfcomm_pcb *tpcb, err_t err)) 
{
  struct pbuf *p;
  struct rfcomm_msg_hdr *cmdhdr;
  err_t ret;
  struct rfcomm_pcb *opcb;
  
  LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_test\n"));

  opcb = rfcomm_get_active_pcb(0, &pcb->l2cappcb->remote_bdaddr);

  if((p = pbuf_alloc(PBUF_RAW, RFCOMM_MSGHDR_LEN, PBUF_RAM)) == NULL) {
    /* Could not allocate memory for pbuf */
    LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_test: Could not allocate memory for pbuf\n"));
    return ERR_MEM;
  }

  /* Set multiplexer modem status command header */
  cmdhdr = p->payload;
  cmdhdr->type = RFCOMM_TEST_CMD;
  cmdhdr->len = 1 | (0 << 1);
  
  if((ret = rfcomm_uih(opcb, 0, p)) == ERR_OK) {
    opcb->test_rsp = test_rsp;
    opcb->to = RFCOMM_TO; /* Set acknowledgement timer, 10-60s */
  }
  pbuf_free(p);
  return ret;
}
/*-----------------------------------------------------------------------------------*/
/* 
 * rfcomm_msc():
 *
 * Sends a RFCOMM modem status multiplexer frame. Also specify the function to be 
 * called when a MSC response is received.
 */
/*-----------------------------------------------------------------------------------*/
err_t
rfcomm_msc(struct rfcomm_pcb *pcb, u8_t fc, 
	   err_t (* msc_rsp)(void *arg, struct rfcomm_pcb *pcb, err_t err))
{
  struct pbuf *p;
  struct rfcomm_msg_hdr *cmdhdr;
  struct rfcomm_msc_msg *mscmsg;
  err_t ret;
  struct rfcomm_pcb *opcb;

  LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_msc\n"));

  opcb = rfcomm_get_active_pcb(0, &pcb->l2cappcb->remote_bdaddr);

  if((p = pbuf_alloc(PBUF_RAW, RFCOMM_MSGHDR_LEN + RFCOMM_MSCMSG_LEN, PBUF_RAM)) == NULL) {
    /* Could not allocate memory for pbuf */
    LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_msc: Could not allocate memory for pbuf\n"));
    return ERR_MEM;
  }

  /* Set multiplexer modem status command header */
  cmdhdr = p->payload;
  cmdhdr->type = RFCOMM_MSC_CMD;
  cmdhdr->len = 1 | (RFCOMM_MSCMSG_LEN << 1);

  /* Set multiplexer parameter negotiation command paramenters */
  mscmsg = (void *)(((u8_t *)p->payload) + RFCOMM_MSGHDR_LEN);
  // mscmsg->dlci = (1 << 0) | (1 << 1) | (((pcb->rfcommcfg & RFCOMM_CFG_IR) ^ 1) << 2) | (pcb->cn << 3);
  mscmsg->dlci = (1 << 0) | (1 << 1) | (0 << 2) | (pcb->cn << 3);
  mscmsg->rs232 = (1 << 0) | (fc << 1) | (0x23 << 2);

  if((ret = rfcomm_uih(opcb, 0, p)) == ERR_OK) {
    pcb->msc_rsp = msc_rsp;
    opcb->to = RFCOMM_TO; /* Set acknowledgement timer, 10-60s */
  }
  pbuf_free(p);
  return ret;
}
/*-----------------------------------------------------------------------------------*/
/* 
 * rfcomm_rpn():
 *
 * Sends a RFCOMM remote port negotiation multiplexer frame to set communication 
 * settings at the remote end of the data link connection.
 */
/*-----------------------------------------------------------------------------------*/
err_t //INITIATOR
rfcomm_rpn(struct rfcomm_pcb *pcb, u8_t br,
	   err_t (* rpn_rsp)(void *arg, struct rfcomm_pcb *pcb, err_t err))
{
  struct pbuf *p;
  struct rfcomm_msg_hdr *cmdhdr;
  struct rfcomm_rpn_msg *rpnmsg;
  err_t ret;
  struct rfcomm_pcb *opcb;

  LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_rpn\n"));

  opcb = rfcomm_get_active_pcb(0, &pcb->l2cappcb->remote_bdaddr);

  if((p = pbuf_alloc(PBUF_RAW, RFCOMM_MSGHDR_LEN + RFCOMM_RPNMSG_LEN, PBUF_RAM)) == NULL) {
    /* Could not allocate memory for pbuf */
    LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_rpn: Could not allocate memory for pbuf\n"));
    return ERR_MEM;
  }
  
  /* Set remote port negotiation command header */
  cmdhdr = p->payload;
  cmdhdr->type = RFCOMM_RPN_CMD;
  cmdhdr->len = 1 | (RFCOMM_RPNMSG_LEN << 1);

  /* Set remote port negotiation command paramenters */
  rpnmsg = (void *)(((u8_t *)p->payload) + RFCOMM_MSGHDR_LEN);
  rpnmsg->dlci = (1 << 0) | (1 << 1) | (((opcb->rfcommcfg & RFCOMM_CFG_IR) ^ 1) << 2) | (pcb->cn << 3);
  rpnmsg->br = br;
  rpnmsg->mask = 1;
  
  if((ret = rfcomm_uih(opcb, 0, p)) == ERR_OK) {
    pcb->rpn_rsp = rpn_rsp;
    opcb->to = RFCOMM_TO; /* Set acknowledgement timer, 10-60s */
    
  }
  pbuf_free(p);
  return ret;
}
/*-----------------------------------------------------------------------------------*/
/* 
 * rfcomm_uih():
 *
 * Sends a RFCOMM unnumbered information frame with header check.
 */
/*-----------------------------------------------------------------------------------*/
err_t //RESPONDER & INITIATOR
rfcomm_uih(struct rfcomm_pcb *pcb, u8_t cn, struct pbuf *q) 
{
  struct pbuf *p, *r;
  err_t ret;
  u16_t tot_len = 0;

  /* Decrease local credits */
  if(pcb->cl == 0xF && pcb->state == RFCOMM_OPEN && pcb->cn != 0) {
    if(pcb->k != 0) {
      --pcb->k;
    } else {
      LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_uih: Out of local credits\n"));
#if RFCOMM_FLOW_QUEUEING
      if(q != NULL) {
	/* Packet can be queued? */
	if(pcb->buf != NULL) {
	  return ERR_OK; /* Drop packet */
	} else {
	  /* Copy PBUF_REF referenced payloads into PBUF_RAM */
	  q = pbuf_take(q);
	  /* Remember pbuf to queue, if any */
	  pcb->buf = q;
	  /* Pbufs are queued, increase the reference count */
	  pbuf_ref(q);
	  LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_uih: Queued packet %p on channel %d\n", (void *)q, pcb->cn));
	}
      }
#else
      LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_uih: Dropped packet.\n"));
      return ERR_OK; /* Drop packet */
#endif /* RFCOMM_FLOW_QUEUEING */      
    }
  }

  if(q != NULL) {
    tot_len = q->tot_len;
  }

  /* Size of information must be less than maximum frame size */
  if(tot_len > pcb->n) {
    LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_uih: Size of information must be less than maximum frame size\n"));
    return ERR_MEM;
  }

  if(tot_len < 127) {
    if((p = pbuf_alloc(PBUF_RAW, RFCOMM_UIH_LEN, PBUF_RAM)) == NULL) {
      LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_uih: Could not allocate memory for pbuf\n"));
      return ERR_MEM; /* Could not allocate memory for pbuf */
    }
  } else {
    if((p = pbuf_alloc(PBUF_RAW, RFCOMM_UIH_LEN+1, PBUF_RAM)) == NULL) {
      LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_uih: Could not allocate memory for pbuf\n")); 
      return ERR_MEM; /* Could not allocate memory for pbuf */
    }
  }

  /* Setup RFCOMM header */
  if(cn == 0) {
    ((u8_t *)p->payload)[0] = (1 << 0) | ((pcb->rfcommcfg & RFCOMM_CFG_IR) << 1) | (0 << 2) | (0 << 3);
  } else {
    ((u8_t *)p->payload)[0] = (1 << 0) | ((pcb->rfcommcfg & RFCOMM_CFG_IR) << 1) | (0 << 2) | (cn << 3);
  }
  ((u8_t *)p->payload)[1] = RFCOMM_UIH;
  if(q != NULL) {
    if(q->tot_len < 127) {
      ((u8_t *)p->payload)[2] = (1 << 0) | (q->tot_len << 1);
    } else {
      ((u16_t *)p->payload)[1] = (0 << 0) | (q->tot_len << 1);
    }
    /* Add information data to pbuf */
    pbuf_chain(p, q);
  } else {
    ((u8_t *)p->payload)[2] = (1 << 0) | (0 << 1); /* Empty UIH frame */
  }
  /* Add information FCS to pbuf */
  if((r = pbuf_alloc(PBUF_RAW, 1, PBUF_RAM)) == NULL) { 
    pbuf_free(p);
    LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_uih: Could not allocate memory for pbuf\n"));
    return ERR_MEM; /* Could not allocate memory for pbuf */
  }

  if(cn == 0) {
    ((u8_t *)r->payload)[0] = pcb->uih0_out_fcs;
  } else {
    ((u8_t *)r->payload)[0] = pcb->uih_out_fcs;
  }
  
  pbuf_chain(p, r);
  pbuf_free(r);

  ret = l2ca_datawrite(pcb->l2cappcb, p);

  /* Dealloc the RFCOMM header. Lower layers will handle rest of packet */
  if(q != NULL) {
    pbuf_dechain(p); /* Have q point to information + FCS */
    pbuf_realloc(q, q->tot_len-1); /* Remove FCS from packet */
  }
  pbuf_free(p);

  return ret;
}
/*-----------------------------------------------------------------------------------*/
/* 
 * rfcomm_uih_credits():
 *
 * Sends a RFCOMM unnumbered information frame with header check and credit based 
 * flow control.
 */
/*-----------------------------------------------------------------------------------*/
err_t //RESPONDER & INITIATOR
rfcomm_uih_credits(struct rfcomm_pcb *pcb, u8_t credits, struct pbuf *q) 
{
  struct pbuf *p, *r;
  err_t ret;
  u16_t tot_len = 0;

  /* Decrease local credits */
  if(pcb->cl == 0xF && pcb->state == RFCOMM_OPEN && pcb->cn != 0) {
    if(pcb->k != 0) {
      --pcb->k;
    } else {
      LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_uih_credits: Out of local credits\n"));
#if RFCOMM_FLOW_QUEUEING
      if(q != NULL) {
	/* Packet can be queued? */
	if(pcb->buf != NULL) {
	  LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_uih_credits: Buffer full. Dropped packet\n"));
	  return ERR_OK; /* Drop packet */
	} else {
	  /* Copy PBUF_REF referenced payloads into PBUF_RAM */
	  q = pbuf_take(q);
	  /* Remember pbuf to queue, if any */
	  pcb->buf = q;
	  /* Pbufs are queued, increase the reference count */
	  pbuf_ref(q);
	  LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_uih_credits: Queued packet %p on channel %d\n", (void *)q, pcb->cn));
	}
      }
#else
      LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_uih_credits: Dropped packet\n"));
#endif /* RFCOMM_FLOW_QUEUEING */
      return ERR_OK;
    }
  }

  if(q != NULL) {
    tot_len = q->tot_len;
  }

  /* Size of information must be less than maximum frame size */
  if(tot_len > pcb->n) {
    LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_uih_credits: Size of information must be less than maximum frame size = %d Packet lenght = %d\n", pcb->n, q->tot_len));
    return ERR_MEM;
  }
  
  if(tot_len < 127) {
    if((p = pbuf_alloc(PBUF_RAW, RFCOMM_UIHCRED_LEN, PBUF_RAM)) == NULL) {
      LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_uih_credits: Could not allocate memory for pbuf\n"));
      return ERR_MEM; /* Could not allocate memory for pbuf */
    }
  } else {
    if((p = pbuf_alloc(PBUF_RAW, RFCOMM_UIHCRED_LEN+1, PBUF_RAM)) == NULL) {
      LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_uih_credits: Could not allocate memory for pbuf\n"));
      return ERR_MEM; /* Could not allocate memory for pbuf */
    }
  }

  /* Setup RFCOMM header */
  ((u8_t *)p->payload)[0] = (1 << 0) | ((pcb->rfcommcfg & RFCOMM_CFG_IR) << 1) | (0  << 2) | (pcb->cn << 3);
  ((u8_t *)p->payload)[1] = RFCOMM_UIH_PF;
  if(q != NULL) {
    if(q->tot_len < 127) {
      ((u8_t *)p->payload)[2] = (1 << 0) | (q->tot_len << 1);
      ((u8_t *)p->payload)[3] = credits;
    } else {
      ((u16_t *)p->payload)[1] = (0 << 0) | (q->tot_len << 1);
      ((u8_t *)p->payload)[4] = credits;
    }
    /* Add information data to pbuf */
    pbuf_chain(p, q);
  } else {
    /* Credit only UIH frame */
    ((u8_t *)p->payload)[2] = (1 << 0) | (0 << 1);
  }
  
  /* Add information FCS to pbuf */
  if((r = pbuf_alloc(PBUF_RAW, 1, PBUF_RAM)) == NULL) {
    pbuf_free(p);
    LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_uih_credits: Could not allocate memory for pbuf\n"));
    return ERR_MEM; /* Could not allocate memory for pbuf */
  }

  ((u8_t *)r->payload)[0] = pcb->uihpf_out_fcs;
  pbuf_chain(p, r);
  pbuf_free(r);

  /* Increase remote credits */
  pcb->rk += credits;

  LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_uih_credits: p->tot_len = %d pcb->k = %d pcb->rk = %d\n", p->tot_len, pcb->k, pcb->rk));

  ret = l2ca_datawrite(pcb->l2cappcb, p);

  /* Free RFCOMM header. Higher layers will handle rest of packet */
  if(q != NULL) {
    pbuf_dechain(p);
    pbuf_realloc(q, q->tot_len-1); /* Remove FCS from packet */
  }
  pbuf_free(p);

  return ret;
}
/*-----------------------------------------------------------------------------------*/
/* 
 * rfcomm_process_msg():
 *
 * Parses the received RFCOMM message and handles it.
 */
/*-----------------------------------------------------------------------------------*/
void
rfcomm_process_msg(struct rfcomm_pcb *pcb, struct rfcomm_hdr *rfcommhdr, struct l2cap_pcb *l2cappcb,
		   struct pbuf *p)
{
  struct rfcomm_msg_hdr *cmdhdr, *rsphdr;
  struct rfcomm_pn_msg *pnreq;
  struct rfcomm_msc_msg *mscreq;
  struct rfcomm_rpn_msg *rpnreq;
  struct rfcomm_pcb *tpcb; /* Temp pcb */
  struct rfcomm_pcb_listen *lpcb; /* Listen pcb */
  struct pbuf *q;
  err_t ret;

  cmdhdr = p->payload;
  pbuf_header(p, -RFCOMM_MSGHDR_LEN);

  switch(cmdhdr->type) {
  case RFCOMM_PN_CMD:
    LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_process_msg: RFCOMM PN command\n"));
    pnreq = p->payload;

    /* Check if the DLC is already established */
    tpcb = rfcomm_get_active_pcb((pnreq->dlci >> 1), &pcb->l2cappcb->remote_bdaddr);
    
    if(tpcb == NULL) {
      /* Check if the server channel exists */
      for(lpcb = rfcomm_listen_pcbs; lpcb != NULL; lpcb = lpcb->next) {
	if(lpcb->cn == (pnreq->dlci >> 1)) {
	  break;
	}
      }
      if(lpcb != NULL) {
	/* Found a listening pcb with a matching server channel number, now initiate a new PCB 
	   with default configuration */
	LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_process_msg: Allocate RFCOMM PCB for CN %d******************************\n", lpcb->cn));
	if((tpcb = rfcomm_new(pcb->l2cappcb)) == NULL) {
	  LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_process_msg: could not allocate PCB\n"));
	  return;
	}
	tpcb->cn = lpcb->cn;
	tpcb->callback_arg = lpcb->callback_arg;
	tpcb->accept = lpcb->accept;
	tpcb->state = RFCOMM_CFG;

	RFCOMM_REG(&rfcomm_active_pcbs, tpcb);
      } else {
	/* Channel does not exist, refuse connection with DM frame */
	LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_process_msg: Channel does not exist, refuse connection with DM frame CN %d\n", (pnreq->dlci >> 1)));
	rfcomm_dm(pcb->l2cappcb, rfcommhdr);
	break;
      }
    }
    /* Get suggested parameters */
    tpcb->cl = pnreq->i_cl >> 4;
    tpcb->p = pnreq->p;
    if(tpcb->n > pnreq->n) {
      tpcb->n = pnreq->n;
    }
    tpcb->k = pnreq->k;
    LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_process_msg: RFCOMM_PN_CMD. tpcb->k = %d\n", tpcb->k));
  
    /* Send PN response */
    cmdhdr->type = cmdhdr->type & 0xFD; /* Set C/R to response */
    if(tpcb->cl == 0xF) {
      pnreq->i_cl = 0 | (0xE << 4); /* Credit based flow control */
    } else {
      pnreq->i_cl = 0; /* Remote device conforms to bluetooth version 1.0B. No flow control */
    }
    pnreq->p = tpcb->p;
    pnreq->n = tpcb->n;
    pnreq->k = tpcb->k;
    pbuf_header(p, RFCOMM_MSGHDR_LEN);

    rfcomm_uih(pcb, 0, p);
    break;
  case RFCOMM_PN_RSP:
    LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_process_msg: RFCOMM PN response\n"));
    pcb->to = 0; /* Reset response timer */
    pnreq = p->payload;
    /* Find PCB with matching server channel number and bluetooth address */
    tpcb = rfcomm_get_active_pcb((pnreq->dlci >> 1), &pcb->l2cappcb->remote_bdaddr);
      
    if(tpcb != NULL) {
      /* Use negotiated settings that may have changed from the default ones */
      if((pnreq->i_cl >> 4) == 0xE) {
	tpcb->cl = 0xF; /* Credit based flow control */
	tpcb->k = pnreq->k; /* Inital credit value */
	LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_process_msg: RFCOMM_PN_RSP. tpcb->k = %d\n", tpcb->k));
	LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_process_msg: Credit based flow control is used for outgoing packets 0x%x %d %d\n", (pnreq->i_cl >> 4), pnreq->k, pnreq->n));
      } else {
	LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_process_msg: No flow control used for outgoing packets 0x%x\n", (pnreq->i_cl >> 4)));
	tpcb->cl = 0; /* Remote device conform to bluetooth version 1.0B. No flow control */
      }
      tpcb->n = pnreq->n; /* Maximum frame size */
      
      if(tpcb->state == W4_RFCOMM_MULTIPLEXER) {
	rfcomm_connect(tpcb, tpcb->cn, tpcb->connected); /* Create a connection for a channel that 
							    waits for the multiplexer connection to
							    be established */
      }

      pcb->state = RFCOMM_OPEN;
      RFCOMM_EVENT_PN_RSP(tpcb,ERR_OK,ret); 
    } /* else silently discard */
    break; 
  case RFCOMM_TEST_CMD:
    /* Send TEST response */
    cmdhdr->type = cmdhdr->type & 0xBF; /* Set C/R to response */
    pbuf_header(p, RFCOMM_MSGHDR_LEN);
 
    rfcomm_uih(pcb, 0, p);
    break;
  case RFCOMM_TEST_RSP:
    pcb->to = 0; /* Reset response timer */
    RFCOMM_EVENT_TEST(pcb,ERR_OK,ret);
    break;
  case RFCOMM_FCON_CMD:
    /* Enable transmission of data on all channels in session except cn 0 */
    for(tpcb = rfcomm_active_pcbs; tpcb != NULL; tpcb = tpcb->next) {
      if(bd_addr_cmp(&(tpcb->l2cappcb->remote_bdaddr), &(l2cappcb->remote_bdaddr)) &&
	 tpcb->cn != 0) {
	tpcb->rfcommcfg |= RFCOMM_CFG_FC;
      }
    }
    /* Send FC_ON response */
    cmdhdr->type = cmdhdr->type & 0xBF; /* Set C/R to response */
    pbuf_header(p, RFCOMM_MSGHDR_LEN);
 
    rfcomm_uih(pcb, 0, p);
    break;
  case RFCOMM_FCON_RSP:
    break;
  case RFCOMM_FCOFF_CMD:
    /* Disable transmission of data on all channels in session except cn 0 */
    for(tpcb = rfcomm_active_pcbs; tpcb != NULL; tpcb = tpcb->next) {
      if(bd_addr_cmp(&(tpcb->l2cappcb->remote_bdaddr), &(l2cappcb->remote_bdaddr)) &&
	 tpcb->cn != 0) {
	tpcb->rfcommcfg &= ~RFCOMM_CFG_FC;
      }
    }
    /* Send FC_OFF response */
    cmdhdr->type = cmdhdr->type & 0xBF; /* Set C/R to response */
    pbuf_header(p, RFCOMM_MSGHDR_LEN);
 
    rfcomm_uih(pcb, 0, p);
    break;
  case RFCOMM_FCOFF_RSP:
    break;
  case RFCOMM_MSC_CMD:
    LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_process_msg: RFCOMM_MSC_CMD\n"));
    mscreq = p->payload;    
    /* Find DLC */
    tpcb = rfcomm_get_active_pcb((mscreq->dlci >> 3), &pcb->l2cappcb->remote_bdaddr);

    if(tpcb != NULL) {
      /* Set flow control bit. Ignore remaining fields in the MSC since this is a type 1 
	 device */
      if((mscreq->rs232 >> 1) & 0x01) {
	tpcb->rfcommcfg |= RFCOMM_CFG_FC;
      } else {
	tpcb->rfcommcfg &= ~RFCOMM_CFG_FC;
      }

      LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcommm_process_msg: fc bit = %d\n", (mscreq->rs232 >> 1) & 0x01));

      /* Send MSC response */
      cmdhdr->type = cmdhdr->type & 0xFD; /* Set C/R to response */
      pbuf_header(p, RFCOMM_MSGHDR_LEN);
      
      if(!(tpcb->rfcommcfg & RFCOMM_CFG_IR) && !(tpcb->rfcommcfg & RFCOMM_CFG_MSC_IN)) { /* We are the responder and should send a MSC command before responding to one */
	LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcommm_process_msg: We are the responder and should send a MSC command before responding to one\n"));
	rfcomm_msc(tpcb, 0, NULL);
      }

      rfcomm_uih(pcb, 0, p);

      tpcb->rfcommcfg |= RFCOMM_CFG_MSC_OUT;
     
      if(tpcb->rfcommcfg & RFCOMM_CFG_MSC_IN && tpcb->state != RFCOMM_OPEN) {
	tpcb->state = RFCOMM_OPEN;
	if(tpcb->rfcommcfg & RFCOMM_CFG_IR) {
	  RFCOMM_EVENT_CONNECTED(tpcb,ERR_OK,ret);
	} else {
	  RFCOMM_EVENT_ACCEPT(tpcb,ERR_OK,ret);
	}
      }
    } /* else silently discard */
    break;
  case RFCOMM_MSC_RSP:
    LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_process_msg: RFCOMM_MSC_RSP\n"));
    /* Information received in response is only a copy of the signals that where sent in the command */
    pcb->to = 0; /* Reset response timer */
    
    mscreq = p->payload;
    
    /* Find PCB with matching server channel number and Bluetooth address */
    tpcb = rfcomm_get_active_pcb((mscreq->dlci >> 3), &pcb->l2cappcb->remote_bdaddr);
 
    if(tpcb != NULL) {
      
      if(tpcb->rfcommcfg & RFCOMM_CFG_MSC_IN) {
	RFCOMM_EVENT_MSC(tpcb,ERR_OK,ret); /* We have sent a MSC after initial configuration of 
					      the connection was done */
      } else {
	tpcb->rfcommcfg |= RFCOMM_CFG_MSC_IN;
	if(tpcb->rfcommcfg & RFCOMM_CFG_MSC_OUT) {
	  tpcb->state = RFCOMM_OPEN;
	  if(tpcb->rfcommcfg & RFCOMM_CFG_IR) {
	    RFCOMM_EVENT_CONNECTED(tpcb,ERR_OK,ret);
	  } else {
	    RFCOMM_EVENT_ACCEPT(tpcb,ERR_OK,ret);
	  }
	}
      }
    } /* else silently discard */
    break;
  case RFCOMM_RPN_CMD:
    LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_process_msg: RFCOMM_RPN_CMD\n"));
    /* Send RPN response */
    if(cmdhdr->len == 8) {
      /* RPN command was a request to set up the link's parameters */
      /* All parameters accepted since this is a type 1 device */
      cmdhdr->type = cmdhdr->type & 0xBF; /* Set C/R to response */
      pbuf_header(p, RFCOMM_MSGHDR_LEN);
      //rfcomm_uih(pcb->l2cappcb, rfcommhdr, p);
      rfcomm_uih(pcb, 0, p);
    } else if(cmdhdr->len == 1) {
      /* RPN command was a request for the link's parameters */
      q = pbuf_alloc(PBUF_RAW, RFCOMM_RPNMSG_LEN+RFCOMM_MSGHDR_LEN, PBUF_RAM);
      rsphdr = q->payload;
      rsphdr->type = cmdhdr->type & 0xBF; /* Set C/R to response */
      rsphdr->len = RFCOMM_RPNMSG_LEN;
      pbuf_header(q, -RFCOMM_MSGHDR_LEN);
      rpnreq = q->payload;
      rpnreq->dlci = ((u8_t *)p->payload)[0]; /* Copy DLCI from command to response */
      rpnreq->br = RFCOMM_COM_BR; /* Default baud rate */
      rpnreq->cfg = RFCOMM_COM_CFG; /* Default data bits, stop bits, parity and parity type */
      rpnreq->fc = RFCOMM_COM_FC; /* Default flow control */
      rpnreq->xon = RFCOMM_COM_XON; /* Default */
      rpnreq->xoff = RFCOMM_COM_XOFF; /* Default */
      rpnreq->mask = 0xFFFF; /* All parameters are valid */
      pbuf_header(q, RFCOMM_MSGHDR_LEN);
      
      rfcomm_uih(pcb, 0, q);
      pbuf_free(q);
    } else {
      //SHOULD NOT HAPPEN. LENGTH SHOULD ALWAYS BE 1 OR 8
    }
    break;
  case RFCOMM_RPN_RSP:
    LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_process_msg: RFCOMM_RPN_CMD\n"));
    pcb->to = 0; /* Reset response timer */
    rpnreq = p->payload;
    LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_msc: rpn response received 0x%x\n", rpnreq->br));

    /* Find PCB with matching server channel number and bluetooth address */
    tpcb = rfcomm_get_active_pcb((rpnreq->dlci >> 3), &pcb->l2cappcb->remote_bdaddr);
    
    if(tpcb != NULL) {
      RFCOMM_EVENT_RPN(tpcb,ERR_OK,ret);
    }
    break;
  case RFCOMM_RLS_CMD:
    LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_process_msg: RFCOMM_RLS_CMD\n"));
    /* Send RLS response */
    cmdhdr->type = cmdhdr->type & 0xBF; /* Set C/R to response */
    pbuf_header(p, RFCOMM_MSGHDR_LEN);
  
    rfcomm_uih(pcb, 0, p);
    break;
  case RFCOMM_RLS_RSP:
    break;
  case RFCOMM_NSC_RSP:
    break;
  default:
    /* Send NSC response */
    q = pbuf_alloc(PBUF_RAW, RFCOMM_MSGHDR_LEN, PBUF_RAM);
    rsphdr = q->payload; 
    rsphdr->type = ((cmdhdr->type & 0x03) << 0) | (RFCOMM_NSC_RSP << 2);
    rsphdr->len = 0;
 
    rfcomm_uih(pcb, 0, q);
    pbuf_free(q);
    break;
  }
}
/*-----------------------------------------------------------------------------------*/
/* 
 * rfcomm_input():
 *
 * Called by the lower layer. Does a frame check, parses the header and forward it to 
 * the upper layer or handle the command frame.
 */
/*-----------------------------------------------------------------------------------*/
err_t
rfcomm_input(void *arg, struct l2cap_pcb *l2cappcb, struct pbuf *p, err_t err)
{
  struct rfcomm_hdr rfcommhdr;
  struct rfcomm_pcb *pcb, *tpcb;
  struct rfcomm_pcb_listen *lpcb;

  s16_t len = 0;
  u8_t hdrlen;
  u8_t fcs;
  
  struct pbuf *q;
  err_t ret;

  rfcommhdr.addr = *((u8_t *)p->payload);
  rfcommhdr.ctrl = ((u8_t *)p->payload)[1];

  /* Find PCB with matching server channel number and bluetooth address */
  pcb = rfcomm_get_active_pcb((rfcommhdr.addr >> 3), &l2cappcb->remote_bdaddr);

  if(pcb == NULL && rfcommhdr.ctrl != RFCOMM_SABM) {
    /* Channel does not exist */
    if(rfcommhdr.ctrl != RFCOMM_DM_PF && rfcommhdr.ctrl != RFCOMM_DM) {
      /* Send a DM response */
      LWIP_DEBUGF(RFCOMM_DEBUG,("Send a DM response to CN %d rfcomm.ctrl == 0x%x\n", (rfcommhdr.addr >> 3), rfcommhdr.ctrl));
      rfcomm_dm(l2cappcb, &rfcommhdr);
    } /* else silently discard packet */ 
    pbuf_free(p);
    return ERR_OK;
  }

  /* Check if length field is 1 or 2 bytes long and remove EA bit */
  if((((u8_t *)p->payload)[2] & 0x01) == 1) {
    hdrlen = RFCOMM_HDR_LEN_1;
    rfcommhdr.len = (((u8_t *)p->payload)[2] >> 1) & 0x007F;
  } else {
    hdrlen = RFCOMM_HDR_LEN_2;
    rfcommhdr.len = (((u16_t *)p->payload)[1] >> 1) & 0x7FFF;
  }

  if(rfcommhdr.ctrl == RFCOMM_UIH_PF) {
    if(pcb->cl == 0xF) {
      rfcommhdr.k = ((u8_t *)p->payload)[hdrlen++];
    }
  }

  /* Frame check */
  for(q = p; q != NULL; q = q->next) {
    len += q->len;
    if(len > (rfcommhdr.len + hdrlen)) {
      len -= q->len;
      len = rfcommhdr.len - len;
      len += hdrlen;
      break;
    }
  }

  fcs = ((u8_t *)q->payload)[len];
  if(rfcommhdr.ctrl == RFCOMM_UIH) {
    if(pcb->cn == 0) {
      if(fcs != pcb->uih0_in_fcs) { /* Check against the precalculated fcs */
	//if(fcs8_crc_check(p, RFCOMM_UIHCRC_CHECK_LEN, fcs) != 0) {
	/* Packet discarded due to failing frame check sequence */
	LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_input: UIH packet discarded due to failing frame check sequence\n"));
	pbuf_free(p);
	return ERR_OK;
      }
    }
  } else if(rfcommhdr.ctrl == RFCOMM_UIH) {
    if(fcs != pcb->uih_in_fcs) { /* Check against the precalculated fcs */
      //if(fcs8_crc_check(p, RFCOMM_UIHCRC_CHECK_LEN, fcs) != 0) {
      /* Packet discarded due to failing frame check sequence */
      LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_input: UIH packet discarded due to failing frame check sequence\n"));
      pbuf_free(p);
      return ERR_OK;
    }
  } else if(rfcommhdr.ctrl == RFCOMM_UIH_PF) {
    if(fcs != pcb->uihpf_in_fcs) { /* Check against the precalculated fcs */
      //if(fcs8_crc_check(p, RFCOMM_UIHCRC_CHECK_LEN, fcs) != 0) {
      /* Packet discarded due to failing frame check sequence */
      LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_input: UIH_PF packet discarded due to failing frame check sequence RFCS = 0x%x LFCS = 0x%x\n", 
			    fcs, pcb->uihpf_in_fcs));
      pbuf_free(p);
      return ERR_OK;
    }
  } else {
    if(fcs8_crc_check(p, hdrlen, fcs) != 0) {
      /* Packet discarded due to failing frame check sequence */
      LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_input: Packet discarded due to failing frame check sequence\n"));
      pbuf_free(p);
      return ERR_OK;
    }
  }

  pbuf_header(p, -hdrlen); /* Adjust information pointer */
  pbuf_realloc(p, rfcommhdr.len); /* Remove fcs from packet */
  
  switch(rfcommhdr.ctrl) {
  case RFCOMM_SABM:
    LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_input: RFCOMM_SABM\n"));
    if(pcb == NULL) {
      /* Check if the server channel exists */
      lpcb = NULL;
      if(rfcommhdr.addr >> 3 == 0) { /* Only the multiplexer channel can be connected without first 
					configuring it */
	for(lpcb = rfcomm_listen_pcbs; lpcb != NULL; lpcb = lpcb->next) {
	  if(lpcb->cn == (rfcommhdr.addr >> 3)) {
	    break;
	  }
	}
      }
      if(lpcb != NULL) {
	/* Found a listening pcb with a matching server channel number, now initiate a new active PCB 
	   with default configuration */
	LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_input: Allocate RFCOMM PCB for CN %d******************************\n", lpcb->cn));
	if((pcb = rfcomm_new(l2cappcb)) == NULL) {
	  /* No memory to allocate PCB. Refuse connection attempt */
	  LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_input: No memory to allocate PCB. Refuse connection attempt CN %d******************************\n", lpcb->cn));
	  rfcomm_dm(l2cappcb, &rfcommhdr);
	  pbuf_free(p);
	  return ERR_OK;
	}
	pcb->cn = lpcb->cn;
	pcb->callback_arg = lpcb->callback_arg;
	pcb->accept = lpcb->accept;
	  
	RFCOMM_REG(&rfcomm_active_pcbs, pcb);
      } else {
	/* Channel does not exist or multiplexer is not connected, refuse connection with DM frame */
	LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_input: Channel does not exist, refuse connection with DM frame CN %d\n", (rfcommhdr.addr >> 3)));
	rfcomm_dm(l2cappcb, &rfcommhdr);
	pbuf_free(p);
	break;
      }
    }
    /* Set role to responder */
    pcb->rfcommcfg &= ~RFCOMM_CFG_IR;
    
    /* Send UA frame as response to SABM frame */
    rfcomm_ua(l2cappcb, &rfcommhdr);
    
    /* FCS precalculation for UIH frames */
    pbuf_header(p, hdrlen); /* Reuse the buffer for the current header */

    /* Change header values to refelct an UIH frame sent to the initiator */
    *((u8_t *)p->payload) &= 0xFB; /* Set direction bit to 0. We are the responder */
    *((u8_t *)p->payload) &= 0xFD; /* Set C/R bit to 0. We are the responder */
    ((u8_t *)p->payload)[1] = RFCOMM_UIH;
    pcb->uih_out_fcs = fcs8_crc_calc(p, RFCOMM_UIHCRC_CHECK_LEN);
    ((u8_t *)p->payload)[1] = RFCOMM_UIH_PF;
    pcb->uihpf_out_fcs = fcs8_crc_calc(p, RFCOMM_UIHCRC_CHECK_LEN);

    /* Change header values to refelct an UIH frame from to the initiator */
    //*((u8_t *)p->payload) |= 0x04; /* Set direction bit to 1. We are the responder */
    *((u8_t *)p->payload) &= 0xFB; /* Set direction bit to 0. We are the responder */
    *((u8_t *)p->payload) |= 0x02; /* Set C/R bit to 1. We are the responder */
    ((u8_t *)p->payload)[1] = RFCOMM_UIH;
    pcb->uih_in_fcs = fcs8_crc_calc(p, RFCOMM_UIHCRC_CHECK_LEN);
    ((u8_t *)p->payload)[1] = RFCOMM_UIH_PF;
    pcb->uihpf_in_fcs = fcs8_crc_calc(p, RFCOMM_UIHCRC_CHECK_LEN);

    /* UIH frame received on the control channel */
    *((u8_t *)p->payload) &= 0xFB; /* Set direction bit to 0 */
    ((u8_t *)p->payload)[1] = RFCOMM_UIH;
    pcb->uih0_in_fcs = fcs8_crc_calc(p, RFCOMM_UIHCRC_CHECK_LEN);

    /* Change header values to reflect an UIH frame sent on the control channel */
    *((u8_t *)p->payload) &= 0xF9; /* Set C/R bit and direction bit to 0 */
    ((u8_t *)p->payload)[1] = RFCOMM_UIH;
    pcb->uih0_out_fcs = fcs8_crc_calc(p, RFCOMM_UIHCRC_CHECK_LEN);
    
    pbuf_free(p);
    break;
  case RFCOMM_UA:
    LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_input: RFCOMM_UA\n"));
    pcb->to = 0;
    if(pcb->state ==  W4_RFCOMM_SABM_RSP) {
      pcb->state = RFCOMM_CFG;
      /* FCS precalculation for UIH frames */
      pbuf_header(p, hdrlen); /* Reuse the buffer for the current header */
      /* Change header values to refelct an UIH frame sent to the responder */
      *((u8_t *)p->payload) &= 0xFB; /* Set direction bit to 0. We are the initiator */
      *((u8_t *)p->payload) |= 0x02; /* Set C/R bit to 1. We are the intitiator */
      ((u8_t *)p->payload)[1] = RFCOMM_UIH;
      pcb->uih_out_fcs = fcs8_crc_calc(p, RFCOMM_UIHCRC_CHECK_LEN);
      ((u8_t *)p->payload)[1]= RFCOMM_UIH_PF;
      pcb->uihpf_out_fcs = fcs8_crc_calc(p, RFCOMM_UIHCRC_CHECK_LEN);

      /* Change header values to reflect an UIH frame sent to the responder */
      *((u8_t *)p->payload) &= 0xFB; /* Set direction bit to 0. We are the intitiator */
      *((u8_t *)p->payload) &= 0xFD; /* Set C/R bit to 0. We are the initiator */
      ((u8_t *)p->payload)[1] = RFCOMM_UIH;
      pcb->uih_in_fcs = fcs8_crc_calc(p, RFCOMM_UIHCRC_CHECK_LEN);
      ((u8_t *)p->payload)[1] = RFCOMM_UIH_PF;
      pcb->uihpf_in_fcs = fcs8_crc_calc(p, RFCOMM_UIHCRC_CHECK_LEN);
      
      /* UIH frame sent on the control channel */
      *((u8_t *)p->payload) &= 0xFB; /* Set direction bit to 0 */
      *((u8_t *)p->payload) |= 0x02; /* Set C/R bit to 1. We are the intitiator */
      ((u8_t *)p->payload)[1] = RFCOMM_UIH;
      pcb->uih0_out_fcs = fcs8_crc_calc(p, RFCOMM_UIHCRC_CHECK_LEN);

      /* Change header values to reflect an UIH frame received on the control channel */
      *((u8_t *)p->payload) &= 0xF9; /* Set C/R bit and direction bit to 0 */
      ((u8_t *)p->payload)[1] = RFCOMM_UIH;
      pcb->uih0_in_fcs = fcs8_crc_calc(p, RFCOMM_UIHCRC_CHECK_LEN);

      if(pcb->cn == 0) {	
	for(tpcb = rfcomm_active_pcbs; tpcb != NULL; tpcb = tpcb->next) {
	  if(bd_addr_cmp(&(tpcb->l2cappcb->remote_bdaddr), &(pcb->l2cappcb->remote_bdaddr)) &&
	     tpcb->state == W4_RFCOMM_MULTIPLEXER) {
	    rfcomm_pn(tpcb, NULL); /* Send a parameter negotiation command to negotiate the 
						     connection settings for a channel that waits for the
						     multiplexer connection to be established */
	    break;
	  }
	}
      } else {
	rfcomm_msc(pcb, 0, NULL); /* Send a modem status command to set V.24 control signals for
				     the RFCOMM connection */
      }
    } else if (pcb->state == W4_RFCOMM_DISC_RSP) {
      //RFCOMM_RMV(&rfcomm_active_pcbs, pcb);
      pcb->state = RFCOMM_CLOSED;
      RFCOMM_EVENT_DISCONNECTED(pcb,ERR_OK,ret);
    } else {
      /* A response without an outstanding request is silently discarded */
    }
    pbuf_free(p);
    break;
  case RFCOMM_DM_PF:
    LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_input: RFCOMM_DM_PF\n"));
  case RFCOMM_DM:
    LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_input: RFCOMM_DM\n"));
    pcb->to = 0;
    //RFCOMM_RMV(&rfcomm_active_pcbs, pcb);
    if(pcb->state ==  W4_RFCOMM_SABM_RSP) {
      pcb->state = RFCOMM_CLOSED;
      RFCOMM_EVENT_CONNECTED(pcb,ERR_CONN,ret);
    } else {
      pcb->state = RFCOMM_CLOSED;
      RFCOMM_EVENT_DISCONNECTED(pcb,ERR_CONN,ret);
    }
    pbuf_free(p);
    break;
  case RFCOMM_DISC:
    LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_input: RFCOMM_DISC\n"));
    //RFCOMM_RMV(&rfcomm_active_pcbs, pcb);
    /* Send UA frame as response to DISC frame */
    ret = rfcomm_ua(l2cappcb, &rfcommhdr);
    pcb->state = RFCOMM_CLOSED;
    RFCOMM_EVENT_DISCONNECTED(pcb,ERR_OK,ret);
    pbuf_free(p);
    break;
  case RFCOMM_UIH_PF: 
    if((rfcommhdr.addr >> 3) == 0) {
      /* Process multiplexer command/response */
      rfcomm_process_msg(pcb, &rfcommhdr, l2cappcb, p);
      pbuf_free(p);
    } else if(pcb->cl == 0xF) {
      /* Process credit based frame */
      if(pcb->rk != 0) {
      --pcb->rk; /* Decrease remote credits */
      }
      LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_input: Received local credits: %d Existing local credits: %d\n", rfcommhdr.k, pcb->k));
      if((pcb->k + rfcommhdr.k) < 255) {
	pcb->k += rfcommhdr.k; /* Increase local credits */
#if RFCOMM_FLOW_QUEUEING
	q = pcb->buf;
        /* Queued packet present? */
        if (q != NULL) {
          /* NULL attached buffer immediately */
          pcb->buf = NULL;
          LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_input: sending queued packet.\n"));
          /* Send the queued packet */
          rfcomm_uih(pcb, pcb->cn, q); 
          /* Free the queued packet */
          pbuf_free(q);
        }
#endif /* RFCOMM_FLOW_QUEUEING */	
      } else {
	pcb->k = 255;
      }
      LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_input: Forward RFCOMM_UIH_PF credit packet to higher layer\n"));
      RFCOMM_EVENT_RECV(pcb,ERR_OK,p,ret); /* Process information. Application must free pbuf */
    } else {
      LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_input: Forward RFCOMM_UIH_PF non credit packet to higher layer\n"));
      RFCOMM_EVENT_RECV(pcb,ERR_OK,p,ret); /* Process information. Application must free pbuf */
    }
    break;
  case RFCOMM_UIH:
    if((rfcommhdr.addr >> 3) == 0) {
      /* Process multiplexer command/response */
      rfcomm_process_msg(pcb, &rfcommhdr, l2cappcb, p);
      pbuf_free(p);
    } else {
      if(pcb->rk != 0) {
	--pcb->rk; /* Decrease remote credits */
      }

      LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_input: Forward RFCOMM_UIH packet to higher layer\n"));
      RFCOMM_EVENT_RECV(pcb,ERR_OK,p,ret); /* Process information. Application must free pbuf */
    }
    break;
  default:
    /* Unknown or illegal frame type. Throw it away! */
    pbuf_free(p);
    break;
  }
  return ERR_OK;
}
/*-----------------------------------------------------------------------------------*/
/* 
 * rfcomm_arg():
 *
 * Used to specify the argument that should be passed callback functions.
 */
/*-----------------------------------------------------------------------------------*/
void
rfcomm_arg(struct rfcomm_pcb *pcb, void *arg)
{
  pcb->callback_arg = arg;
}
/*-----------------------------------------------------------------------------------*/
/* 
 * rfcomm_recv():
 * 
 * Used to specify the function that should be called when a RFCOMM connection 
 * receives data.
 */
/*-----------------------------------------------------------------------------------*/
void
rfcomm_recv(struct rfcomm_pcb *pcb, 
	    err_t (* recv)(void *arg, struct rfcomm_pcb *pcb, struct pbuf *p, err_t err))
{
  pcb->recv = recv;
}
/*-----------------------------------------------------------------------------------*/
/* 
 * rfcomm_disc():
 * 
 * Used to specify the function that should be called when a RFCOMM channel is 
 * disconnected
 */
/*-----------------------------------------------------------------------------------*/
void
rfcomm_disc(struct rfcomm_pcb *pcb, 
	    err_t (* disc)(void *arg, struct rfcomm_pcb *pcb, err_t err))
{
  pcb->disconnected = disc;
}
/*-----------------------------------------------------------------------------------*/
/* 
 * rfcomm_listen():
 * 
 * Set the state of the connection to be LISTEN, which means that it is able to accept 
 * incoming connections. The protocol control block is reallocated in order to consume 
 * less memory. Setting the connection to LISTEN is an irreversible process. Also 
 * specify the function that should be called when the channel has been connected.
 */
/*-----------------------------------------------------------------------------------*/
#if LWBT_LAP
err_t
rfcomm_listen(struct rfcomm_pcb *npcb, u8_t cn, 
	      err_t (* accept)(void *arg, struct rfcomm_pcb *pcb, err_t err))
{
  struct rfcomm_pcb_listen *lpcb;

  if((lpcb = lwbt_memp_malloc(MEMP_RFCOMM_PCB_LISTEN)) == NULL) {
    LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_listen: Could not allocate memory for pcb\n"));
    return ERR_MEM;
  }
  lpcb->cn = cn;
  lpcb->callback_arg = npcb->callback_arg;
  lpcb->accept = accept;
  lpcb->state = RFCOMM_LISTEN;

  lwbt_memp_free(MEMP_RFCOMM_PCB, npcb);
  RFCOMM_REG((struct rfcomm_pcb **)&rfcomm_listen_pcbs, (struct rfcomm_pcb *)lpcb);
  return ERR_OK;
}
#endif /* LWBT_LAP */
/*-----------------------------------------------------------------------------------*/