USB的“Virtual_COM_Port”例程分析--核心文件core.c流程图分析

1.Setup0_Process()

 

 

 

uint8_t Setup0_Process(void)
{//处理主机发来的Setup包
  union
  {
    uint8_t* b;
    uint16_t* w;
  } pBuf;
#if defined STM32F303xE || defined STM32F302x8 
  uint16_t offset = 0;
  pBuf.b = (uint8_t *)( PMAAddr + _GetEPRxAddr(ENDP0));
#else  
  uint16_t offset = 1;
  
  pBuf.b = PMAAddr + (uint8_t *)(_GetEPRxAddr(ENDP0) * 2); /* *2 for 32 bits addr 这是取得端点0接收缓冲区的起始地址*/
    //PMAAddr是包缓冲区起始地址,_GetEPRxAddr(ENDP0)获得端点0描述符表里的接收缓冲区地址。
  //乘以2的原因是,描述符里地址项为16位,使用的是相对偏移
    
#endif
  if (pInformation->ControlState != PAUSE)//如果不处于暂停状态,那么获取主机发送setup包数据
  {
    pInformation->USBbmRequestType = *pBuf.b++; /* bmRequestType  请求类型,表示数据传输方向 请求类型 请求的接收者 */
    pInformation->USBbRequest = *pBuf.b++; /* bRequest 标准请求及代码  */
    pBuf.w += offset;  /* word not accessed because of 32 bits addressing */
    pInformation->USBwValue = ByteSwap(*pBuf.w++); /* wValue */    //regs.c ByteSwap()
    pBuf.w += offset;  /* word not accessed because of 32 bits addressing */
    pInformation->USBwIndex  = ByteSwap(*pBuf.w++); /* wIndex */   //regs.c ByteSwap()
    pBuf.w += offset;  /* word not accessed because of 32 bits addressing */
    pInformation->USBwLength = *pBuf.w; /* wLength */
  }

  pInformation->ControlState = SETTING_UP;//设置控制状态为 SETTING_UP设置
  if (pInformation->USBwLength == 0)
  {
    /* 不需要向主机发送数据的处理 */
    NoData_Setup0();
  }
  else
  {
    /* 需要向主机发送数据的处理 */
    Data_Setup0(); 
  }
  return Post0_Process();
}

 

2.In0_Process()

 

 

 

uint8_t In0_Process(void)
{//处理主机发来的IN包
  uint32_t ControlState = pInformation->ControlState;

    //有数据发送,继续发送数据,或者已经发送完成了
  if ((ControlState == IN_DATA) || (ControlState == LAST_IN_DATA))//进入到这里
  {
    DataStageIn();//第一次取设备描述符只取一次 当前的状态变为WAIT_STATUS_OUT 
                      //表明设备等待状态过程 主机输出0字节
    /* ControlState may be changed outside the function */
    ControlState = pInformation->ControlState;
  }
    
  //处于等待发送状态阶段的数据
  else if (ControlState == WAIT_STATUS_IN) //设置地址状态阶段进入这个程序
  {/* 请求为设置地址且接收者为设备 */
    if ((pInformation->USBbRequest == SET_ADDRESS) &&
        (Type_Recipient == (STANDARD_REQUEST | DEVICE_RECIPIENT)))
    {/* 之前主机把地址发送过来了,但是还没设置地址。现在有了主机的状态数据,就可以开始设置地址了,之后将以此地址和主机进行通信 */
      SetDeviceAddress(pInformation->USBwValue0);//设置使用新的地址
      pUser_Standard_Requests->User_SetDeviceAddress();//这个函数就一个赋值语句,设置设备为地址状态, bDeviceState = ADDRESSED。
    }
    (*pProperty->Process_Status_IN)();//没有处理
    ControlState = STALLED;//停止
  }

  else
  {
    ControlState = STALLED;
  }

  pInformation->ControlState = ControlState;//保存设置状态

  return Post0_Process();    //返回时调用这个函数,没做什么事
}

 

3.Out0_Process()

 

 

 

