#pragma module UG_EXAMPLE "X-2" /* ******************************************************************************** ** ** ** © Copyright 1976, 2005 Hewlett-Packard Development Company, L.P. ** ** ** ** Confidential computer software. Valid license from HP and/or ** ** its subsidiaries required for possession, use, or copying. ** ** ** ** Consistent with FAR 12.211 and 12.212, Commercial Computer Software, ** ** Computer Software Documentation, and Technical Data for Commercial ** ** Items are licensed to the U.S. Government under vendor's standard ** ** commercial license. ** ** ** ** Neither HP nor any of its subsidiaries shall be liable for technical ** ** or editorial errors or omissions contained herein. The information ** ** in this document is provided "as is" without warranty of any kind and ** ** is subject to change without notice. The warranties for HP products ** ** are set forth in the express limited warranty statements accompanying ** ** such products. Nothing herein should be construed as constituting an ** ** additional warranty. ** ** ** ******************************************************************************** ** ** ** FACILITY: ** ** Example ** ** ABSTRACT: ** ** This program will take two USB to RS232 converters based on the ** Prolific PL2303 chip and loop them to each other. It is intended as ** a simple example of how to use the USB Generic "UG" driver to talk to ** a USB device. The program will do the following steps: ** ** 1) Assign a channel to each device. ** 2) It will then find out how many pipes each device open channels ** for each pipe ** 3) It will associate a channel for each pipe ** 4) Will enable a hangup AST for each device ** 5) Set each device to 38K baud; 8 bits; no parity; 1.5 stop bits ** 6) It will then exchange block of data and check for correctness ** 7) Will prompt a user to see if step 6 is to be repeated. ** ** You will need one or both of these added to sys$user_config.dat to ** use this example. ** ** ** device = "Prolific serial" ** name = ug ** driver = sys$ugdriver ** begin_private ** usb_config_type = interface ** vendor_id = 1659 ** product_id = 8963 ** begin_interface ** interface_class = -1 ** interface_sub_class = 0 ** interface_protocol = 0 ** end_interface ** end_private ** end_device ** ** device = "Radio Shack" ** name = ug ** driver = sys$ugdriver ** begin_private ** usb_config_type = interface ** vendor_id = 5203 ** product_id = 16422 ** begin_interface ** interface_class = -1 ** interface_sub_class = 0 ** interface_protocol = 0 ** end_interface ** end_private ** end_device ** ** AUTHOR: Forrest A. Kenney ** ** Your Name 07-April-2005 ** ** REVISION HISTORY: ** ** X-2 FAK002 Forrest A. Kenney 25-October-2005 ** Correct the comments in the header changes in the UCM code ** made some of them obsolete. Also fix two cases of off by ** one in the catch_reset code. It would get a negative array ** offset and that would crash the application when exiting. ** ** X-1 FAK001 Forrest A. Kenney 03-August-2005 ** Initial check in. **/ /* ** Include files */ #include #include /* String descriptors */ #include /* VMS I/O function codes */ #include #include /* LIB RTL routines */ #include /* C signals */ #include /* System error codes */ #include /* System routines */ #include /* Stand I/O routines */ #include /* String functions */ #include #include "ugdef.h" /* UG definitions */ /* Globals */ #define MAX_PIPES 10 typedef struct _iosb { short int status; short int msg_len; int usb_status; } UG_IOSB; typedef struct _device_data { short int channels[MAX_PIPES]; int pipe_count; uint64 pipe_handles[MAX_PIPES]; int pipe_state[MAX_PIPES]; int pipe_type[MAX_PIPES]; int pipe_direction[MAX_PIPES]; int bulk_in_pipe_index; int bulk_out_pipe_index; int interrupt_pipe_index; struct dsc$descriptor_s device_name; UG_IOSB iosb; unsigned char interrupt_data[10]; } DEVICE_DATA; DEVICE_DATA device_1; DEVICE_DATA device_2; /* Prototypes */ void catch_reset (); int get_channels (short int *channels, int *pipe_count, uint64 *pipe_handles); int associate_channels (struct dsc$descriptor_s *devname, short int *channels, int pipe_count, uint64 *pipe_handles, int *pipe_direction, int *pipe_type); int configure_device (short int channel, uint64 pipe_handle); int exchange_data (DEVICE_DATA *device_1, DEVICE_DATA *device_2); int map_pipes (DEVICE_DATA *device); void interrupt_rtn (DEVICE_DATA *device); int ask_y_n (char *question); /* **++ ** Main - Get us started ** ** Functional description: ** ** Simple mainline code to get us started off. ** ** Calling convention: ** ** int main(argc, argv) ** ** Input parameters: ** ** argc Count of arguments not used ** argv Pointer to arguments not used ** ** Output parameters: ** ** None ** ** Return value: ** ** Lots ** ** Environment: ** ** User mode ** **-- */ int main(int argc, char *argv[]) { int status; if (argc >= 3) { device_1.device_name.dsc$b_dtype = DSC$K_DTYPE_T; device_1.device_name.dsc$b_class = DSC$K_CLASS_S; device_1.device_name.dsc$w_length = strlen(argv[1]); device_1.device_name.dsc$a_pointer = argv[1]; status = sys$assign(&device_1.device_name, &device_1.channels[0], 0, 0, 0); if (status & SS$_NORMAL) { device_1.pipe_count = 1; device_2.device_name.dsc$b_dtype = DSC$K_DTYPE_T; device_2.device_name.dsc$b_class = DSC$K_CLASS_S; device_2.device_name.dsc$w_length = strlen(argv[2]); device_2.device_name.dsc$a_pointer = argv[2]; status = sys$assign(&device_2.device_name, &device_2.channels[0], 0, 0, 0); if (status & SS$_NORMAL) { device_2.pipe_count = 1; signal(SIGINT, catch_reset); status = get_channels(&device_1.channels[0], &device_1.pipe_count, &device_1.pipe_handles[0]); if (status == SS$_NORMAL) { status = get_channels(&device_2.channels[0], &device_2.pipe_count, &device_2.pipe_handles[0]); if (status == SS$_NORMAL) { status = associate_channels(&device_1.device_name, &device_1.channels[0], device_1.pipe_count, &device_1.pipe_handles[0], &device_1.pipe_direction[0], &device_1.pipe_type[0]); if (status == SS$_NORMAL) { status = associate_channels(&device_2.device_name, &device_2.channels[0], device_2.pipe_count, &device_2.pipe_handles[0], &device_2.pipe_direction[0], &device_2.pipe_type[0]); if (status & SS$_NORMAL) { status = configure_device(device_1.channels[0], device_1.pipe_handles[0]); if (status == SS$_NORMAL) { status = configure_device(device_2.channels[0], device_2.pipe_handles[0]); if (status == SS$_NORMAL) { exchange_data(&device_1, &device_2); } } } } } else { printf("Failed to get pipe handles for %s\n", argv[2]); } } else { printf("Failed to get pipe handles for %s\n", argv[1]); } } else { printf("\nAssign for %s failed reason %08X\n", argv[1], status); } } else { printf("\nAssign for %s failed reason %08X\n", argv[1], status); } } else { printf("Not enough device specified\n"); status = SS$_NOSUCHDEV; } return status; } /* **++ ** catch_reset - Catch CTRL-C ** ** Functional description: ** ** Catch the CTRL-C signal so that we can resetart the mainline and ** undo what we have wrought. ** ** Calling convention: ** ** void catch_reset() ** ** Input parameters: ** ** None ** ** Output parameters: ** ** None ** ** Return value: ** ** None ** ** Environment: ** ** User mode. ** **-- */ void catch_reset() { int i; int status; i = device_1.pipe_count; while (i > 0) { i -= 1; status = sys$dassgn(device_1.channels[i]); if (status != SS$_NORMAL) { break; } } i = device_2.pipe_count; while (i > 0) { i -= 1; status = sys$dassgn(device_2.channels[i]); if (status != SS$_NORMAL) { break; } } } /* **++ ** get_channels - Get count of channels for the device ** ** Functional description: ** ** This routine will get the number of channels for the device, get all ** the pipe handles. Then it will get the device descriptor and we will check ** that the device and vendor id's are ok. ** ** Calling convention: ** ** int get_channels (short int *channels, int *pipe_count, ** uint64 *pipe_handles); ** ** Input parameters: ** ** None ** ** Output parameters: ** ** None ** ** Return value: ** ** SS$_IVDEVNAM - Wrong device for this test ** SS$_XXXX - Other failures ** SS$_NORMAL - Success ** ** Environment: ** ** User mode. ** **-- */ int get_channels(short int *channels, int *pipe_count, uint64 *pipe_handles) { int status; DEVICE_DESCRIPTOR device_descriptor; UG_IOSB iosb; /* ** Get the pipe count. */ status = sys$qiow(0, channels[0], IO$_SENSEMODE, &iosb, 0, 0, pipe_count, 4, UG$_GET_PIPE_COUNT, 0, 0, 0); if (status == SS$_NORMAL) { if (iosb.status == SS$_NORMAL) { status = sys$qiow(0, channels[0], IO$_SENSEMODE, &iosb, 0, 0, pipe_handles, 8 * (*pipe_count), UG$_GET_PIPE_HANDLES, 0, 0, 0); if (status == SS$_NORMAL) { if(iosb.status == SS$_NORMAL) { status = sys$qiow(0, channels[0], IO$_SENSEMODE, &iosb, 0, 0, &device_descriptor, sizeof(DEVICE_DESCRIPTOR), UG$_GET_DEVICE_DESCRIPTOR, 0, 0, 0); if (status == SS$_NORMAL) { if (iosb.status == SS$_NORMAL) { if (((device_descriptor.ug$w_idvendor == 1659) && (device_descriptor.ug$w_idproduct == 8963)) || ((device_descriptor.ug$w_idvendor == 5203) && (device_descriptor.ug$w_idproduct == 16422))) { status = SS$_NORMAL; } else { status = SS$_IVDEVNAM; } } else { status = iosb.status; } } } else { status = iosb.status; } } } else { status = iosb.status; } } return(status); } /* **++ ** associate_channels - Associate a uniqure channel for each pipe ** ** Functional description: ** ** This routine will pick assign additional channels and then associate ** each channel to a pipe. It will also pick up the pipe direction we will ** need that for later use. ** ** Calling convention: ** ** int associate_channels(struct dsc$descriptor_s *devname, ** short int *channels, int pipe_count, ** uint64 *pipe_handles, int *pipe_direction, ** int *pipe_type) ** ** Input parameters: ** ** struct dsc$descriptor_s *devname Pointer to a VMS device name ** shot int *channels Pointer to array of VMS channels ** int pipe_count Count of pipes for this device ** uint64 *pipe_handles Pointer to array of pipe handles ** int *pipe_direction Pointer to array to store pipe directiosn ** int *pipe_type Pointer to array to store pipe type ** ** Output parameters: ** ** channels Filled in with additional channels ** pipe_direction Filled in with pipe direction ** ** Return value: ** ** SS$_NORMAL - Worked ** SS$_XXXXX - Various failure status values ** ** Environment: ** ** User mode. ** **-- */ int associate_channels(struct dsc$descriptor_s *devname, short int *channels, int pipe_count, uint64 *pipe_handles, int *pipe_direction, int *pipe_type) { int i; int status; UG_IOSB iosb; for (i = 1; i < pipe_count; i++) { status = sys$assign(devname, &channels[i], 0, 0, 0); if (status != SS$_NORMAL) { break; } } if (status == SS$_NORMAL) { for (i = 0; i < pipe_count; i++) { status = sys$qiow(0, channels[i], IO$_SETMODE, &iosb, 0, 0, 0, 0, UG$_ASSOCIATE, &pipe_handles[i], 0, 0); if (status == SS$_NORMAL) { if (iosb.status == SS$_NORMAL) { status = sys$qiow(0, channels[i], IO$_SENSEMODE, &iosb, 0, 0, &pipe_direction[i], 4, UG$_GET_PIPE_DIRECTION, pipe_handles[i], 0, 0); if (status == SS$_NORMAL) { if (iosb.status == SS$_NORMAL) { status = sys$qiow(0, channels[i], IO$_SENSEMODE, &iosb, 0, 0, &pipe_type[i], 4, UG$_GET_PIPE_TYPE, pipe_handles[i], 0, 0); if (status == SS$_NORMAL) { if (iosb.status != SS$_NORMAL) { status = iosb.status; break; } } else { break; } } } else { break; } } else { break; status = iosb.status; } } else { break; } } } return(status); } /* **++ ** configure_device - Set device to known starting state ** ** Functional description: ** ** This routine will set the device to the following starting state: ** ** 8 Data Bits ** 2 Stop Bits ** No parity ** No Flow control ** 38.4K baud ** ** Calling convention: ** ** int configure_device (short int channel, uint64 pipe_handle) ** ** Input parameters: ** ** channel VMS channel associated to control pipe ** pipe_handle Pipe handle for control pipe ** ** Output parameters: ** ** None ** ** Return value: ** ** SS$_NORMAL Worked ** SS$_XXXX Failure reasons ** USB$_XXXX Failure reasons ** ** Environment: ** ** User mode. ** **-- */ int configure_device(short int channel, uint64 pipe_handle) { int status; struct line_coding { unsigned long DTERate; unsigned char CharFormat; unsigned char ParityType; unsigned char DataBits; } line_data; DEVICE_REQUEST device_request; UG_IOSB iosb; line_data.DTERate = 38400; line_data.CharFormat = 2; line_data.ParityType = 0; // 0 = None, 1 = even, 2 = odd, 3 = Mark, 4 = Space line_data.DataBits = 8; device_request.ug$b_bmrequesttype = 0X21; // Class request to Interface device_request.ug$b_brequest = 0x20; // Vendor Specific Request device_request.ug$w_wvalue = 0; device_request.ug$w_windex = 0; device_request.ug$w_wlength = sizeof(struct line_coding); status = sys$qiow(0, channel, IO$_SETMODE, &iosb, 0, 0, &device_request, sizeof(DEVICE_REQUEST), UG$_DEVICE_REQUEST, pipe_handle, &line_data, 0); if (status == SS$_NORMAL) { if (iosb.status != SS$_NORMAL) { status = iosb.status; } } return(status); } /* **++ ** exchange_data - exchange data on the two devices ** ** Functional description: ** ** This routine will finish the work needed to actually echchange data ** on the two devices. Device 1 is the read device and device 2 is the write ** device. The steps we need to do next are: ** ** 1) Find the interrupt pipes for both devices and kick off reads ** on them ** 2) Find the bulk in pipe for device 1 and the bulk out pipe for ** device 2 ** ** ** Calling convention: ** ** int exchange_data (DEVICE_DATA *device_1, DEVICE_DATA *device_2) ** ** Input parameters: ** ** device_1 ptr to data describing the first device ** device_2 ptr to data describing the second device ** ** Output parameters: ** ** None ** ** Return value: ** ** SS$_NORMAL - Worked ** USB$_XXXX - Various USB failure reasons ** SS$_XXXXX - Various system failure reasons ** SS$_DATACHECK - Data compare failed ** ** Environment: ** ** User mode. ** **-- */ int exchange_data (DEVICE_DATA *device_1, DEVICE_DATA *device_2) { #define MAX_DATA 256 unsigned char data[MAX_DATA]; unsigned char in_data[MAX_DATA]; int efn; int i; int index; int in_bytes; int in_pipe; int out_pipe; int status; UG_IOSB in_iosb; UG_IOSB out_iosb; status = lib$get_ef(&efn); if (status != SS$_NORMAL) { return(status); } /* ** Build up a block of binary data to transfer. */ for (i = 0; i < MAX_DATA; i++) { data[i] = i; } /* ** Find the input, output, and interrupt pipes and queue repeating reads to ** the interrupt pipes. */ status = map_pipes(device_1); if (status == SS$_NORMAL) { status = map_pipes(device_2); if (status == SS$_NORMAL) { in_pipe = device_1->bulk_in_pipe_index; out_pipe= device_2->bulk_out_pipe_index; /* ** Now lets queue reads to the interrupt pipes for both devices */ index = device_1->interrupt_pipe_index; status = sys$qio(0, device_1->channels[index], IO$_READVBLK, &device_1->iosb, &interrupt_rtn, device_1, &device_1->interrupt_data[0], 10, USB$_SHORT_XFER_OK, device_1->pipe_handles[index], 0, 0, 0); if (status == SS$_NORMAL) { index = device_2->interrupt_pipe_index; status = sys$qio(0, device_2->channels[index], IO$_READVBLK, &device_2->iosb, &interrupt_rtn, device_1, &device_2->interrupt_data[0], 10, USB$_SHORT_XFER_OK, device_2->pipe_handles[index], 0, 0, 0); if (status == SS$_NORMAL) { do { memset(&in_data[0], 0, MAX_DATA); status = sys$qio(efn, device_2->channels[out_pipe], IO$_WRITEVBLK, &out_iosb, 0, 0, &data[0], MAX_DATA, 0, device_2->pipe_handles[out_pipe], 0, 0, 0); if (status == SS$_NORMAL) { in_bytes = 0; do { status = sys$qiow(0, device_1->channels[in_pipe], IO$_READVBLK, &in_iosb, 0, 0, &in_data[in_bytes], MAX_DATA, USB$_SHORT_XFER_OK, device_1->pipe_handles[in_pipe], 0, 0, 0); if (status == SS$_NORMAL) { if (in_iosb.status == SS$_NORMAL) { in_bytes += in_iosb.msg_len; } else { return (in_iosb.status); } } else { return (status); } } while (in_bytes < MAX_DATA); status = sys$synch(efn, &out_iosb.status); if (status == SS$_NORMAL) { if (out_iosb.status == SS$_NORMAL) { for (i = 0; i < MAX_DATA; i++) { if (in_data[i] != data[i]) { printf("Data did not match at byte %d\n", i); status = SS$_DATACHECK; return(status); } } } else { status = out_iosb.status; break; } } else { break; } } else { break; } } while (ask_y_n("Send another message: ")); } } } } return(status); } /* **++ ** map_pipes - Walk pipes and find directions and type ** ** Functional description: ** ** Walk all the pipes looking for the bulk in, bulk out, and interrupt ** pipe index. These are needed so we can target read, writes to the correct ** pipe. ** ** Calling convention: ** ** int map_pipes (DEVICE_DATA *device); ** ** Input parameters: ** ** None ** ** Output parameters: ** ** None ** ** Return value: ** ** SS$_NORMAL - Worked ** SS$_XXXXX - Various failure reasons ** USB$_XXXX - Various USB failure reasons ** ** Environment: ** ** User mode. ** **-- */ int map_pipes(DEVICE_DATA *device) { int pipe_direction; int pipe_index; int pipe_type; int status; UG_IOSB iosb; /* ** The control pipe is always pipe zero so we can skip that one. Also we ** know the test device does not have an Isochronous pipe so we can skip ** that check. Finally, the test device only has an input interrupt pipe ** so if the pipe type is interrupt we are done no need to get direction. */ for (pipe_index = 1; pipe_index < device->pipe_count; pipe_index++) { status = sys$qiow(0, device->channels[pipe_index], IO$_SENSEMODE, &iosb, 0, 0, &pipe_type, 4, UG$_GET_PIPE_TYPE, device->pipe_handles[pipe_index], 0, 0, 0); if (status == SS$_NORMAL) { if (iosb.status == SS$_NORMAL) { if (pipe_type != UG$_PIPE_TYPE_INTERRUPT) { /* ** Must be bulk pipe get direction. */ status = sys$qiow(0, device->channels[pipe_index], IO$_SENSEMODE, &iosb, 0, 0, &pipe_direction, 4, UG$_GET_PIPE_DIRECTION, device->pipe_handles[pipe_index], 0, 0, 0); if (status == SS$_NORMAL) { if (iosb.status == SS$_NORMAL) { if (pipe_direction == USB$_XFER_IN) { device->bulk_in_pipe_index = pipe_index; } else { device->bulk_out_pipe_index = pipe_index; } } else { status = iosb.status; break; } } else { break; } } else { device->interrupt_pipe_index = pipe_index; } } else { status = iosb.status; break; } } else { break; } } return(status); } /* **++ ** interrupt_rtn - Simple Routine to output interrupt data ** ** Functional description: ** ** This routine will get called when the interrupt pipe for either ** device completes it will output data about the interrupt. ** ** The data byte we care about in the interrupt pipe is byte 9 that bytes ** has the following layout. ** ** DCD - Bit 0 ** DSR - Bit 1 ** BREAK - Bit 2 ** RING - Bit 3 ** FRAME_ERROR - Bit 4 ** PARITY_ERROR - Bit 5 ** OVER_RUN - Bit 6 ** CTS - Bit 7 ** ** Calling convention: ** ** void interrupt_rtn (DEVICE_DATA *device) ** ** Input parameters: ** ** device Pointer to data structure that describes the device. ** ** Output parameters: ** ** None ** ** Return value: ** ** None ** ** Environment: ** ** User mode. ** **-- */ void interrupt_rtn(DEVICE_DATA *device) { typedef struct _int_bits { uint8 DCD : 1, DSR : 1, BREAK : 1, RING : 1, FRAME_ERROR : 1, PARITY_ERROR : 1, OVER_RUN : 1, CTS : 1; } INT_BITS; int index = device->interrupt_pipe_index; int status; INT_BITS *int_data; if (device->iosb.status == SS$_NORMAL) { if (device->iosb.msg_len > 8) { int_data = (INT_BITS *) &device->interrupt_data[8]; printf("Device %s received and interrupt It decodes as follows:\n", device->device_name.dsc$a_pointer); if (int_data->DCD) { printf(" DCD\n"); } if (int_data->DSR) { printf(" DSR\n"); } if (int_data->BREAK) { printf(" BREAK\n"); } if (int_data->RING) { printf(" RING\n"); } if (int_data->FRAME_ERROR) { printf(" FRAME ERROR\n"); } if (int_data->PARITY_ERROR) { printf(" PARITY ERROR\n"); } if (int_data->OVER_RUN) { printf(" OVER RUN\n"); } if (int_data->CTS) { printf(" CTS\n"); } status = sys$qiow(0, device->channels[index], IO$_READVBLK, &device->iosb, &interrupt_rtn, device, &device->interrupt_data[0], 10, USB$_SHORT_XFER_OK, device->pipe_handles[index], 0, 0, 0); if (status != SS$_NORMAL) { printf("Unable to re-enable AST\n"); } } else { printf("Device %s received and interrupt event with %d bytes must be %d bytes to be valid\n", device->device_name.dsc$a_pointer, device->iosb.msg_len, 9); } } return; } /* **+ ** ask_y_n - Ask simple Y/N question ** ** Functional Description: ** ** This routine will ask a simple question that is answered with a Y or N. ** If the user does not enter a Y or an N then error message will be output and ** the question will be asked again. ** ** Explicit Inputs: ** ** question - Pointer to question to be asked ** ** Implicit Inputs: ** ** None ** ** Local Variables: ** ** resp - Character string to read users answer into ** ** status - Return status from program ** ** Outputs: ** ** returns - 1 for a yes answer 0 for a no answer ** ** NOTES: ** ** None ** **- */ int ask_y_n(char *question) { char resp[2]; int status = -1; resp[1] = '\0'; do { printf("%s", question); resp[0] = (char)toupper(getchar()); do { if (feof(stdin) || ferror(stdin)) { printf("\n\nUser requested exit.\n\n"); LIB$SIGNAL(SS$_ENDOFFILE | STS$K_SEVERE); /* Make EOF fatal */ } } while (getchar() != '\n'); if (resp[0] == 'Y') status = 1; else if (resp[0] == 'N') status = 0; else printf("\nYou must enter a Y or an N.\n"); } while (status < 0); return(status); }