Home |
Attachment/Detachment Callbacks Structure of a Passive USB Client |
Structure of a Passive USB ClientA passive USB client is a driver which does not need a thread. Rather, it only publishes an API to be called by the application and communicates with the USB device only from this API. A typical passive USB client is a printer driver. Below is a framework for a very simple USB printer driver. Please note that the code below is intended as an example only. RTUSB-32 contains a real printer class driver. Its complete source code is available in file Driver\Rtu32\Usbprint.c. The example below is the demo program PrintDemo. PrintDemo has an attachment/detachment callback called USBPrinterEvent. In the RTUConnect event, it checks whether this new device has the desired interface. If so, the required pipes are opened. Since the pipes are also used by the application callable API (functions PrinterInit and Print in this example), the pipes are protected with semaphore Lock. The API functions PrinterInit and Print lock the semaphore to get the pipe handles. These handles are then used to communicate with the printer. If no printer is connected, the use of the invalid pipe handles will return error code RTU_INVALID_PIPE. Passive USB Client (PrintDemo):#include <stdlib.h> #include <stdio.h> #include <string.h> #include <conio.h> #include <Rtk32.h> #include <Rtusb.h> // The device we want has USB class RTU_USB_CLASS_PRINT declared in Rtusb.h // subclasses #define SUBCLASS_PRINT 0x01 // protocols #define USB_PROT_UNI_DIR 0x01 #define USB_PROT_BI_DIR 0x02 #define USB_PROT_1284 0x03 // class specific request codes (see USB Printer Class spec). #define GET_DEVICE_ID 0x00 #define GET_PORT_STATUS 0x01 #define SOFT_RESET 0x02 #define TIMEOUT 2000 // milliseconds // DriverDataType is a structure with data the driver has to maintain // per device. In this case, it's only two pipes. typedef struct { RTUPipeHandle ControlPipe; RTUPipeHandle PipeOut; } DriverDataType; // We only want to support one device at a time, so a global variable will do // multi-device drivers should allocate this from the heap and maintain it // through RTUSet/GetClientData() static DriverDataType DriverData; // Still, the calling application and the attach/detach callback must access // DriverData, so we must protect it with a semaphore. static RTKSemaphore Lock; /*-----------------------------------*/ static void LockPrinter(DriverDataType * D) // function to lock the driver's semaphore and return a copy // of the device's data { if (Lock == NULL) Lock = RTKCreateSemaphore(ST_RESOURCE, 1, "USB Printer Lock"); RTKWait(Lock); if (D) *D = DriverData; } /*-----------------------------------*/ static void UnlockPrinter(void) // release driver semaphore previously locked with LockPrinter { RTKSignal(Lock); } /*-----------------------------------*/ void PrinterInit(void) // send a soft reset command to the printer { int Result; DriverDataType D; LockPrinter(&D); Result = RTUSendSetup(D.ControlPipe, 0x21, SOFT_RESET, 0, 0, 0, NULL); if (Result < RTU_SUCCESS) printf("Error in PrinterInit: %s\n", RTUErrorMessage(Result)); UnlockPrinter(); } /*-----------------------------------*/ void Print(const void * Data, int Len) // send print data to the printer { int Result; DriverDataType D; // get the device data LockPrinter(&D); // release printer again. We will stick to the device that was attached // when this API was called. If the device is removed (even if a new one // is reattached), all RTUSB-32 functions will return RTU_INVALID_HANDLE. UnlockPrinter(); // send the data on its way Result = RTUStartIO(D.PipeOut, (void*)Data, Len); if (Result < RTU_SUCCESS) { // probably RTU_INVALID_HANDLE printf("no printer connected\n"); return; } // wait and see if the printer accepts the data within // a reasonable period of time Result = RTUIOWaitTimed(D.PipeOut, NULL, TIMEOUT); while (Result == RTU_TIMEOUT) { printf("timeout. Hit ESC to cancel or any other key to retry...\n"); if (getch() == 27) { RTUCancelIO(D.PipeOut); RTUIOWait(D.PipeOut, NULL); // flush cancel event return; } // wait some more Result = RTUIOWaitTimed(D.PipeOut, NULL, TIMEOUT); } // if we had an error, tell the user about it if (Result < RTU_SUCCESS) printf("Error in Print: %s\n", RTUErrorMessage(Result)); } /*-----------------------------------*/ static void RTTAPI USBPrinterEvent(RTUDeviceHandle Device, RTUSBEvent Event) // attach/detach event callback { switch (Event) { case RTUConnect: { const RTUConfigurationDescriptor * C; // look for a suitable interface const RTUInterfaceDescriptor * I = RTUFindInterface(Device, RTU_USB_CLASS_PRINT, SUBCLASS_PRINT, -1, 0, &C); // and endpoint const RTUEndpointDescriptor * E = RTUFindEndpoint(I, RTUBulk, RTUOutput); // ignore if we already have a printer, or this is not a printer if (DriverData.ControlPipe || I == NULL || E == NULL) return; // grab it. This will fail if someone else grabbed it if (RTUClaimInterface(Device, C, I, USBPrinterEvent) < RTU_SUCCESS) return; // lock our device data LockPrinter(NULL); // open all required pipes DriverData.ControlPipe = RTUOpenPipe(Device, NULL, 1); DriverData.PipeOut = RTUOpenPipe(Device, E, 1); // this is optional, but convenient as we can query this data // in the RTUDisconnect event RTUSetClientData(Device, USBPrinterEvent, &DriverData); // done UnlockPrinter(); break; } case RTUDisconnect: // lock device LockPrinter(NULL); // close all pipes RTUClosePipe(DriverData.PipeOut); RTUClosePipe(DriverData.ControlPipe); DriverData.PipeOut = DriverData.ControlPipe = 0; // done UnlockPrinter(); break; } } const char Hello[] = "Hello USB Printer Demo!\n\f"; /*-----------------------------------*/ int main(void) { RTURegisterCallback(USBPrinterEvent); // install our USB client // start USB stack printf("Found %i USB host controllers\n", FindUSBControllers()); printf("Connect a USB printer and hit return...\n"); getc(stdin); printf("printing...\n"); Print(Hello, strlen(Hello)); printf("done.\n"); return 0; } Structure of an Active USB Client
|