uint8_t Out0_Process(void)
{//处理主机发来的OUT包
  uint32_t ControlState = pInformation->ControlState;
/* 如果还处于IN_DATA 或 LAST_IN_DATA 状态,那么说明协议出错了,
    设置端点控制状态为STALLED ,同时端点状态会在后面设为STALL */
  if ((ControlState == IN_DATA) || (ControlState == LAST_IN_DATA))
  {
    /* host aborts the transfer before finish 提前终止传输*/
    ControlState = STALLED;//当控制端口在控制传送的数据和状态阶段发送 STALL 时,在收到 SETUP 之前,对该端点的访问都应该返回 STALL。
  }
    /* 如果还处于 OUT_DATA 或 LAST_OUT_DATA 状态那么继续接收 */
  else if ((ControlState == OUT_DATA) || (ControlState == LAST_OUT_DATA))
  {
    DataStageOut();
    ControlState = pInformation->ControlState; /* may be changed outside the function */
  }
   //由于此时状态为WAIT_STATUS_OUT,所以执行下面代码
  else if (ControlState == WAIT_STATUS_OUT) //之前在等待状态包
  {/* 程序运行到这里,说明状态包已经发送过来 */
    (*pProperty->Process_Status_OUT)();//状态包发送过来后的处理函数,也可以不处理,什么都不做
    ControlState = STALLED;     //状态变成了终止发送和接受       
  }


  /* Unexpect state, STALL the endpoint */
  else
  {
    ControlState = STALLED;//控制点的状态为停止
  }

  pInformation->ControlState = ControlState;

  return Post0_Process();//进行实际的状态操作
}

 

4.Data_Setup0()

 

 

 

 

 

 

 

 

/**
  * Function Name  : Data_Setup0.
  * Description    : Proceed the processing of setup request with data stage.
                      控制过程有数据阶段的处理,该程序在控制传输过程中只进一次
  * Input          : None.
  * Output         : None.
  * Return         : None.
  */
