#define _USBTMC_C_ #include #include "lpc176x.h" #include "stdio.h" #include "usb_hardware.h" #include "queue.h" #include "mem.h" #ifndef USBTMC // #endif в конце файла #pragma message(" _____________________________________________________________ " ) #pragma message(" ############################################################# " ) #pragma message(" # USBTMC Project, Alpha Version # " ) #pragma message(" # v 2.1 - Версия проекта # " ) #pragma message(" # Проект поддерживает старый протокол USB-CONTROL # " ) #pragma message(" # Чтобы включть TMC нужно объявить макрос 'USBTMC' в файле # " ) #pragma message(" # project.h # " ) #pragma message(" # 21-сен-2011 15:00 # " ) #pragma message(" ############################################################# " ) #pragma message(" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ " ) #else #include "gpib_parser.h" #include "usb_enumeration.h" //#include "spi.h" #include "hal.h" #include "usbtmc.h" #include "utils.h" #include "i2_c.h" #include "options.h" #include "usb_options.h" #include "endpoints.h" #include "usb_application.h" #include "usb_proto.h" // // Все связанное с USBTMC протоколом находится в этом файле, а // также в файле GPIB_parser.c // #include "../usbtmc/gpib.h" BYTE SerialNumber[9]; unsigned short int DeviceID = 0; BYTE gEP2BufOutExpand[USB_MAX_BULKOUT_BUFFERSIZE]; // перед gEP2BufInExpand BYTE gEP2BufInExpand[USB_MAX_BULKIN_BUFFERSIZE]; // следом за gEP2BufOutExpand ( на случай переполнения ) BYTE gGPIBErrorQueue[USB_MAX_ERROR_QUEUE]; BYTE gGPIBFunctionContext[USB_MAX_FUNCCONTEXT]; extern const BYTE abDescriptors_USBTMC[]; extern const BYTE abDescriptors_Proprietary[]; extern BYTE SerialNumberDescriptor[]; const char *DeviceIDs[] = { "SC6000T", "SC8000T" }; BOOL bUSBTMCEnable =FALSE; BOOL usbtmc_RaiseError_CatDescription( USB_DEVICE_INFO * udi, const char * description, unsigned int desc_len ); BOOL usbtmc_RaiseError( USB_DEVICE_INFO * udi, GPIB_ErrorClass_t errClass, int dwCode, const char * description, unsigned int desc_len ); // ========================================================================================================================================= void ReadDeviceInfo( void ) { { s_memset( SerialNumber, '0', 8 ); DeviceID = 0; } SerialNumber[8] = '\0'; } // ========================================================================================================================================= void usbtmc_HOOK_control_out( USB_DEVICE_INFO * udi ); // ========================================================================================================================================= // volatile size_t debug_call_ep2irq_1 = 0; // volatile size_t debug_call_ep2irq_2 = 0; // volatile size_t debug_call_ep2irq_3 = 0; // volatile size_t debug_call_ep2irq_4 = 0; // volatile size_t debug_call_ep2irq_5 = 0; // Обработчик прерываний EP2 (IN/OUT) // вызывается по адресу из переменной, инициализация в UsbtmcInit() void USB_Interrupt( unsigned int EndpointStatus ) { // debug_call_ep2irq_5++; if( USB_EVENT_ENDPOINT( EndpointStatus,USB_EP_PHY_ADDRESS_BULK_OUT ) ) { //debug_call_ep2irq_1++; usb_EP2_rx_int_func(&gUSBInfo); //-- Bulk-OUT } if( USB_EVENT_ENDPOINT( EndpointStatus,USB_EP_PHY_ADDRESS_BULK_IN ) ) { //debug_call_ep2irq_2++; usb_EP2_tx_int_func(&gUSBInfo); //-- Bulk-IN } if( USB_EVENT_ENDPOINT( EndpointStatus,USB_EP_PHY_ADDRESS_INT_IN ) ) { // debug_call_ep2irq_3++; usb_EP1_tx_int_func(&gUSBInfo); //-- Int-IN } if(USB_EVENT_ENDPOINT(EndpointStatus,USB_EP_PADDRESS_OUT )) // Control OUT { //debug_call_ep2irq_4++; usbtmc_HOOK_control_out(&gUSBInfo); //-- IN } } // ========================================================================================================================================= IRQ_FUNCTION_ADDRESS UsbtmcInit(void) { USB_DEVICE_INFO * udi = &gUSBInfo; bUSBTMCEnable = FALSE; #ifdef USBTMC_SUPPORT_OLD if(USB_INTERFACE_CONT==GetInterface(udi)) { //ReadDeviceInfo(); --- replaced with VariableInit_Post() // DevDepInit(); --- replaced with VariableInit_Post() return 0; // если было произведено переключение интерфейса -> выходим } #endif bUSBTMCEnable = TRUE; udi->Descriptors = (BYTE*)&abDescriptors_USBTMC[0]; //-- Descriptors udi->EPBulkStatus.OutPipe.pDefaultBuffer = gEP2BufOutExpand;//-- EP2 buffer udi->EPBulkStatus.InPipe.pDefaultBuffer = gEP2BufInExpand;//-- EP2 buffer udi->EPBulkStatus.InPipe.pfTransferEndHandler = usbtmc_EndOfTransfer; udi->EPBulkStatus.OutPipe.pfTransferEndHandler = NULL; memset( (BYTE*)&udi->GPIBFunctionContext, 0, sizeof(udi->GPIBFunctionContext) ); usb_reset_endpoint_status( &udi->EPBulkStatus ); //-- configure logical-EP2 [In-EP5, Out-EP4, Bulk] // ДОСТАТОЧНО ПОДМЕНИТЬ udi->Descriptors // -- usb_config_endpoints( udi->Descriptors ) -- // вызывать usb_config_endpoints не надо, вызов в InitUSB() udi->usbtmcGpib.gpib_root = GPIB_InitializeAllCommands(); // init SCPI command tree usbtmc_state_machine_init( &udi->usbtmcGpib.StateMachine ); /* ---------- обязательная защита банка памяти 0 (заводские калибровки) ------------- */ /* WARNING: команда защита памяти перенесена, теперь она выполняется при инициализации класса ACMBase() вне зависимости от используемого протокола (USBTMC/PLANAR) */ // SPI должен быть инициализирован // USB_SETUP_PACKET dummy_setup_packet; // s_memset( &dummy_setup_packet, 0, sizeof(USB_SETUP_PACKET)); // usbapp_SET_PROTECT_FLASH( &dummy_setup_packet ); // защита банка памяти 0 /* ---------------------------------------------------------------------------------- */ UsbtmcDevDepInit( udi ); return (IRQ_FUNCTION_ADDRESS)USB_Interrupt; } // ==================================================================================================================== // перехват событий EP0 // void usbtmc_HOOK_control_out( USB_DEVICE_INFO * udi ) // перехват сообщений контрольного протокола (OUT) { // после его обработки в usb_enumeration USB_SETUP_PACKET * usb_setup_packet = &udi->EP0SetupPacket; switch( usb_setup_packet->bmRequestType & USB_CMD_MASK_COMMON ) { case USB_CMD_STD_DEV_OUT: //-- STANDARD OUT device requests { switch(usb_setup_packet->bRequest) { case CLEAR_FEATURE: { if( usb_setup_packet->wValue == 0 ) // for EP { switch(usb_setup_packet->wIndex) { case USB_EP_LOG_ADDRESS_BULK_IN: { usbtmc_reset_bulkrespond_status (&udi->BulkRespondStatus); usb_reset_pipe_status (&udi->EPBulkStatus.InPipe); s_memset( (BYTE*)usb_setup_packet, 0x00, sizeof(USB_SETUP_PACKET) ); // пакет уже отработан, стриаем пакет чтобы не попасть сюда снова при любом другом прерывании } break; case USB_EP_LOG_ADDRESS_BULK_OUT: { usbtmc_reset_bulkmessage_status (&udi->BulkMessageStatus); usb_reset_pipe_status (&udi->EPBulkStatus.OutPipe); s_memset( (BYTE*)usb_setup_packet, 0x00, sizeof(USB_SETUP_PACKET) ); // пакет уже отработан, стриаем пакет чтобы не попасть сюда снова при любом другом прерывании } break; } } } break; } // switch(usb_setup_packet->bRequest) } // case USB_CMD_STD_DEV_OUT break; } } // ========================================================================================================================================= void UsbtmcDevDepInit( USB_DEVICE_INFO * udi ) { /* ReadDeviceInfo(); --- replaced, VariableInit_Post */ GPIBInit( udi ); GPIB_DevDepInit( udi ); } // ========================================================================================================================================= void usbtmc_state_machine_init( USB_USBTMC_GPIBEMULATION_STATEMACHINE * pStateMachine ) { s_memset( (BYTE*)pStateMachine, 0x00, sizeof(USB_USBTMC_GPIBEMULATION_STATEMACHINE) ); pStateMachine->ESR |= (1<qErrorQueue); } // ========================================================================================================================================= // @ @ @@@@@ @@@@@ @@@@@@ @ @ @@@@ @@@@ @@@@@ @@@@@ @@@@ @@@@@ // @ @ @ @ @ @ @@ @@ @ @ @ @ @ @ @ @ @ @ @ @ // @ @ @@@@@@ @@@@@ @ @ @@ @ @ @@@@@@ @@@@@ @@@@@ @ @ @@@@@ // @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ // @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ // @@@@ @@@@ @@@@@ @@@ @ @ @@@@ @@@@ @ @@ @ @@ @@@@ @ @@ void usbtmc_GenRecieveError( USB_DEVICE_INFO * udi ) { //usbtmc_RaiseError( udi, GPIB_ERROR_ID_EXE, ERROR_USBTMC_BUFFER_OVERFLOW_OUT, "", 0); /* 27/08/18 usbtmc_RaiseError( udi, GPIB_ERROR_ID_EXE, ERROR_USBTMC_INTERNAL, "", 0); */ if( ! usbtmc_RaiseError( udi, errClass_Device, ERROR_USBTMC_INTERNAL, "", 0) ) { usbtmc_ErrorQueue_RestoreCheckpoint( udi ); } } BOOL usbtmc_RaiseError_CatDescription( USB_DEVICE_INFO * udi, const char * description, unsigned int desc_len ) { // дополняет usbtmc_RaiseError(). нужна для добавления description предыдущей добавленной ошибки в очередь // Позволяет не создавать новый элемент в очереди (с кодом ошибки) а добавить текст к предыдущей QUEUE * pQueue = &udi->usbtmcGpib.StateMachine.qErrorQueue; return queue_cat( pQueue, description, desc_len ); } // ========================================================================================================================================= BOOL usbtmc_ErrorQueue_CreateCheckpoint( USB_DEVICE_INFO * udi ) { QUEUE * pQueueSrc = &udi->usbtmcGpib.StateMachine.qErrorQueue; QUEUE * pQueueDst = &udi->usbtmcGpib.StateMachine.qErrorQueue_backup; *pQueueDst = *pQueueSrc; return TRUE; } BOOL usbtmc_ErrorQueue_RestoreCheckpoint( USB_DEVICE_INFO * udi ) { QUEUE * pQueueDst = &udi->usbtmcGpib.StateMachine.qErrorQueue; QUEUE * pQueueSrc = &udi->usbtmcGpib.StateMachine.qErrorQueue_backup; *pQueueDst = *pQueueSrc; return TRUE; } // ========================================================================================================================================= BOOL usbtmc_RaiseError( USB_DEVICE_INFO * udi, GPIB_ErrorClass_t errClass, int dwCode, const char * pDesc, size_t nDescLen ) { QUEUE * pQueue = &udi->usbtmcGpib.StateMachine.qErrorQueue; BOOL rc = FALSE; uint8_t aMemBuf[64]; // --------------------------------------------------------- usbtmc_ErrorQueue_CreateCheckpoint( udi ); // --------------------------------------------------------- if( pDesc == NULL ) nDescLen = 0; if( nDescLen > sizeof(aMemBuf) - 2*sizeof(uint32_t) ) { nDescLen = sizeof(aMemBuf) - 2*sizeof(uint32_t); } // --------------------------------------------------------- { size_t nCount = 0; if( !queue_getcount( pQueue, &nCount ) ) return FALSE; if( nCount > 16 ) return FALSE; } // --------------------------------------------------------- uint32_t * pCode = (unsigned int*)((ptrdiff_t)aMemBuf + 0); // error code uint32_t * pClass = (unsigned int*)((ptrdiff_t)aMemBuf + sizeof(uint32_t)); // error class // --------------------------------------------------------- *pCode = dwCode; *pClass = errClass; // 27/08/18, *pCode = ErrorID // --------------------------------------------------------- if( NULL != pDesc && 0 != nDescLen ) { char * pText = (char*)((ptrdiff_t)aMemBuf + 2* sizeof(uint32_t)); // error description memcpy( pText, pDesc, nDescLen ); } // --------------------------------------------------------- switch( errClass ) { case errClass_Command: GPIB_SET_CME(); break; case errClass_Execution: GPIB_SET_EXE(); break; case errClass_Query: GPIB_SET_QRE(); break; case errClass_Device: GPIB_SET_DDE(); break; default: GPIB_SET_DDE(); break; } // --------------------------------------------------------- if( TRUE == queue_add( pQueue, aMemBuf, 2* sizeof(uint32_t) + nDescLen ) ) { GPIB_SET_EAV(); rc = TRUE; } return rc; } // ========================================================================================================================================= BOOL usbtmc_ClearLastError( USB_DEVICE_INFO * udi ) { char dummy[64]; usbtmc_GetErrorText( udi, dummy, 64 ); return (GPIB_GET_EAV()); } // ========================================================================================================================================= int usbtmc_GetRegisteredErrorDescription( int dwErrCode, char * pOutput, unsigned int cbMaxLength ) { switch(dwErrCode) { case ERROR_USBTMC_PARAMETER: return snprintf(pOutput, cbMaxLength, "%s", "Parameter error "); case ERROR_USBTMC_EXECUTION: return snprintf(pOutput, cbMaxLength, "%s", "Execution error "); case ERROR_USBTMC_DATANOTFOUND: return snprintf(pOutput, cbMaxLength, "%s", "No data found "); case ERROR_USBTMC_NOTHERMCOMPDATA: return snprintf(pOutput, cbMaxLength, "%s", "No data found "); case ERROR_USBTMC_ARRAY_CORRUPTED: return snprintf(pOutput, cbMaxLength, "%s", "Data array corrupted "); //case ERROR_USBTMC_HEADER_CORRUPTED: //return snprintf(pOutput, cbMaxLength, "%s", "Header corrupted "); case ERROR_USBTMC_INTERNAL: return snprintf(pOutput, cbMaxLength, "%s", "Internal execution error"); case ERROR_USBTMC_TOOMANY_REQUESTS: return snprintf(pOutput, cbMaxLength, "%s", "Too many requests in line ("); case ERROR_USBTMC_INVALID_HEADER: //case ERROR_USBTMC_COMMANDONLY: //case ERROR_USBTMC_REQUESTONLY: // return snprintf(pOutput, cbMaxLength, "%s", "Invalid header "); //case ERROR_USBTMC_BUFFER_OVERFLOW_OUT: //return snprintf(pOutput, cbMaxLength, "%s", "IN-Buffer overflow"); // IN-Buffer: Host->Dev //case ERROR_USBTMC_BUFFER_OVERFLOW_IN: //return snprintf(pOutput, cbMaxLength, "%s", "OUT-Buffer overflow"); // OUT-Buffer: Dev->Host } return 0; } // ========================================================================================================================================= int usbtmc_GetErrorText( USB_DEVICE_INFO * udi, char * pText, unsigned int dwMaxSize) { QUEUE * pQueue = &udi->usbtmcGpib.StateMachine.qErrorQueue; int rc = 0; size_t nCount = 0; if( queue_getcount( pQueue, &nCount ) ) { if( nCount == 0 ) { GPIB_CLR_EAV(); strcpy( pText, "0, \"No error\"" ); rc = strlen( pText ); } else { if( queue_get_topitemsize(pQueue, (unsigned int*)&rc) ) { if( queue_get( pQueue, pText, dwMaxSize, (unsigned int*)&rc) ) { // --------------------------------------------------------------- // 80 00 00 00 B L A B L A B L A // _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ... _ _ _ // \ / \ / // code error description // // // 1 2 8 , " BLA BLA BLA " // _ _ _ ... _ _ _ _ _ _ _ _ _ _ _ ... _ _ _ // \ / \ / // code error description // // --------------------------------------------------------------- // the description is the rest bytes after two integer codes (8bytes) size_t nDescLen = ( rc - sizeof(int) * 2 ); if( rc > 0 ) { if( pText[ rc - 1 ] == '\0' ) { nDescLen--; // cut off the null-term } } // retrieve the error number int dwCode = *((int*)pText); //unsigned int ID = *((unsigned int*)pText + 1); char tmpBuf[64]; size_t nTmpBufSize = sizeof(tmpBuf); nCount = snprintf( tmpBuf, nTmpBufSize,"%d, \"", dwCode); if( nCount >= nTmpBufSize ) nCount = 0; else nTmpBufSize -= nCount; // retrieve the common description length // note: cbMaxLength is 0 size_t nCommonDescLength = usbtmc_GetRegisteredErrorDescription( dwCode, &tmpBuf[nCount], 0 ); if( dwMaxSize < nCount + nCommonDescLength + nDescLen ) { // not enougth free space in the buffer rc = 0; } else { // The description begins with the (sizeof(int) * 2) character // and has a nDescLen character length // We have @nCount bytes in @tmpBuf to print // It is required to move the description in the @pText // to the (nCount + nCommonDescLength), due to it is // required to print firstly the data from @pText, then // the @nCommonDescLength bytes of data returned by @usbtmc_GetRegisteredErrorDescription(), // and then the data that actually is in @pText (@nDescLen length) // [nCount] [nCommonDescLength] [nDescLen] // So, we know, that is required to allocate the space for: // - @nCount bytes of @tmpBuf // - @nCommonDescLength bytes of common description (usbtmc_GetRegisteredErrorDescription) // Let's move the description: memmove( &pText[nCount + nCommonDescLength], &pText[sizeof(int) * 2], nDescLen ); // copy @tmpBuf to the begin of @pText memcpy( &pText[0], tmpBuf, nCount ); // and actually print the common description to the @pText after the @tmpBuf contents char x = pText[nCount+nCommonDescLength]; // this character will be filled with null-term by usbtmc_GetRegisteredErrorDescription() usbtmc_GetRegisteredErrorDescription( dwCode, &pText[nCount], (nCommonDescLength + 1) ); pText[nCount+nCommonDescLength] = x; rc = nCount + nCommonDescLength + nDescLen; if( rc < dwMaxSize ) { pText[rc] = '\"'; rc++; } } } else { rc = 0; } } if( queue_getcount(pQueue, &nCount)==TRUE && nCount==0) GPIB_CLR_EAV(); } } return rc; } // ========================================================================================================================================= unsigned int usbtmc_EndOfTransfer( void * vudi ) { USB_DEVICE_INFO * udi = (USB_DEVICE_INFO *) vudi; USB_EP_STATUS * pbulk_status = (USB_EP_STATUS *) &udi->EPBulkStatus; USB_PIPE_ENTRY_IN * pInPipe = (USB_PIPE_ENTRY_IN *) &pbulk_status->InPipe; USB_BULKRESPOND_STATUS * pBulkRespond = (USB_BULKRESPOND_STATUS*) &udi->BulkRespondStatus; // вызывается когда в Pipe кончаются данные: // InPipe имеет два поля: dwAllLength и dwLength // Первое задает коичество данных, которые нужно передать // Второе - только то что вмещается в выходной буфер (или сколько запросил Host, смотря, что меньше) // ----------------------------------- if( pInPipe->dwLength != 0 ) return 0; // ----------------------------------- if ( pBulkRespond->dwDeviceOut > 0 && pInPipe->dwLength == 0 ) { unsigned int dwBytesRemaining = pBulkRespond->dwDeviceOut; s_memcpy( pInPipe->pDefaultBuffer + sizeof(BULKIN_HEADER), // сдвигаем буфер к началу. Оставляем место под заголовок pInPipe->pData, // текущий указатель на данные dwBytesRemaining); // оставшееся количество непереданных данных // --------------------------- usbtmc_init_intransfer( udi ); // сброс // --------------------------- pBulkRespond->dwDeviceOut = dwBytesRemaining; pBulkRespond->bIsLastTransfer = FALSE; pBulkRespond->bEndOfMessage = FALSE; if( (udi->GPIBFunctionContext.LastFunction != NULL && udi->GPIBFunctionContext.bEnable == TRUE) ) { TCmdParser_f LastFunction = (TCmdParser_f)udi->GPIBFunctionContext.LastFunction; // ------------------------------------------ udi->GPIBFunctionContext.bEnable = TRUE; udi->GPIBFunctionContext.LastFunction = NULL; // ------------------------------------------ udi->usbtmcGpib.pData = pInPipe->pDefaultBuffer + sizeof(BULKIN_HEADER) + dwBytesRemaining; udi->BulkRespondStatus.RespondBufferSize-= dwBytesRemaining; udi->BulkRespondStatus.dwDeviceOut += LastFunction( udi, NULL, FALSE ); } // не уходим на REQUEST_DEV_DEP_MSG_IN. так как сюда попадем раньше чем придет реальный REQ_MSG_IN => bTag еще старый // оставляем pInPipe.dwLength = 0 чтобы кормить NAKами до прихода следующего REQ_MSG_IN } else { // --------------------------------------------------------------------------------- // пока функция не установит флаг bIsLastTransfer, EndOfTransfer() будет вызывать ее if( pBulkRespond->bIsLastTransfer == FALSE ) { if( ! (udi->GPIBFunctionContext.LastFunction != NULL && udi->GPIBFunctionContext.bEnable == TRUE) ) { pInPipe->dwAllLength = 0; pBulkRespond->bIsLastTransfer = TRUE; pBulkRespond->bEndOfMessage = TRUE; } else { TCmdParser_f LastFunction = (TCmdParser_f)udi->GPIBFunctionContext.LastFunction; // --------------------------------------------------------------------------------- // ------------------------------------------ udi->GPIBFunctionContext.bEnable = TRUE; udi->GPIBFunctionContext.LastFunction = NULL; // ------------------------------------------ // --------------------------- usbtmc_init_intransfer( udi ); // --------------------------- udi->BulkRespondStatus.dwDeviceOut += LastFunction( udi, NULL, FALSE ); // usbtmc_REQUEST_DEV_DEP_MSG_IN( udi ); // там идет модификация заголовка данных } } } return pBulkRespond->bIsLastTransfer; } // ========================================================================================================================================= void usbtmc_create_function_context( USB_DEVICE_INFO * udi, void * pFunction ) { udi->GPIBFunctionContext.LastFunction = pFunction; udi->GPIBFunctionContext.bEnable =TRUE; queue_create( gGPIBFunctionContext, USB_MAX_FUNCCONTEXT, &udi->GPIBFunctionContext.Context); } // ========================================================================================================================================= void usbtmc_delete_function_context( USB_DEVICE_INFO * udi ) { udi->GPIBFunctionContext.bEnable = FALSE; udi->GPIBFunctionContext.LastFunction = NULL; queue_clear( &udi->GPIBFunctionContext.Context); } // ========================================================================================================================================= int USBTMC_StateMachine_Modified( USB_USBTMC_GPIBEMULATION_STATEMACHINE * pStateMachine) { // Check event status register and the mask if( (pStateMachine->ESE) & (pStateMachine->ESR) ) { // set Event Status Bit pStateMachine->STB |= (1<STB &= (~(1<STB & 0xBF) & pStateMachine->SRE ) { pStateMachine->STB |= (1<STB &= (~(1<ESR |= (1<ESR |= (1<usbtmcStatus; USB_BULKMESSAGE_STATUS * pBulkMessage = (USB_BULKMESSAGE_STATUS*) &udi->BulkMessageStatus; USB_SETUP_PACKET * usp = &udi->EP0SetupPacket; USB_EP_STATUS * eps = &udi->EP0Status; BYTE * pbRespondBytes = ( BYTE * ) eps->InPipe.pData; // проверяют, сбросили ли мы передачу по Bulk-OUT. if( pusbtmcStatus->USBTMC_InitiateRecieved == TRUE ) // если вообще был принят запрос на сброс передачи { pusbtmcStatus->USBTMC_InitiateRecieved = FALSE; pbRespondBytes[0] = pusbtmcStatus->USBTMC_status; pbRespondBytes[1] = 0; // reserved pbRespondBytes[2] = 0; // reserved pbRespondBytes[3] = 0; // reserved *((unsigned int *)(&pbRespondBytes[4])) = pBulkMessage->nBytesRecieved; pBulkMessage->nBytesRecieved = 0; } else { pbRespondBytes[0] = STATUS_SPLIT_NOT_IN_PROGRESS; pbRespondBytes[1] = 0; // reserved pbRespondBytes[2] = 0; // reserved pbRespondBytes[3] = 0; // reserved *((unsigned int *)(&pbRespondBytes[4])) = 0; } eps->InPipe.dwLength = ( 8 <= usp->wLength)? 8: usp->wLength; return 0x00; } //========================================================================================================================= //========================================================================================================================= // usbtmc_abort_bulkout_transfer - прерывает текущую передачу Bulk-OUT, // инициализируя все необходимые переменные и параметры так, чтобы принять // корректно новую Bulk-OUT передачу начиная с BULK-OUT Header от хоста // @@@@ @@@@@ @@@ @@@@@ @@@@@@@ @@@@@ @ @ @ @ @ @@@ @ @ @@@@@@@ // @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ // @ @ @@@@@ @ @ @ @ @ @@@@@ @ @ @ @@@@ @@@@@ @ @ @ @ @ // @@@@@@ @ @ @ @ @@@@@ @ @ @ @ @ @ @ @ @ @ @ @ @ // @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ // @ @ @@@@@ @@@ @ @ @ @@@@@ @@@@ @ @@@@@@ @ @ @@@ @@@@ @ @ int usbtmc_abort_bulkout_transfer ( USB_DEVICE_INFO * udi, int dwFlags ){ // abort HOST->DEVICE int ep_state; USB_USBTMC_CLASS_STATUS * pusbtmcStatus = (USB_USBTMC_CLASS_STATUS *) &udi->usbtmcStatus; USB_BULKMESSAGE_STATUS * pBulkMessage = (USB_BULKMESSAGE_STATUS*) &udi->BulkMessageStatus; USB_SETUP_PACKET * usp = &udi->EP0SetupPacket; USB_EP_STATUS * eps = &udi->EP0Status; BYTE * pbRespondBytes = ( BYTE * ) eps->InPipe.pData; // MACRO_PROGRAMSECURITY_BOACXT_TRYENTER(); // проверяем, вызвана ли usbtmc_abort_bulkin_transfer() рекурсивно, если да, выходим //----------------------------------------------------------------------------------------------------------------- if( dwFlags & FLAG_USB_SETUPPACKET_RECIEVED ) { /* если функция вызвана с этим флагом, это означает, что был принят запрос на уничтожение передачи Bulk-Out Передача не будет уничтожена сразу. В прерывании делать этого не стоит, так как вполне возможно, что функция будет вызвана в момент, когда ведется передача и выполняется функция usb_EP2_tx_func__(), которая работает с переменными и указателями, которые нужно обнулить здесь. Нужно дождаться, основной поток выдйдет из таких функций. Следует поставить заппрос в очередь, установив флажок. Рефакторинг. 30/08/18. Прочитал. Не понял. Ни одна из функций протокола USBTMC не вызывается из основгого потока. Возможно, коментарий уже устарел. Все передачи ведутся из прерывания, а значит "застать" никакую функцию в никаком месте нельзя, поскольку она вызывается из контекста того же вектора прерывания. */ // запрос поставлен в очередь на обработку из контекста прерывания // see "USBTMC spec., rev 1.0, 2003": "page 18-19..." if(pusbtmcStatus->USBTMC_InitiateRecieved == FALSE) { // -- запрос INITIATE_ABORT_BULK_OUT принят не был // -- хост не должен присылать два таких запроса подряд // -- если хост прислал class request не CHECK_STATUS, его обработает usbtmc_class_request_fault pbRespondBytes[1] = pBulkMessage->bTag; // -- заполняем оставшееся поле ответного сообщения eps->InPipe.dwLength = ( 2 <= usp->wLength)? 2: usp->wLength; // -- ответное сообщение содержит 2 байта if((pBulkMessage->OUTTransferInProgress == TRUE) && (pBulkMessage->bTag == (0xFF & usp->wValue))) { // - Передача в процессе ---> Передача будет завершена, // - bTag совпал ---> запрос поставлен в очередь, статус: STATUS_PENDING pusbtmcStatus->USBTMC_InitiateRecieved = TRUE; // -- принят запрос INITIATE pusbtmcStatus->USBTMC_status = STATUS_PENDING; // -- передача ожижает завершения pbRespondBytes[0] = STATUS_SUCCESS; // -- статус операции (текущего запроса) : STATUS_SUCCESS, операция выполнена, запрос поставлен в очередь /* 30/08/18, рефакторинг. поскольку в программе не найдены вызовы обработчиков usbtmc в основном потоке main(), мера предосторожности обработки вызова usbtmc_abort_bulkout_transfer ( udi, 0 ) через usbtmc_service излишняя. Заменил вызов -MACRO_FLAGSET_INITIATE_BULKOUT_ABORT()- на прямой вызов usbtmc_abort_bulkout_transfer ( udi, 0 ) -- MACRO_FLAGSET_INITIATE_BULKOUT_ABORT(); // -- сигнализируем */ usbtmc_abort_bulkout_transfer ( udi, 0 ); } else { // - или bTag не совпал ---> // - или bTag совпал, но передача уже завершена. ---> статус операции: не STATUS_SUCCESS pusbtmcStatus->USBTMC_InitiateRecieved = FALSE; // -- запрос INITIATE не принят // -- see USBTMC spec, rev 1.0, 2003, page 22, table 20 // -- получаем сосотояние EP. смотрим, заняты ли ее буферы ep_state = usb_lpc_cmd_read(CMD_EP_SELECT | USB_EP_PHY_ADDRESS_BULK_OUT); if( ep_state & ((1<<5)|(1<<6))) pusbtmcStatus->USBTMC_status = STATUS_TRANSFER_NOT_IN_PROGRESS; else pusbtmcStatus->USBTMC_status = STATUS_FAILED; pbRespondBytes[0] = pusbtmcStatus->USBTMC_status; } } } else { // вызов функции из main // -- сюда мы попадем, если вызовем ф-ю без флага FLAG_USB_SETUPPACKET_RECIEVED // -- это может означать, что был принят запрос на обрыв передачи и установлен флаг FLAG_USBTMC_INITIATE_BULKOUT_ABORT // -- так как вызов осуществлен из основного потока, а не из прерывания, обработаем запрос // =============================================================================================== // >>> Важно! О вызове usbtmc_abort_bulkin, usbtmc_abort_bulkout <<< // ----------------------------------------------------------------------------------------------- // / \ > Нельзя вызывать usbtmc_abort_bulkout и usbtmc_abort_bulkin из прерывания! < // // / | \ > Так как функции usb_EP2_t(/r)x вызываются из основного потока, то и < // // /__*__\ > прерывать передачи нужно УБЕДИВШИСЬ, что основной поток не находится в < // // > usb_EP2_t(/r)x и не изменит какую либо из переменных сразу после выхода < // // > из прерывания, где эта переменная была сброшена. Это чревато непрогнозируемым поведением < // // > устройства и, соответственно, неправильной работой протокола USBTMC < // // > < // // =============================================================================================== // Abort Bulk trnasfer (Host->Device) //usp->wValue; // -- D0...D7 - the bTag value associated with the transfer to abort //usp->wIndex; // -- must specify direction (D7) and endpoint number (D0...D3) //usp->wLength; // -- Number of bytes to transfer: length of response to this request // -- имеем только одну Bulk-Out поэтому игнорируем поле usp->wIndex // -- обрабатываем bTag // -- see "USBTMC spec., rev 1.0, 2003": "page 18-19..." if(pBulkMessage->OUTTransferInProgress == TRUE ) { // -- bTag совпал при проверке запроса из прерывания, передача в прогрессе и будет завершена usb_stall_ep( USB_EP_LOG_ADDRESS_BULK_OUT, 0x01 ); // -- see USBTMC spec, rev 1.0, 2003, page 22, table 20, desc. of "STATUS_SUCCESS" pusbtmcStatus->USBTMC_status = STATUS_SUCCESS; } else { // bTag хоть и совпал при проверке запроса из прерывания, но передача видимо уще закончена usb_stall_ep( USB_EP_LOG_ADDRESS_BULK_OUT, 0x01 ); // -- see USBTMC spec, rev 1.0, 2003, page 22, table 20, desc. of "STATUS_SUCCESS" pusbtmcStatus->USBTMC_status = STATUS_SUCCESS; // -- все равно STATUS_SUCCESS // -- see USBTMC spec, rev 1.0, 2003, page 19, table 16, page 23, table 23 // -- STATUS_TRANSFER_NOT_IN_PROGRESS можно установить ТОЛЬКО при приеме запроса, тоесть в прерваании // -- а здесь мы уже обрабатываем уже принятый запрос. Здесь мы согласны, что передача в прогрессе // -- так как она была в прогрессе на момент приема запроса. только STATUS_SUCCESS } pBulkMessage->MsgID = 0x00; pBulkMessage->bTag = 0x00; pBulkMessage->bTagLast = 0x00; pBulkMessage->bBulkHeaderRecieved = FALSE; //(void)pBulkMessage->nBytesRemaining; // = 0x00; pBulkMessage->nBytesRecieved = 0x00; (void)pBulkMessage->nBytesRecieved; // = 0x00; // требуется не обнулять чтобы вернуть в запросе CHECK_ABORT_BULK_OUT_STATUS pBulkMessage->OUTTransferInProgress = FALSE; } // MACRO_PROGRAMSECURITY_BOACXT_LEAVE(); // -- покидаем return 0x00; } //========================================================================================================================= void usbtmc_reset_bulkmessage_status ( USB_BULKMESSAGE_STATUS * pBulkMessage ) { s_memset( (BYTE*)pBulkMessage, 0x00, sizeof(USB_BULKMESSAGE_STATUS) ); } void usbtmc_reset_bulkrespond_status ( USB_BULKRESPOND_STATUS * pBulkRespond ) { s_memset( (BYTE*)pBulkRespond, 0x00, sizeof(USB_BULKRESPOND_STATUS) ); } //========================================================================================================================= // usbtmc_abort_bulkin_transfer - прерывает текущую передачу Bulk-IN, // инициализируя все необходимые переменные и параметры так, чтобы начать // корректно новую Bulk-IN передачу начиная с BULK-IN Header к хосту // @@@@ @@@@@ @@@ @@@@@ @@@@@@@ @@@@@ @ @ @ @ @ @@@ @ @ // @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @@ @ // @ @ @@@@@ @ @ @ @ @ @@@@@ @ @ @ @@@@ @@@@@ @ @ @ @ // @@@@@@ @ @ @ @ @@@@@ @ @ @ @ @ @ @ @ @ @ @ @ // @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ // @ @ @@@@@ @@@ @ @ @ @@@@@ @@@@ @ @@@@@@ @ @ @@@ @ @@ int usbtmc_abort_bulkin_transfer ( USB_DEVICE_INFO * udi, int dwFlags ) { // abort DEVICE->HOST USB_USBTMC_CLASS_STATUS * pusbtmcStatus = (USB_USBTMC_CLASS_STATUS *) &udi->usbtmcStatus; USB_BULKRESPOND_STATUS * pBulkRespond = (USB_BULKRESPOND_STATUS*) &udi->BulkRespondStatus; USB_SETUP_PACKET * usp = &udi->EP0SetupPacket; USB_EP_STATUS * eps = &udi->EP0Status; BYTE * pbRespondBytes = ( BYTE * ) eps->InPipe.pData; int ep_state; // MACRO_PROGRAMSECURITY_BIACXT_TRYENTER(); // проверяем, вызвана ли usbtmc_abort_bulkin_transfer() рекурсивно, если да, выходим //------------------------------------------------------------------------------------------- // ======================================================= // // >>> О статусах STATUS_SUCCESS и STATUS_PENDING <<< // // ------------------------------------------------------- // // Операция INITIATE_ABORT_BULK_IN происходит в // // / \ два этапа: принимается запрос от хоста, что // // / | \ требуется завершить передачу, затем хост по- // // /__*__\ сылает запрос, справляясь о результатах. // // // // INITIATE_ABORT_BULK_IN - иницирует завершение передачи. // // Если в ответ на этот запрос устройство отправит STATUS_ // // SUCCESS, для хоста это значит, что устройство приняло // // запрос в обработку, но не означает, что передача УЖЕ // // прервана! Об результате он узнает позже, поэтому, сразу // // после отправки ответа STATUS_SUCCESS, устройство меняет // // статус на STATUS_PENDING и старается завершить передачу // // Теперь, если хост пришлет запрос CHECK_ABORT_BULK_OUT_ // // STATUS ДО того, как пришлет очередной Bulk-IN запрос, // // лишая устройство возможности отослав SHORT пакет корре- // // ктно завершить передачу, устройство ответит ему ста - // // тусом STATUS_PENDING! А если хост сначала запросит // // Short пакет, и определив, что он Short, запросит // // CHECK_ABORT_BULK_OUT_STATUS, устройство вернет хосту // // STATUS_SUCCESS, так как оно корректно завершило передачу// // ======================================================= if( dwFlags & FLAG_USB_SETUPPACKET_RECIEVED ) { /* если функция вызвана с этим флагом, это означает, что был принят запрос на уничтожение передачи Bulk-In Передача не будет уничтожена сразу. В прерывании делать этого не стоит, так как вполне возможно, что функция будет вызвана в момент, когда ведется передача и выполняется функция usb_EP2_rx_func(), которая работает с переменными и указателями, которые нужно обнулить здесь. Нужно дождаться, основной поток выдйдет из таких функций. Следует поставить заппрос в очередь, установив флажок. */ // запрос поставлен в очередь на обработку из контекста прерывания // see "USBTMC spec., rev 1.0, 2003": "page 18-19..." if(pusbtmcStatus->USBTMC_InitiateRecieved == FALSE) { // -- запрос INITIATE_ABORT_BULK_OUT принят не был // -- хост не должен присылать два таких запроса подряд // -- если хост прислал class request не CHECK_STATUS, его обработает usbtmc_class_request_fault pbRespondBytes[1] = udi->BulkRespondStatus.bTag; eps->InPipe.dwLength = (2 <= usp->wLength)? 2: usp->wLength; if((pBulkRespond->INTransferInProgress == TRUE) && (udi->BulkRespondStatus.bTag == (0xFF & usp->wValue))) { // - Передача в процессе ---> Передача будет завершена, // - bTag совпал ---> запрос поставлен в очередь, статус: STATUS_PENDING pusbtmcStatus->USBTMC_InitiateRecieved = TRUE; // -- принят запрос INITIATE pusbtmcStatus->USBTMC_status = STATUS_PENDING; // -- передача ожижает завершения pbRespondBytes[0] = STATUS_SUCCESS; // -- статус операции (текущего запроса) : STATUS_SUCCESS, операция выполнена, запрос поставлен в очередь /* 30/08/18, рефакторинг. поскольку в программе не найдены вызовы обработчиков usbtmc в основном потоке main(), мера предосторожности обработки вызова usbtmc_abort_bulkin_transfer ( udi, 0 ) через usbtmc_service излишняя. Заменил вызов -MACRO_FLAGSET_INITIATE_BULKIN_ABORT()- на прямой вызов usbtmc_abort_bulkout_transfer ( udi, 0 ) -- MACRO_FLAGSET_INITIATE_BULKIN_ABORT(); */ usbtmc_abort_bulkin_transfer ( udi, 0 ); } else { // - или bTag не совпал ---> // - или bTag совпал, но передача уже завершена. ---> статус операции: не STATUS_SUCCESS pusbtmcStatus->USBTMC_InitiateRecieved = FALSE; // -- запрос INITIATE не принят // -- see USBTMC spec, rev 1.0, 2003, page 22, table 24 // -- получаем сосотояние EP. смотрим, заняты ли ее буферы ep_state = usb_lpc_cmd_read(CMD_EP_SELECT | USB_EP_PHY_ADDRESS_BULK_IN); if( ep_state & ((1<<5)|(1<<6))) pusbtmcStatus->USBTMC_status = STATUS_TRANSFER_NOT_IN_PROGRESS; else pusbtmcStatus->USBTMC_status = STATUS_FAILED; pbRespondBytes[0] = pusbtmcStatus->USBTMC_status; } } } else { // ================================================================================== // // >>> О INTransferInProgress = TRUE, что написано чуть ниже <<< // // -------------------------------------------------------------------------------- // // / \ Внимание! Возможен такой вариант. Хост запросил данные. Количество // // / | \ данных кратно размеру буфера точки, и, поидее, следует отправить Short // // /__*__\ пакет. Когда Short пакет ставится в очередь на отправку по прерыванию, // // переменная INTransferInProgress устанавливается в FALSE. Если хост не // // станет запрашивать этот Short пакет, то передача поидее еще не завершена. Но: // // INTransferInProgress уже равна FALSE. Если прислать запрос INITIATE_ABORT_BULK_IN // // то вернется статус STATUS_TRANSFER_NOT_IN_PROGRESS, хотя это не так, т.к. не // // запрошен Short пакет! Поэтому, INTransferInProgress равна TRUE вопреки логике // /*--------------------------------------------------------*/ // /* see USBTMC spec, page 12, point 10, and also */ // /* see USBTMC spec, page 26, example in 4.2.1.5 */ // /* "Device MUST always terminate bulk-in trnasfer by sen- */ // /* ding short packet!!!" поэтому нельзя сразу взять и */ // /* pBulkMessage->INTransferInProgress = false, так как */ // /* тогда short пакет не отправится. */ // /* Однако если флаг FLAG_USBTMC_FORCE_BULKIN_ABORT уста - */ // /* новлен, значит EP остановлена (HALT (STALLED)), и нет */ // /* необходимости и смысла ждать отсылки Short пакета, */ // /* который, собственно, никода не будет отправлен по одной*/ // /* простой причине: точка остановлена (HALT) и нащ short */ // /* пакет хосту не нужен, он пришлет CLEAR_FEATURE, поэтому*/ // /* нужно INTransferInProgress установить в FALSE */ // /*--- ---*/ // /* Если вызвать функцию из Initiate_clear с параметром */ /* FLAG_USBTMC_FORCE_BULKIN_ABORT то все ограничения от- */ /* меняются и INTransferInProgress и INTransferTerminating*/ /* приравниваются в FALSE */ /* !!! вообще, в спецификации сказано, что можно вернуть */ /* статус PENDING в ответ на запрос CHECK_CLEAR_STATUS */ /* если хост не прочел short пакет. (USBTMC, rev 1.0,2003 */ /* page 28, table 34 Однако это справедливо*/ /* если устройство НЕ может удалить пакет из FIFO и тре- */ /* буется, чтобы хост его прочел. Можно просто удалить все*/ /* данные из буферов и всегда возвращать STATUS_SUCCESS */ /*--- ---*/ /* BOOLVAR равняется TRUE если не передан нулевой пакет */ // /* или еще остались данные на отправку */ // /**/BOOL BOOLVAR = (udi->EPBulkStatus.InPipe.dwLength>0 || eps->shortpacketsending || eps->shortpacket) ? TRUE:FALSE; // /**/ BOOLVAR&= pBulkRespond->INTransferInProgress; // -- если мы попали сюда после последнего пакета передачи и передача уже завершена! /**/pBulkRespond->INTransferInProgress = ( dwFlags & // /**/ FLAG_USBTMC_FORCE_BULKIN_ABORT )?FALSE:(BOOLVAR); // /**/pBulkRespond->INTransferTerminating =( dwFlags & // /**/ FLAG_USBTMC_FORCE_BULKIN_ABORT )?FALSE:(BOOLVAR); // /**/ /* pBulkMessage->nBytesSent = 0; */ /* Обнулится по началу но- */// /**/ /* вой передачи. Нужна для запроса статуса */// /**/ /* завершения передачи (CHECK_ABORT_BULK_IN_STATUS) */// /*--------------------------------------------------------*/ // // ---------------------------------------------------------------------------------- // //============================================================================================================ // передача должна завершиться Short пакетом. для этого его надо положить в очередь на отправку в буфер EP // если буферы в данный момент заняты, то по завершению передачи в функции по прерыванию от завершения передачи // (usb_EP2_tx_func__) short пакет сам встанет в очередь, если нужно (если количество данных кратно размеру // буфера. Если хотя бы один буфер пуст, можно положить Short пакет прямо здесь. Однако он не нужен, если остав- // шееся количество данных меньше размера буфера EP. //----------------------------------------------------------------------------------------------------------- ep_state = usb_lpc_cmd_read(CMD_EP_SELECT | USB_EP_PHY_ADDRESS_BULK_IN); if( udi->EPBulkStatus.InPipe.dwLength!=0 && (!(dwFlags & FLAG_USBTMC_FORCE_BULKIN_ABORT))) // если остались не переданные данные { if(udi->EPBulkStatus.InPipe.dwLength >= USB_MAX_PACKET2) // необходим short пакет, так как последняя порция данных равна или больше размер буфера { if( !((ep_state&(1<<5))) && ((ep_state&(1<<6))) ) // если буферы точки свободны (хотябы один) { udi->EPBulkStatus.shortpacket = FALSE; // будем использовать двойную буфферизацию usb_ep_write(USB_EP_LOG_ADDRESS_BULK_IN,(BYTE*)0, 0); // ставим в очередь short пакет } else { udi->EPBulkStatus.shortpacket = FALSE; // пакет передатся в следущей транзакции } } } else { // что мы имеем: udi->EPBulkStatus.InPipe.dwLength == 0 // мы в функции, значит INTransferInProgress была равна true, когда был принят запрос // значит получилось так, что запрос обработан с опозданием и последняя порция данных уже отправлена // возможно еще не передан short пакет нулевой длинны // также сюда попадем если вызвали функцию из initiate_clear с флагом FLAG_USBTMC_FORCE_BULKIN_ABORT if(dwFlags & FLAG_USBTMC_FORCE_BULKIN_ABORT) { udi->EPBulkStatus.shortpacket=FALSE; udi->EPBulkStatus.shortpacketsending=FALSE; } } //=========================================================================================================== usb_reset_pipe_status( &udi->EPBulkStatus.InPipe ); pBulkRespond->MsgID = 0x00; // для честности pBulkRespond->bTag = 0x00; // для честности udi->GPIBFunctionContext.bEnable = FALSE; udi->GPIBFunctionContext.LastFunction = NULL; } // MACRO_PROGRAMSECURITY_BIACXT_LEAVE(); return 0x00; } //========================================================================================================================= //========================================================================================================================= // @@@@ @ @ @@@@ @@@@ @ @ @@@@ @@@@@ @@@ @@@@@ @@@@@@@ @@@@@ @ @ @ @ @ @@@ @@ @ // @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @@ @ // @ @ @ @@@@@@ @ @@@@ @ @ @@@@@ @ @ @ @ @ @@@@@ @ @ @ @@@@ @@@@@ @ @ @ @ // @ @@@@@ @ @ @ @ @@@@@@ @ @ @ @ @@@@@ @ @ @ @ @ @ @ @ @ @ @ @ // @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @@ // @@@@ @ @ @@@@ @@@@ @ @ @ @ @@@@@ @@@ @ @ @ @@@@@ @@@@ @ @@@@@@ @ @ @@@ @ @@ int usbtmc_checkstatus_abort_bulkin (USB_DEVICE_INFO * udi ) { USB_USBTMC_CLASS_STATUS * pusbtmcStatus = (USB_USBTMC_CLASS_STATUS *) &udi->usbtmcStatus; USB_BULKRESPOND_STATUS * pBulkRespond = (USB_BULKRESPOND_STATUS*) &udi->BulkRespondStatus; USB_SETUP_PACKET * usp = &udi->EP0SetupPacket; USB_EP_STATUS * eps = &udi->EP0Status; BYTE * pbRespondBytes = ( BYTE * ) eps->InPipe.pData; // проверяют, сбросили ли мы передачу по Bulk-IN. if( pusbtmcStatus->USBTMC_InitiateRecieved == TRUE ) // если вообще был принят запрос на сброс передачи { pusbtmcStatus->USBTMC_InitiateRecieved = FALSE; pbRespondBytes[0] = pusbtmcStatus->USBTMC_status; //---------------------------------------------------------------------- // see USBTMC spec, rev 1.0, 2003, page 25, table 29, description of "STATUS_PENDING" // and also see USBTMC spec, rev 1.0, 2003, page 25, table 28, desc. of byte with offset 1 pbRespondBytes[1] = ( /* bmAbortBulkIn */ (pusbtmcStatus->USBTMC_status==STATUS_PENDING) && (pBulkRespond->INTransferInProgress == TRUE) && (pBulkRespond->INTransferTerminating == TRUE) )?(1<<0):0x00; // Если статус STATUS_PENDING (ожидается), INTransferInProgress=TRUE, и INTransferTerminating = TRUE // значит передача в процессе завершения, и судя по документации для счастья только и нехватает отправить // Short пакет хосту. ( см ссылки на док-ю выше ) pbRespondBytes[1] &= 0x01; // bytes Reserved //----------------------------------------------------------------------- pbRespondBytes[2] = 0; // reserved pbRespondBytes[3] = 0; // reserved *((unsigned int *)(&pbRespondBytes[4])) = pBulkRespond->nBytesSent; pBulkRespond->nBytesSent = 0; } else { pbRespondBytes[0] = STATUS_SPLIT_NOT_IN_PROGRESS; pbRespondBytes[1] = 0; // reserved pbRespondBytes[2] = 0; // reserved pbRespondBytes[3] = 0; // reserved *((unsigned int *)(&pbRespondBytes[4])) = 0; } eps->InPipe.dwLength = ( 8 <= usp->wLength)? 8: usp->wLength; return 0x00; } //========================================================================================================================= //========================================================================================================================= // @@@ @ @ @@@ @@@@@ @@@ @@@@ @@@@@ @@@@ @@@@ @ @@@@ @@@@ @@@@@ // @ @@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ // @ @ @ @ @ @ @ @@@@@@ @ @@@@@ @ @ @@@@@@ @@@@@@ @@@@@ // @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ // @ @ @@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ // @@@ @ @ @@@ @ @@@ @ @ @ @@@@ @@@@ @@@@@@ @@@@ @ @ @ @@ int usbtmc_initiate_clear( USB_DEVICE_INFO * udi, int dwFlags) { USB_USBTMC_CLASS_STATUS * pusbtmcStatus = (USB_USBTMC_CLASS_STATUS *) &udi->usbtmcStatus; USB_BULKRESPOND_STATUS * pBulkRespond = (USB_BULKRESPOND_STATUS*) &udi->BulkRespondStatus; USB_EP_STATUS * eps = &udi->EP0Status; BYTE * pbRespondBytes = ( BYTE * ) eps->InPipe.pData; if( FLAG_USB_SETUPPACKET_RECIEVED & dwFlags ) { if(pusbtmcStatus->USBTMC_InitiateRecieved == FALSE) { //pbRespondBytes[0] = STATUS_SPLIT_NOT_IN_PROGRESS; //pusbtmcStatus->USBTMC_status = STATUS_SPLIT_NOT_IN_PROGRESS; // -- запрос только пришел, ставим его в ожидание pusbtmcStatus->USBTMC_InitiateRecieved = TRUE; pusbtmcStatus->USBTMC_status = STATUS_PENDING; pbRespondBytes[0] = STATUS_SUCCESS; /* 30/08/18, рефакторинг. поскольку в программе не найдены вызовы обработчиков usbtmc в основном потоке main(), мера предосторожности обработки вызова usbtmc_initiate_clear() через usbtmc_service излишняя. Заменил вызов -MACRO_FLAGSET_INITIATE_CLEAR()- на прямой вызов usbtmc_initiate_clear(udi, 0) -- MACRO_FLAGSET_INITIATE_CLEAR(); -- */ eps->InPipe.dwLength = 1; usbtmc_initiate_clear(udi, 0); } else ; // -- отработает usbtmc_class_request_fault() } else { usbtmc_bulkout_stall_and_abort ( udi ); // see USBTMC spec, rev 1.0, 2003, page 26, "4.2.1.6 INITIATE_CLEAR", last paragraph // ===================================================================== // >>> Почему буферы чистятся до вызова usbtmc_abort_bulkin_transfer <<< // --------------------------------------------------------------------- // / \ > По спецификации каждая передача Bulk-In Должна заканчи- // / | \ > ваться short пакетом. В соотвествии с ней, стр 28, табл. 34 // /__*__\ > описание байта bmClear, в очереди должен находиться short // > пакет, пока хост его не прочтет. Вызов фунции // > usbtmc_abort_bulkin_transfer может поставить в очередь нулевой пакет, // > который необходим для завершения передачи, а чистка буферов его // > его уничтожит. Поэтому, нужно сначала почисить, а потом вызвать // > функцию, которая !может быть! поставит в очередь short пакет. // --------------------------------------------------------------------- // необходимо ОЧИСТИТЬ ВСЕ буферы // ---------- Discard Last queued respond -----------------------------/ /*----- Clear Buffer 1*/ usb_lpc_cmd(CMD_EP_SELECT | USB_EP_PHY_ADDRESS_BULK_IN); usb_lpc_cmd(CMD_EP_CLEAR_BUFFER ); /*----- Clear Buffer 2*/ usb_lpc_cmd(CMD_EP_SELECT | USB_EP_PHY_ADDRESS_BULK_IN); usb_lpc_cmd(CMD_EP_CLEAR_BUFFER ); /*----- Clear Buffer 1*/ usb_lpc_cmd(CMD_EP_SELECT | USB_EP_PHY_ADDRESS_BULK_OUT); usb_lpc_cmd(CMD_EP_CLEAR_BUFFER ); /*----- Clear Buffer 2*/ usb_lpc_cmd(CMD_EP_SELECT | USB_EP_PHY_ADDRESS_BULK_OUT); usb_lpc_cmd(CMD_EP_CLEAR_BUFFER ); // clear Bulk-IN transfer usbtmc_abort_bulkin_transfer( udi, FLAG_USBTMC_FORCE_BULKIN_ABORT ); // clear Bulk-Out transfer for restore Bulk-Out syncronization // it is invoked in usbtmc_bulkout_stall() above // -- usbtmc_abort_bulkout_transfer ( udi, 0); eps->InPipe.dwLength = 1; if( pBulkRespond->INTransferInProgress == TRUE) { // see USBTMC spec, rev 1.0, 2003, page 27, table 32 // нужно уничтожить все передачи, вне зависимости от того, передан ли short пакет или нет pusbtmcStatus->USBTMC_status = STATUS_SUCCESS; pbRespondBytes[0] = STATUS_SUCCESS; // -- запрос обработан } else { pusbtmcStatus->USBTMC_status = STATUS_SUCCESS; pbRespondBytes[0] = STATUS_SUCCESS; // -- запрос обработан } } return 0x00; } //========================================================================================================================= //========================================================================================================================= // @@@@ @ @ @@@@ @@@@ @ @ @@@@ @ @@@@ @@@@ @@@@@ // @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ // @ @@@@@@ @@@@@@ @ @@@@ @ @ @@@@@@ @@@@@@ @@@@@ // @ @ @ @ @ @ @ @ @ @ @ @ @ @ // @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ // @@@@ @ @ @@@@ @@@@ @ @ @@@@ @@@@@@ @@@@ @ @ @ @@ int usbtmc_checkstatus_clear( USB_DEVICE_INFO * udi ) { USB_USBTMC_CLASS_STATUS * pusbtmcStatus = (USB_USBTMC_CLASS_STATUS *) &udi->usbtmcStatus; USB_BULKRESPOND_STATUS * pBulkRespond = (USB_BULKRESPOND_STATUS*) &udi->BulkRespondStatus; USB_EP_STATUS * eps = &udi->EP0Status; BYTE * pbRespondBytes = ( BYTE * ) eps->InPipe.pData; if( pusbtmcStatus->USBTMC_InitiateRecieved == TRUE ) { eps->InPipe.dwLength = 2; if( pBulkRespond->INTransferInProgress == TRUE ) { pbRespondBytes[0] = pusbtmcStatus->USBTMC_status; pbRespondBytes[1] = (0x01 & (1<<0)); // -- bmClear.D0 = 0, see USBTMC spec, rev 1.0, 2003, page 29, table 34. } else { pbRespondBytes[0] = pusbtmcStatus->USBTMC_status; pbRespondBytes[1] = (0x01 & (0<<0)); // -- bmClear.D0 = 0, see USBTMC spec, rev 1.0, 2003, page 29, table 34. } pusbtmcStatus->USBTMC_InitiateRecieved = FALSE; } else { pbRespondBytes[0] = STATUS_SPLIT_NOT_IN_PROGRESS; pbRespondBytes[1] = 0x00; } eps->InPipe.dwLength = 2; return 0x00; } //========================================================================================================================= //========================================================================================================================= // ##### #### #### #### ##### ###### #### ###### # # ##### ##### # # ###### #### // # # # # # # # # # # # # # # # # # # # # # # # // ##### ###### ###### # # ###### # ###### # # # ###### ##### # # # ###### // # # # # # # # # # # # # # # # # # ## # # // # # # # # # # # # # # # # # # # # # # # ## # # # // # ## #### # # #### #### ### # # ### #### #### ##### ## ### #### #ifdef __SERIAL_POLL_SUPPORT__ int usbtmc_READ_STATUS_BYTE( USB_DEVICE_INFO * udi ){ int rc = FALSE; struct _INTERRUPT_IN { BYTE bNotify1; BYTE bNotify2; } InterruptIn; USB_INTERRUPT_DISABLE_INTIN_NAK(); InterruptIn.bNotify1 = 0x80; //D7 must be 1, see USB488 spec, rev 1.0, 2003, page 9, 3.4 Interrupt_IN, Table 6,7 InterruptIn.bNotify2 = _STB; #ifdef __USBTMC_SERVICE_REQUEST_SUPPORT__ // see USB488 spec, rev 1.0, 2003, page 9, 3.4 Interrupt_IN, Table if( GPIB_GET_RQS() ) // GPIB_GET_RQS() gets RQS Bit from STB registers and DO not clears it! { InterruptIn.bNotify1 = 0x81; // see USB488 spec, rev 1.0, 2003, page 9, 3.4 Interrupt_IN, Table 6 // see USB488 spec, rev 1.0, 2003, page 9, 3.4 Interrupt_IN, text below Table 6 GPIB_CLR_RQS__(); // no state-machine state modify, clear ServiceRequestBit } else #endif InterruptIn.bNotify1 |=udi->usbtmcGpib.StateMachine.bTag_Interrupt; // --------------- проверяем можем ли положить в Interrupt Точку данные -------------- rc = usb_lpc_cmd_read( CMD_EP_SELECT | USB_EP_PHY_ADDRESS_INT_IN ); rc = ( (rc & (1<<5))?FALSE:TRUE ); // return whether it is possible to queue bytes if( rc ) { usb_ep_write( USB_EP_LOG_ADDRESS_INT_IN, (BYTE*)&InterruptIn, 2); rc = usb_lpc_cmd_read( CMD_EP_SELECT | USB_EP_PHY_ADDRESS_INT_IN ); rc = ( (rc & (1<<5))?TRUE:FALSE ); // return result of queuing bytes } return rc; } #endif int usbtmc_read_status_byte( USB_DEVICE_INFO * udi ) { USB_USBTMC_CLASS_STATUS * pusbtmcStatus = (USB_USBTMC_CLASS_STATUS *) &udi->usbtmcStatus; USB_EP_STATUS * eps = &udi->EP0Status; BYTE * pbRespondBytes = ( BYTE * ) eps->InPipe.pData; USB_SETUP_PACKET * usp = &udi->EP0SetupPacket; USB_USBTMC_GPIBEMULATION_STATEMACHINE * pStateMachine = (USB_USBTMC_GPIBEMULATION_STATEMACHINE*) &(udi->usbtmcGpib.StateMachine); // ------------------------------------------------------------------------------------------- pStateMachine->bTag_Interrupt = (0x7F & (usp->wValue)); // Prior to call usbtmc_READ_STATUS_BYTE_Interrupt() // ----------------------------------------------------------- #ifdef __SERIAL_POLL_SUPPORT__ // see USB488 spec, rev 1.0, 2003, page 13, 4.3.1.2 if( usbtmc_READ_STATUS_BYTE(udi) == FALSE ) pusbtmcStatus->USBTMC_status = STATUS_INTERRUPT_IN_BUSY; else pusbtmcStatus->USBTMC_status = STATUS_SUCCESS; pbRespondBytes[2] = 0x00; // STB will be returned in INTERRUPT IN #else pbRespondBytes[2] = _STB; // STB will be returned here, in Control Response Packet pusbtmcStatus->USBTMC_status = STATUS_SUCCESS; #endif // see USB488 spec, rev 1.0, 2003, page 13, table 12 pbRespondBytes[0] = pusbtmcStatus->USBTMC_status; pbRespondBytes[1] = pStateMachine->bTag_Interrupt; // ----------------------------------------------------------- eps->InPipe.dwLength = 3; return 0x00; } //========================================================================================================================= //========================================================================================================================= // @@@@ @@@@ @@@@@@ @@@@ @@@@ @@@@@ @@@@ @@@@@ @@@ @ @@@ @@@@@@ @@@@ @@@@@ // @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ // @ @@@@@@ @ @ @@@@@@ @ @ @@@@@@ @@@@@ @ @ @ @ @@@@@@ @@@@@@ // @ @@@ @ @ @ @ @ @@@@@ @ @ @ @ @ @ @ @ @ @ // @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ // @@@@ @@@@ @@@ @@@@ @ @ @ @ @ @@@@@ @@@ @@@@@@ @@@ @@@ @@@@ @@@@ // see USBTMC spec, rev 1,0, 2003, page 28-29 int usbtmc_getcapabilites ( USB_DEVICE_INFO * udi ) { USB_USBTMC_CLASS_STATUS * pusbtmcStatus = (USB_USBTMC_CLASS_STATUS *) &udi->usbtmcStatus; USB_EP_STATUS * eps = &udi->EP0Status; BYTE * pbRespondBytes = ( BYTE * ) eps->InPipe.pData; /*pbRespondBytes[0] = pusbtmcStatus->USBTMC_status;*/ pbRespondBytes[0] = STATUS_SUCCESS; // fixed: 05/09/18, NiVisa warned VI_WARN_UNKNOWN_STATUS if this value is 0x00 pbRespondBytes[1] = 0; // reserved pbRespondBytes[2] = 0x01; // BCD version number of the relevant USBTMC spec pbRespondBytes[3] = 0x00; // ver 1.0 // interface in not Listen-only and is not Talk-Only // interface not support INDICATOR_PULSE pbRespondBytes[4] = (0x07 & ((0<>> USB488 compatibilites: IEEE 488.2 Interface <<<") #pragma message(">>> USB488 compatibilites: SCPI Commands are supported <<<") #pragma message(">>> USB488 compatibilites: Service Request are supported <<<") #endif #else // byte with offset 15: // D0 - DT0 Device Trigger // D1 - RL0 Remote Local // D2 - SR0 Service Request // D3 - Not Support SCPI pbRespondBytes[15] = 0x0F & ((0<<3) | (0<<2) | (0<<1) | (0<<0)); // byte with offset 14: // D0 - Not support TRIGGER message // D1 - Not support GO_TO_LOCAL, REN_CONTROL, LOCAL_LOCKOUT // D2 - It is USB488 interface, but SR1 not supported, SCPI not supported pbRespondBytes[14] = 0x07 & ((1<<2) | (0<<1) | (0<<0)); #pragma message(">>> USB488 compatibilites: IEEE 488.2 Interface <<<") #pragma message(">>> USB488 compatibilites: NOT Support SCPI <<<") #pragma message(">>> USB488 compatibilites: NOT Support Service Request <<<") #endif pbRespondBytes[16] = 0x00; // USB488 Reserved pbRespondBytes[17] = 0x00; // USB488 Reserved pbRespondBytes[18] = 0x00; // USB488 Reserved pbRespondBytes[19] = 0x00; // USB488 Reserved pbRespondBytes[20] = 0x00; // USB488 Reserved pbRespondBytes[21] = 0x00; // USB488 Reserved pbRespondBytes[22] = 0x00; // USB488 Reserved pbRespondBytes[23] = 0x00; // USB488 Reserved eps->InPipe.dwLength = 24; return 0x00; } //========================================================================================================================= // @@@ @ @ @@@@ @@@ @@@@ @@@@ @@@@@@ @@@@ @@@@@ @@@@@ @ @ @ @@@@@ @@@@ // @ @@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ // @ @ @ @ @ @ @ @ @@@@@@ @ @ @ @@@@@ @ @ @ @ @ @@@@@@ @@@@@@ // @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @@@@@ @ @ @ @ @ // @ @ @@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ // @@@ @ @ @@@@ @@@ @@@@ @ @ @@@ @@@@ @ @@ @ @@@@ @@@@@@ @@@@ @@@@ //========================================================================================================================= int usbtmc_indicator_pulse ( USB_DEVICE_INFO * udi ) { // -- Request "INDICATOR_PULSE" is not supported. // see USBTMC spec, rev 1.0, 2003, page 29, table 37, decription of byte with offset 4, bit D2. // Control-IN -> STALL usb_stall_ep( 0x01, TRUE ); return 0x00; } //========================================================================================================================= //========================================================================================================================= // @@@@@ @@@@ @@@@@ @@@@@ @@@@ @@@@@ @ @ @ @ @ @ @ @@@@ @@@@@ @@@@@ @@@@ @@@@ @@@@ // @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @@ @@ @ @ @ @ @ @ @ @ @ @ // @ @ @@@@@@ @@@@@ @@@@@@ @@@@@@ @@@@@ @ @ @ @@@@ @ @@ @ @@@@@@ @@@@@@ @@@@@@ @@@@@@ @ @@@@@@ // @@@@@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @@@ @ // @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ // @ @ @ @ @@ @@@@ @@@@ @@@@@ @@@@ @@@@@@ @ @ @ @ @@@@ @@@@ @@@@ @ @ @@@@ @@@@ // // Параметр bCheckDataStage: // Протокол USBTMC содержит 4 основных комманды: DEV_DEP_MSG_OUT, VENDOR_SPECIFIC_OUT, REQUEST_DEV_DEP_MSG_IN, REQUEST_VENDOR_SPECIFIC_IN // DEV_DEP_MSG_OUT, VENDOR_SPECIFIC_OUT предполагают стадию данных в Bulk-Out, тоесть устройство принимает TransferSize байт // REQUEST_DEV_DEP_MSG_IN, REQUEST_VENDOR_SPECIFIC_IN же предполагают стадию данных в Bulk-IN, тоесть устройство не принимает, а передает TransferSize байт // При приеме комманды Bulk-Out требуется точно определить, сколько данных будет передано, тоесть когда завершать передачу и рассматривать следующую порцию // данных как новую передачу. Для этого и служит поле TransferSize. Однако в двух запросах REQUEST_DEV_DEP_MSG_IN, REQUEST_VENDOR_SPECIFIC_IN это // поле учитывать не нужно, так как оно имеет противоположный смысл. Для определения, учитывать или не учитывать TransferSize введен параметр bCheckDataStage // Принимая запрос и определив, что это заголовок, нужно вызвать функцию usbtmc_parse( ..., TRUE ), функция вернет TRUE, если это поток // Host->Device ( DEV_DEP_MSG_OUT, VENDOR_SPECIFIC_OUT ) или FALSE, если TransferSize определяет, сколько данных требуется прочитать из устройства Device->Host (REQUEST_DEV_DEP_MSG_IN, REQUEST_VENDOR_SPECIFIC_IN) // Функция, приняв TRUE в bCheckDataStage завершится немедленно, не обрабатывая запрос int usbtmc_parse( USB_DEVICE_INFO * udi, BOOL bCheckDataStage ) { USB_BULKMESSAGE_STATUS * pBulkMessage = (USB_BULKMESSAGE_STATUS*) &udi->BulkMessageStatus; int rc = FALSE; //---------------------------------------------------------------------------------------- switch( pBulkMessage->MsgID ) { // DEV_DEP_MSG_OUT // Host sent a message to the device // The message length: TransferSize ( size of Bulk-Out transaction ) case DEV_DEP_MSG_OUT: { // Process the flag: @bCheckDataStage // It is dummy call to identify the direction if( bCheckDataStage ) return (TRUE); // identify a transfer that sends a USBTMC device dependent // command message from the Host to a device rc = usbtmc_DEV_DEP_MSG_OUT( udi ); } break; // REQUEST_DEV_DEP_MSG_IN // Device responds to the host // Maximum Bulk-IN transaction size is @TransferSize case REQUEST_DEV_DEP_MSG_IN: { // Process the flag: @bCheckDataStage // It is dummy call to identify the direction if( bCheckDataStage == TRUE ) return (FALSE); // identify the transfer as a USBTMC command message to the device, // allowing the device to send a USBTMC response message containing // device dependent message data bytes rc = usbtmc_REQUEST_DEV_DEP_MSG_IN( udi ); } break; // // VENDOR_SPECIFIC_OUT // Host sent a vendor specific message to the device case VENDOR_SPECIFIC_OUT: { // Process the flag: @bCheckDataStage // It is dummy call to identify the direction if( bCheckDataStage == TRUE ) return (TRUE); rc = FALSE; // not supported } break; // // VENDOR_SPECIFIC_OUT // Host requests answer on vendor specific message from device case REQUEST_VENDOR_SPECIFIC_IN: { // Process the flag: @bCheckDataStage // It is dummy call to identify the direction if( bCheckDataStage == TRUE ) return (FALSE); rc = FALSE; // not supported } break; case TRIGGER: rc = FALSE; // not supported break; default:; rc = FALSE; // not supported } return rc; } //========================================================================================================================= //========================================================================================================================= // @@@@@ @@@@ @@@@@ @@@@@ @@@@ @@@@ @@@@@@ @@@@@ @ @@@@@ @@@@ @@@@ // @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ // @ @ @@@@@@ @@@@@ @@@@@@ @@@@@@ @ @ @@@@@ @ @@@@@ @@@@@@ @ @ // @@@@@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ // @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ // @ @ @ @ @@ @@@@ @@@@ @@@@ @@@ @ @@ @@@@@@ @ @@ @@@@ @@@ @ BOOL usbtmc_class_request(USB_DEVICE_INFO * udi) { BOOL rc = FALSE; USB_USBTMC_CLASS_STATUS * pusbtmcStatus = (USB_USBTMC_CLASS_STATUS *) &udi->usbtmcStatus; USB_SETUP_PACKET * usp = &udi->EP0SetupPacket; // -- проверяем, нужно ли обрабатывать запрос. // -- если иницирован запрос INITIATE, запрос рассматривается как non operate, согласно документации // -- see USBTMC spec, rev 1.0, 2003, page 20-21 "a","b","c" // See USBTMC spec, rev 1.0, 2003, 4.2.1.1, "USBTMC split transactions" if( (TRUE == pusbtmcStatus->USBTMC_InitiateRecieved) && (CHECK_ABORT_BULK_IN_STATUS != usp->bRequest) && (CHECK_ABORT_BULK_OUT_STATUS != usp->bRequest) && (CHECK_CLEAR_STATUS != usp->bRequest) ) { rc = usbtmc_class_request_fault( udi ); } else { // Processing USBTMC Class requests, see "USBTMC spec., rev. 1.0, 2003", 4.2.1, "USBTMC requests" switch(usp->bRequest) { case INITIATE_ABORT_BULK_OUT: usbtmc_abort_bulkout_transfer ( udi, FLAG_USB_SETUPPACKET_RECIEVED ); rc = TRUE; break; case CHECK_ABORT_BULK_OUT_STATUS: usbtmc_checkstatus_abort_bulkout( udi ); rc = TRUE; break; case INITIATE_ABORT_BULK_IN: usbtmc_abort_bulkin_transfer ( udi, FLAG_USB_SETUPPACKET_RECIEVED ); rc = TRUE; break; case CHECK_ABORT_BULK_IN_STATUS: usbtmc_checkstatus_abort_bulkin( udi ); rc = TRUE; break; case INITIATE_CLEAR: usbtmc_initiate_clear( udi, FLAG_USB_SETUPPACKET_RECIEVED ); rc = TRUE; break; case CHECK_CLEAR_STATUS: usbtmc_checkstatus_clear( udi ); rc = TRUE; break; case GET_CAPABILITES: usbtmc_getcapabilites( udi ); rc = TRUE; break; case READ_STATUS_BYTE: usbtmc_read_status_byte( udi ); rc = TRUE; break; case INDICATOR_PULSE: rc = usbtmc_indicator_pulse( udi ); // see USBTMC spec, rev 1.0, 2003, page29, table 37, desc. of byte with offset 4, bit D2 break; case GO_TO_LOCAL: case LOCAL_LOCKOUT: case REN_CONTROL: usb_stall_ep( 0x01, TRUE ); // not support, -> stall rc = FALSE; break; default: // see USB 2.0 spec, page 256, 9.4.5, last paragaph, "device need not return STALL for class-specific and vendor-specific requests." // see USB 2.0 spec, 9.2.7: It is preferred that the STALL PID be returned at the next Data stage transaction, as this avoids unnecessary bus activity // "preferred" - предпочтительно, но не обязательно // // rc = FALSE; // STALL, see USB 2.0 spec, page 252, "9.4 Standard Device Requests" rc = TRUE; // No STALL } } return rc; } //- --------------------------------------------------------------------------------------------------------------------------------- int usbtmc_class_request_fault ( USB_DEVICE_INFO * udi ) { // -- usbtmc_request_fault - обработчик запроса как non-operation запроса. USB_USBTMC_CLASS_STATUS * pusbtmcStatus = (USB_USBTMC_CLASS_STATUS *) &udi->usbtmcStatus; USB_EP_STATUS * eps = &udi->EP0Status; BYTE * pbRespondBytes = ( BYTE * ) eps->InPipe.pData; if( pusbtmcStatus->USBTMC_InitiateRecieved==TRUE ) { pbRespondBytes[0] = STATUS_SPLIT_IN_PROGRESS; pbRespondBytes[1] = 0x00; // -- не важно, see USBTMC spec, rev 1.0, 2003, page 19, after table 16, "A response with ...", "Host MUST ignore " ... "all response bytes" eps->InPipe.dwLength = 2; } return TRUE; } //========================================================================================================================= int usbtmc_DEV_DEP_MSG_OUT ( USB_DEVICE_INFO * udi ) { int rc = FALSE; USB_BULKRESPOND_STATUS * pBulkRespond = (USB_BULKRESPOND_STATUS*) &udi->BulkRespondStatus; BYTE * pBulkData_DeviceIn = (BYTE*) udi->BulkMessageStatus.pData; // указатель на принятые данные. данные могут лежать как в буфере EP так и в общем буфере // ----------------------------------------------------------------------------------------------------------------------------------------------------------------- // Хост иницирует DEV_DEP_MSG_OUT. Размер передачи этого запроса может быть больше // буфера конечной точки, и тогда данные нужно кешировать. Кеширование происходит // в единственный буффер, в 1,5Кб, адрес get_addr_in_buffer(). Делается допущение, // что когда хост иницирует REQUEST_DEV_DEP_MSG_IN, этот буфер не понадобится, поскольку // в нем уже лежат подготовленные данные для ответа на запрос DEV_DEP_MSG_OUT. // Однако если придет несколько комманд в одной строке, вызовы назначенных на комманду // функций изменят содержимое буфера pBulkData_DeviceIn обращаясь к нему как // к указателю на заголовок BULKIN, будут кешировать отправляемые данные в него // Вобщем, строка комманд будет повреждена. Чтобы этого избежать, GPIB_CommandExecute // кеширует строку комманд на время разбора. // --------------------------------------------- // инициализация передачи usbtmc_init_intransfer_newcmd( udi ); // --------------------------------------------- // перед выполнением GPIB функции нельзя удалять контекст, // ведь след комманда ( cmd1;cmd2 ) может его и не использует, а текущая (cmd1) не успеет передать данные usbtmc_delete_function_context( udi ); // ---------------------------------------------------------------------------------------------------------------------------- // вызываем обработчик комманд SCPI if( 0 <= GPIB_CommandExecute( udi, pBulkData_DeviceIn ) ) { // тут произошло преобразование в ВЕРХНИЙ РЕГИСТР!!! rc = TRUE; // ------------------------------------------------------------------- // See USB488, rev 1.0, 2003, page 13, 4.3.1.3 Status byte MAV bit if( pBulkRespond->dwDeviceOut > 0 ) GPIB_SET_MAV(); // -- устанавливаем Message Available бит если есть что ответить // ------------------------------------------------------------------- } else { rc = FALSE; // какая-то ошибка } return rc; } // ========================================================================================================================================= int usbtmc_REQUEST_DEV_DEP_MSG_IN ( USB_DEVICE_INFO * udi ) { int rc = FALSE; // USB_USBTMC_CLASS_STATUS * pusbtmcStatus = (USB_USBTMC_CLASS_STATUS *) &udi->usbtmcStatus; USB_EP_STATUS * bulk_endpoint = (USB_EP_STATUS*) &udi->EPBulkStatus; USB_PIPE_ENTRY_IN * pInPipe = (USB_PIPE_ENTRY_IN*) &bulk_endpoint->InPipe; USB_BULKMESSAGE_STATUS * pBulkMessage = (USB_BULKMESSAGE_STATUS*) &udi->BulkMessageStatus; USB_BULKRESPOND_STATUS * pBulkRespond = (USB_BULKRESPOND_STATUS*) &udi->BulkRespondStatus; BULKIN_HEADER * pBulkInHeader = (BULKIN_HEADER*) pInPipe->pData; BYTE * pData = (BYTE*) pInPipe->pData + sizeof(BULKIN_HEADER); // pdata !!! // ------------------------------------------------------------------------------------------------------------------------------------- // ============== INITIALIZE RESPOND =============== pInPipe->dwLength = 0; pBulkRespond->nBytesSent = 0; // обнуляем статистику отправленных байт pBulkRespond->bTag = pBulkMessage->bTag; pBulkRespond->MsgID = DEV_DEP_MSG_IN; pBulkRespond->INTransferInProgress = TRUE; // see USBTMC spec, rev 1.0, 2003, page 12, point 5 // нужно: пример: хост послал DEV_DEP_MSG_OUT а потом передумал и решил прервать запрос BulkIN, даже не начав читать! //----------------------------------------------------- // WARNING! 29/08/18 // При включении оптимизации компилятор может заменить вызов // memset() на последовательность инструкций быстрой очистки // памяти на основе инструкции STM, которая требует, чтобы // адрес был выровнен на 4. Т.к. указатель pBulkInHeader // был получен преобразованием типа из pData типа uint8_t, // то и полученный указатель может оказаться невыровнен на 4. // Однако после кастинга к типу BULKIN_HEADER* компилятор про // это "забывает" и считает адрес @pBulkInHeader выровненным. // На некотором уровне оптимизации приведет к HardFault, // т.к. компилятор по праву считает @pBulkInHeader честным // адресом на выровненную структуру и вставляет инструкцию STM // (STM R6!, {R0-R2}, где R0..R2 = 0x00000000 ) // ЧТОБЫ избежать такого поведения, требуется ОБРАТНО скастить // указатель @pBulkInHeader к типу uint8_t, чтобы у компилятора // не возникала соблазна провернуть вышеописанный трюк. // При банальном (BYTE*)pBulkInHeader полученный указатель уже // не считается выровненным на 4, и вызов memset() будет нельзя // заменить на STM-инструкцию, и компилятору придется сделать // вызов memclr(), что уже не приведет к ошибке. // Такие дела. memset( (BYTE*) /* проблемы с оптимизацией: НУЖНО ОБЯЗАТЕЛЬНО КАСТИТЬ К ТИПУ uint8_t !!!! */ pBulkInHeader, 0x00, sizeof(BULKIN_HEADER) ); // =============== DEFAULT ==================== pBulkInHeader->MsgID = pBulkRespond->MsgID; pBulkInHeader->bTag = pBulkRespond->bTag; // -- must match with bTag in last message pBulkInHeader->bTagInverse = ~pBulkInHeader->bTag; // TransferSize инициализирована в DEV_DEP_MSG_OUT // ------------------------------------------------------ rc = TRUE; // ------------------------------------------------------ // Когда приходит DEV_DEP_MSG_OUT данные, которые нужно отправить в ответ // складываются в выходной буфер со смещением в sizeof(BULKIN_HEADER) // в REQUEST_DEV_DEP_MSG_OUT модифицируется лишь HEADER, и отправляется ответ // Это достигается за счет разнесения буферов приема комманд DEV_DEP_MSG_OUT // и REQUEST_DEV_DEP_MSG_OUT. //----------------------------------------------------- { // хост прислыает в Bulk-Out заголовке размер передачи TransferSize // для запроса REQUEST_DEV_DEP_MSG_IN это число определяет количество данных // которые требуется прочитать из устройства. Свыше этого числа отправлять нельзя USB_INTERRUPT_ENABLE_BULKIN_NAK(); if(pBulkRespond->bIsLastTransfer == TRUE && pBulkRespond->dwDeviceOut > pBulkRespond->InTransferSize) pBulkRespond->bIsLastTransfer = FALSE; // ------------------------------------------------------------------------------------------------------ // Is it the last transfer? if( pBulkRespond->bIsLastTransfer == TRUE ) { // Yes, it is the last transfer // Is anything to send? if( 0 == pBulkRespond->dwDeviceOut ) { // No. // IEEE 488.2, 11.5.1.1.7 Bit 2 — Query ERROR (QYE) // An attempt is being made to read data from the Output Queue when no output is either present or pending if( pBulkRespond->bEndOfMessage ) { // this is the last read event of long transfer // ignore this read (void)pBulkRespond->bEndOfMessage; } else GPIB_SET_QRE(); } pData[pBulkRespond->dwDeviceOut++] = '\n'; } // ------------------------------------------------------------------------------------------------------ // --------------------------------------------------------------------------------------------------------- pBulkInHeader->stRespondMessage.TransferSize = MIN(pBulkRespond->dwDeviceOut, pBulkRespond->InTransferSize); // --------------------------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------- pBulkRespond->dwDeviceOut+=sizeof(BULKIN_HEADER); // для упрощения учета данных // ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------- pInPipe->dwLength = sizeof(BULKIN_HEADER) + pBulkInHeader->stRespondMessage.TransferSize; // ----------------------------------------------------------------------------------------- // ----------------------------------------------------------------- // Установка аттрибута End Of Message (EOM) в BULKIN заголовке if( pBulkRespond->bIsLastTransfer == TRUE) pBulkInHeader->stRespondMessage.bmTransferAttributes |= (1<<0); // Если функция не сбросила bIsLastTransfer, то передача считается последней else pBulkInHeader->stRespondMessage.bmTransferAttributes &= ~(1<<0); // ----------------------------------------------------------------- } return rc; } // ========================================================================================================================================== // ========================================================================================================================================== void usbtmc_flush_buffer( TENDPOINT_LOG_ADDRESS apropriate_endpoint, USB_PIPE_ENTRY * pipe_entry ) { switch( apropriate_endpoint ) { case USB_EP_LOG_ADDRESS_BULK_OUT: { if( pipe_entry->pDefaultBuffer == gEP2BufOutExpand ) s_memset( (BYTE*)gEP2BufOutExpand, 0x00, USB_MAX_BULKOUT_BUFFERSIZE ); } break; case USB_EP_LOG_ADDRESS_BULK_IN: { if( pipe_entry->pDefaultBuffer == gEP2BufInExpand ) s_memset( (BYTE*)gEP2BufInExpand, 0x00, USB_MAX_BULKIN_BUFFERSIZE ); } break; } } // ========================================================================================================================================== void usbtmc_init_intransfer( USB_DEVICE_INFO * udi ) { USB_EP_STATUS * bulk_endpoint = (USB_EP_STATUS*) &udi->EPBulkStatus; USB_PIPE_ENTRY_IN * pPipeIn = (USB_PIPE_ENTRY_IN*) &bulk_endpoint->InPipe; USB_BULKRESPOND_STATUS * pBulkRespond = (USB_BULKRESPOND_STATUS*) &udi->BulkRespondStatus; BULKIN_HEADER * pBulkInHeader = (BULKIN_HEADER*) pPipeIn->pDefaultBuffer; GPIB_COMMAND_TREE * pGpib = (GPIB_COMMAND_TREE*) &udi->usbtmcGpib; // ------------------------------------------------------------------------------------------------------------------------- // ------------------------------------------------------------------------------------------------------------------------- pPipeIn->pData = pPipeIn->pDefaultBuffer; // указываем буфер для передачи pBulkInHeader->stRespondMessage.TransferSize = 0; // обнуляем размер передачи pGpib->pData = pPipeIn->pData + sizeof(BULKIN_HEADER); // указываем начало буфера для складывания ответов на комманды pBulkRespond->bIsLastTransfer = TRUE; // считаем сообшение коротким (в одну Transfer ) // устанавливаем максимальное количество данных, которое можно записать в выходной буфер pBulkRespond->RespondBufferSize = USB_MAX_BULKIN_BUFFERSIZE - sizeof(BULKIN_HEADER); pBulkRespond->dwDeviceOut = 0; } void usbtmc_init_intransfer_newcmd( USB_DEVICE_INFO * udi ) { USB_EP_STATUS * bulk_endpoint = (USB_EP_STATUS*) &udi->EPBulkStatus; USB_BULKRESPOND_STATUS * pBulkRespond = (USB_BULKRESPOND_STATUS*) &udi->BulkRespondStatus; if( pBulkRespond->dwDeviceOut > 0 ) { /* IEEE 488.2, 11.5.1.1.7 Bit 2 — Query ERROR (QYE) Query Errors are detected by the Output Queue Control, see 6.1.10. This event bit indicates that either 1) An attempt is being made to read data from the Output Queue when no output is either present or pending, or > 2) Data in the Output Queue has been lost. See 6.5.7 for a complete description. The Query Error bit shall not be set to report any other condition. Events that generate Query Errors shall not also generate Execution Errors, Command Errors, or Device-Specific Errors. */ // The device already has some bytes of respond. // This call will make the device to lost the respond. // In this case the Query Error bit should be set in Event Status Register GPIB_SET_QRE(); } // ------------------------------------------------------------------------------------------------------------------------- usbtmc_init_intransfer( udi ); } #endif