void Data_Setup0(void)
{
  uint8_t *(*CopyRoutine)(uint16_t);
  RESULT Result;
  uint32_t Request_No = pInformation->USBbRequest;

  uint32_t Related_Endpoint, Reserved;
  uint32_t wOffset, Status;
    
  CopyRoutine = NULL;   //这是一个函数指针,由用户提供
  wOffset = 0;

  /*GET DESCRIPTOR   获得描述符*/
  if (Request_No == GET_DESCRIPTOR)
  { //如果是标准请求并且接收者是设备
    if (Type_Recipient == (STANDARD_REQUEST | DEVICE_RECIPIENT))
    {
      uint8_t wValue1 = pInformation->USBwValue1;//得到描述符类型 
      if (wValue1 == DEVICE_DESCRIPTOR)          //如果是获取设备描述符
      {
        CopyRoutine = pProperty->GetDeviceDescriptor;//得到设备描述符函数
      }
#ifdef LPM_ENABLED
      else if (wValue1 == DEVICE_BOS_DESCRIPTOR)
      {
        CopyRoutine = pProperty->GetBosDescriptor;
      }
#endif
      else if (wValue1 == CONFIG_DESCRIPTOR)
      {
        CopyRoutine = pProperty->GetConfigDescriptor;//得到配置描述符函数
      }
      else if (wValue1 == STRING_DESCRIPTOR)
      {
        CopyRoutine = pProperty->GetStringDescriptor; //得到字符串描述符函数
      }  /* End of GET_DESCRIPTOR */
    }
  }

  /*GET STATUS  请求为得到状态并且值为0,数据长度为2,偏移为0*/
  else if ((Request_No == GET_STATUS) && (pInformation->USBwValue == 0)
           && (pInformation->USBwLength == 0x0002)
           && (pInformation->USBwIndex1 == 0))
  {
    /* GET STATUS for Device  得到设备的状态*/
    if ((Type_Recipient == (STANDARD_REQUEST | DEVICE_RECIPIENT))
        && (pInformation->USBwIndex == 0))
    {
      CopyRoutine = Standard_GetStatus;//得到状态
    }

    /* GET STATUS for Interface  得到接口的状态*/
    else if (Type_Recipient == (STANDARD_REQUEST | INTERFACE_RECIPIENT))
    {/* 检查当前类的接口设置设备是否合适,如果合适并且当前的配置不是0 */
      if (((*pProperty->Class_Get_Interface_Setting)(pInformation->USBwIndex0, 0) == USB_SUCCESS)
          && (pInformation->Current_Configuration != 0))
      {
        CopyRoutine = Standard_GetStatus;
      }
    }

    /* GET STATUS for EndPoint   得到端点的状态*/
    else if (Type_Recipient == (STANDARD_REQUEST | ENDPOINT_RECIPIENT))
    {
      Related_Endpoint = (pInformation->USBwIndex0 & 0x0f);
      Reserved = pInformation->USBwIndex0 & 0x70;

      if (ValBit(pInformation->USBwIndex0, 7))
      {
        /*Get Status of endpoint & stall the request if the related_ENdpoint
        is Disabled*/
        Status = _GetEPTxStatus(Related_Endpoint);
      }
      else
      {
        Status = _GetEPRxStatus(Related_Endpoint);
      }

      if ((Related_Endpoint < Device_Table.Total_Endpoint) && (Reserved == 0)
          && (Status != 0))
      {
        CopyRoutine = Standard_GetStatus;
      }
    }

  }

  /*GET CONFIGURATION  得到配置*/
  else if (Request_No == GET_CONFIGURATION)
  {
    if (Type_Recipient == (STANDARD_REQUEST | DEVICE_RECIPIENT))
    {
      CopyRoutine = Standard_GetConfiguration;
    }
  }
  /*GET INTERFACE   得到接口*/
  else if (Request_No == GET_INTERFACE)
  {
    if ((Type_Recipient == (STANDARD_REQUEST | INTERFACE_RECIPIENT))
        && (pInformation->Current_Configuration != 0) && (pInformation->USBwValue == 0)
        && (pInformation->USBwIndex1 == 0) && (pInformation->USBwLength == 0x0001)
        && ((*pProperty->Class_Get_Interface_Setting)(pInformation->USBwIndex0, 0) == USB_SUCCESS))
    {
      CopyRoutine = Standard_GetInterface;
    }

  }
  
  if (CopyRoutine)    //判断是否取得描述符
  {
    pInformation->Ctrl_Info.Usb_wOffset = wOffset;
    pInformation->Ctrl_Info.CopyData = CopyRoutine;//将函数指针传入到结构体变量中
    /* sb in the original the cast to word was directly */
    /* now the cast is made step by step */
    (*CopyRoutine)(0);   //得到发送数据大小
    Result = USB_SUCCESS;
  }
  else
  {//之前的请求都不支持,看看是不是类请求
    Result = (*pProperty->Class_Data_Setup)(pInformation->USBbRequest);
    if (Result == USB_NOT_READY)
    {
      pInformation->ControlState = PAUSE;
      return;
    }
   }
/*0xffff表示数据没有准备好*/
  if (pInformation->Ctrl_Info.Usb_wLength == 0xFFFF)
  {
    /* Data is not ready, wait it */
    pInformation->ControlState = PAUSE;//暂停
    return;
  }
    /* 设备不支持或者不需要发送数据 */
  if ((Result == USB_UNSUPPORT) || (pInformation->Ctrl_Info.Usb_wLength == 0))
  {
    /* Unsupported request */
    pInformation->ControlState = STALLED;
    return;
  }


  if (ValBit(pInformation->USBbmRequestType, 7))//D7表示数据传输方向:设备向主机
  {
    /* Device ==> Host :设备到主机*/
    __IO uint32_t wLength = pInformation->USBwLength;  //要发送的数据  //这个一般是64  

    /* Restrict the data length to be the one host asks for */  //设备描述符长度18
    if (pInformation->Ctrl_Info.Usb_wLength > wLength)//如果实际的数据长度大于请求的数据长度
    {
      pInformation->Ctrl_Info.Usb_wLength = wLength;//那么只发送请求的长度(64字节)
    }
    
    else if (pInformation->Ctrl_Info.Usb_wLength < pInformation->USBwLength)//如果实际的数据长度小于请求的数据长度
    {
      if (pInformation->Ctrl_Info.Usb_wLength < pProperty->MaxPacketSize)//可以一个包发送出去
      {
        Data_Mul_MaxPacketSize = FALSE;
      }
      else if ((pInformation->Ctrl_Info.Usb_wLength % pProperty->MaxPacketSize) == 0)//可以整数包发送
      {
        Data_Mul_MaxPacketSize = TRUE;//整包发送
      }
            //还有一种情况:需要多次发送而最后一个包不足 在后面处理
    }   

    pInformation->Ctrl_Info.PacketSize = pProperty->MaxPacketSize;//只有这一次确定了控制端点的发送包大小
    DataStageIn();   //调用这个函数实现描述符的输出准备
  }
  else//主机向设备
  {
    pInformation->ControlState = OUT_DATA;
    vSetEPRxStatus(EP_RX_VALID); /* enable for next data reception 使能接收*/
  }

  return;
}

 

5.NoData_Setup0()

 

 

 

 

 

 

 

 

 

 

 

/**
  * Function Name  : NoData_Setup0.
  * Description    : Proceed the processing of setup request without data stage.
                     处理没有数据阶段的建立过程
  * Input          : None.
  * Output         : None.
  * Return         : None.
  */
void NoData_Setup0(void)
{
  RESULT Result = USB_UNSUPPORT;
  uint32_t RequestNo = pInformation->USBbRequest;
  uint32_t ControlState;
/* 标准请求且接收者为设备 */
  if (Type_Recipient == (STANDARD_REQUEST | DEVICE_RECIPIENT)) 
  {
    /* Device Request*/
    /* SET_CONFIGURATION  设置配置*/
    if (RequestNo == SET_CONFIGURATION)
    {
      Result = Standard_SetConfiguration();
    }

    /*SET ADDRESS    设置地址*/
    else if (RequestNo == SET_ADDRESS)
    {
      if ((pInformation->USBwValue0 > 127) || (pInformation->USBwValue1 != 0)
          || (pInformation->USBwIndex != 0)
          || (pInformation->Current_Configuration != 0))
        /* Device Address should be 127 or less*/
      {/* 参数检查发现错误 */
        ControlState = STALLED;//控制状态设置为停止状态
        goto exit_NoData_Setup0;//结束
      }
      else//确认地址无误
      {/* 这里只返回成功,表明可以发送状态阶段的数据了,
                但是还不能设置地址,还必须等到状态数据被主机接收到才行 */
        Result = USB_SUCCESS;
      }
    }
    /*SET FEATURE for Device  设置设备特性*/
    else if (RequestNo == SET_FEATURE)
    {
            //特性选择为远程唤醒并且当前的特性为远程唤醒
      if ((pInformation->USBwValue0 == DEVICE_REMOTE_WAKEUP) \
          && (pInformation->USBwIndex == 0))
      {
        Result = Standard_SetDeviceFeature();
      }
      else
      {
        Result = USB_UNSUPPORT;
      }
    }
    /*Clear FEATURE for Device */
    else if (RequestNo == CLEAR_FEATURE)
    {
      if (pInformation->USBwValue0 == DEVICE_REMOTE_WAKEUP
          && pInformation->USBwIndex == 0
          && ValBit(pInformation->Current_Feature, 5))
      {
        DevRemoteWakeup = 0;
        Result = Standard_ClearFeature();
      }
      else
      {
        Result = USB_UNSUPPORT;
      }
    }

  }

  /* Interface Request  接口请求*/
  else if (Type_Recipient == (STANDARD_REQUEST | INTERFACE_RECIPIENT))
  {
    /*SET INTERFACE  设置特性*/
    if (RequestNo == SET_INTERFACE)
    {
      Result = Standard_SetInterface();//设置接口
    }
  }

  /* EndPoint Request   端点请求*/
  else if (Type_Recipient == (STANDARD_REQUEST | ENDPOINT_RECIPIENT))
  {
    /*CLEAR FEATURE for EndPoint*/
    if (RequestNo == CLEAR_FEATURE)
    {
      Result = Standard_ClearFeature();
    }
    /* SET FEATURE for EndPoint*/
    else if (RequestNo == SET_FEATURE)
    {
      Result = Standard_SetEndPointFeature();
    }
  }
  else
  {
    Result = USB_UNSUPPORT;
  }

//再检查是不是其他情况
  if (Result != USB_SUCCESS)
  {
    Result = (*pProperty->Class_NoData_Setup)(RequestNo);
    if (Result == USB_NOT_READY)
    {
      ControlState = PAUSE;
      goto exit_NoData_Setup0;
    }
  }

  if (Result != USB_SUCCESS)
  {/* 检查了所有可能的情况还是返回 不是USB_SUCCESS */
    ControlState = STALLED;//停止
    goto exit_NoData_Setup0;
  }

  ControlState = WAIT_STATUS_IN;/* After no data stage SETUP :说明设置地址没有做任何工作*/

  USB_StatusIn();//发送状态数据 无数据的控制传输需要发送状态数据,表明数据接收完成
                 //建立阶段后直接进入状态阶段,主机发IN令牌包,设备返回0字节数据包,主机再ACK。
exit_NoData_Setup0:
  pInformation->ControlState = ControlState;
  return;
}

 

6.DataStageIn()

 

 

 

 

 

/**
  * Function Name  : DataStageIn.
  * Description    : Data stage of a Control Read Transfer.
                     控制传输的发送数据阶段,该程序在控制传输过程中会多次进入        
  * Input          : None.
  * Output         : None.
  * Return         : None.
  */
void DataStageIn(void)
{/* 有可能数据已经发送完成了,主机没有发送状态数据,而是继续发送了 IN 令牌包,那么设备的状态设为 WAIT_STATUS_OUT,也就是说端点的反应为 NAK  */    
    /* 此时当主机收到 NAK 握手包时就不会再发 IN 令牌包过来了,而是发送OUT令牌包,同时把 0 字节数据发送过来了 */
    
  ENDPOINT_INFO *pEPinfo = &pInformation->Ctrl_Info;  //端点信息保存在指针变量中
  uint32_t save_wLength = pEPinfo->Usb_wLength;       //得到字符的长度
  uint32_t ControlState = pInformation->ControlState; //得到当前的状态

  uint8_t *DataBuffer;
  uint32_t Length;
/* 如果要发送的数据长度为0并且是最后一次数据,也就是说数据都发送完了 */
  if ((save_wLength == 0) && (ControlState == LAST_IN_DATA))
  {//程序运行到这里说明数据都发送完了,这里分两种情况:整包发送和非整包发送,两种处理情况不一样
    if(Data_Mul_MaxPacketSize == TRUE) //刚好能整包进行发送则需要发送0字节数据表明数据发送结束
    {
      /* No more data to send and empty packet */
      Send0LengthData();              //发送空包并储存状态
      ControlState = LAST_IN_DATA;    //还有一次数据要发送,就是当前的0字节数据
            //整包发送完成之后只要发送一次0字节数据就行了,
            //在SETUP来到之前都不会进入,防止当前的0数据包发送完成后进入
      Data_Mul_MaxPacketSize = FALSE;
    }
    else //字符的长度比数据包要小
    {//数据已经发送完
      /* No more data to send so STALL the TX Status*/
      ControlState = WAIT_STATUS_OUT;//发送完成了,等待接收状态数据包
      vSetEPTxStatus(EP_TX_STALL);//设置为STALL,既然已经发送完了,发送端点就可以停止了
    }
    goto Expect_Status_Out;//因为数据发送完成了,那么直接可以结束了
  }

  Length = pEPinfo->PacketSize;//最大发送字节数//得到数据包大小 64字节
  //要发送的数据小于等于最大发送大小,则处于 LAST_IN_DATA    18字节<64字节  ControlState = LAST_IN_DATA
  ControlState = (save_wLength <= Length) ? LAST_IN_DATA : IN_DATA;

  if (Length > save_wLength)//如果要发送的数据小于最大包长
  {
    Length = save_wLength;//就按照实际大小发送
  }
  //否则以一个包的最大数据量进行发送
    //以下是主要执行代码:
  DataBuffer = (*pEPinfo->CopyData)(Length);                   //得到要发送的数据地址(前面已经确定了发送数据的大小),这里共18个字节
  
  UserToPMABufferCopy(DataBuffer, GetEPTxAddr(ENDP0), Length); //将要发送的数据放到发送缓冲区中

  SetEPTxCount(ENDP0, Length);       //设置发送字节的数目:18

  pEPinfo->Usb_wLength -= Length;//更新需要的数据长度//Usb_wLength等于0
  pEPinfo->Usb_wOffset += Length;//更新最新包数据偏移//偏移到18
  vSetEPTxStatus(EP_TX_VALID);//使能发送端点 只要主机的IN令牌包一来 SIE就会将描述符返回给主机
/* 设置端点为接收有效 */
  USB_StatusOut();/* Expect the host to abort the data IN stage :使能接收也有效,主机可以取消IN*/

Expect_Status_Out:
  pInformation->ControlState = ControlState;//保存控制状态
}

 

7.DataStageOut()

 

 

 

 

 

void DataStageOut(void)
{
  ENDPOINT_INFO *pEPinfo = &pInformation->Ctrl_Info;
  uint32_t save_rLength;

  save_rLength = pEPinfo->Usb_rLength;//保存要读的数据长度

  if (pEPinfo->CopyData && save_rLength)
  {
    uint8_t *Buffer;
    uint32_t Length;

    Length = pEPinfo->PacketSize;//得到数据包大小
    if (Length > save_rLength)
    {
      Length = save_rLength;
    }

    Buffer = (*pEPinfo->CopyData)(Length);//得到数据的地址 
    pEPinfo->Usb_rLength -= Length;//更新需要的数据长度
    pEPinfo->Usb_rOffset += Length;//更新最新包数据偏移
    PMAToUserBufferCopy(Buffer, GetEPRxAddr(ENDP0), Length);//复制数据到用户数据Buffer

  }

  if (pEPinfo->Usb_rLength != 0)//如果还有数据需要接收
  {
    vSetEPRxStatus(EP_RX_VALID);/* re-enable for next data reception使能下一次数据接收 */
    SetEPTxCount(ENDP0, 0);//发送0字节数据,确认数据接收成功
    vSetEPTxStatus(EP_TX_VALID);/* Expect the host to abort the data OUT stage 希望主机取消数据阶段*/
  }
  /* Set the next State设置下一次阶段*/
  if (pEPinfo->Usb_rLength >= pEPinfo->PacketSize)
  {
    pInformation->ControlState = OUT_DATA;
  }
  else
  {
    if (pEPinfo->Usb_rLength > 0)//还有最后一次数据接收
    {
      pInformation->ControlState = LAST_OUT_DATA;
    }
    else if (pEPinfo->Usb_rLength == 0)//没有数据接收
    {
      /* USB spec: section 8.5.3.1 Reporting Status Results */
      pInformation->ControlState = WAIT_STATUS_IN;
      (*pProperty->Process_Status_IN)();      
      if (pInformation->ControlState == STALLED)
      {
        /* command failed to complete: 命令未能完成
        in this case we should return a STALL */
        vSetEPRxStatus(EP_RX_STALL);
        vSetEPTxStatus(EP_TX_STALL);//将端点0的发送和接收都设置为:STALL
      }
      /* command completed successfully: 命令成功完成
      send a zero-length packet during status stage */
      else USB_StatusIn();//发送状态阶段的数据 0字节
    }
  }
}

 

8.Post0_Process()

 

 

 

/**
  * Function Name  : Post0_Process
  * Description    : Stall the Endpoint 0 in case of error.
                     在出现错误的情况下暂停端点0
  * Input          : None.
  * Output         : None.
  * Return         : - 0 if the control State is in PAUSE
  *                  - 1 if not.
  */
uint8_t Post0_Process(void)
{
   
  SetEPRxCount(ENDP0, Device_Property.MaxPacketSize);//设置最大接收包为64字节

  if (pInformation->ControlState == STALLED) 
  {
    vSetEPRxStatus(EP_RX_STALL); 
    vSetEPTxStatus(EP_TX_STALL);//将端点0的发送和接收都设置为:STALL,这样只接收SETUP令牌包。
  }

  return (pInformation->ControlState == PAUSE);
}

 

9.SetDeviceAddress()

 

 

 

/**
  * Function Name  : SetDeviceAddress.
  * Description    : Set the device and all the used Endpoints addresses.
                     设置设备和所有使用的端点地址
  * Input          : - Val: device address.
  * Output         : None.
  * Return         : None.
  */
void SetDeviceAddress(uint8_t Val)
{
  uint32_t i;
  uint32_t nEP = Device_Table.Total_Endpoint;

  /* set address in every used endpoint */
  for (i = 0; i < nEP; i++)//在每个使用的端点中设置地址
  {
    _SetEPAddress((uint8_t)i, (uint8_t)i);
  } /* for */
  _SetDADDR(Val | DADDR_EF); /* set device address and enable function 设置设备地址和启用功能*/ 
}

 

posted @ 2021-04-02 00:51  万事兴  阅读(757)  评论(0)    收藏  举报