#pragma module el_usb_2 "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 module conatins code to access the Lascar Electronics ** EL-USB-2 temperature logger. The following routines can be called ** by an application to setup, get status, and data from the logger: ** ** open_el_usb_2 Assigns a channel to the logger and fills in the ** device data blocl ** dump_data Reads 32K data block of temperatures and humidity ** get_status Read device status block ** setup_logger Writes sample control information to device ** ** To get UGDRIVER to associated to the logger you will need to add the ** following to the sys$user_config.dat file. For more on how USB devices ** are associated to drivers see the section on USB device configuration in ** UGDRIVER Programmers Guide. ** ** device = "EL-USB-2" ** name = ug ** driver = sys$ugdriver ** begin_private ** usb_config_type = interface ** vendor_id = 4292 ** product_id = 2 ** release_number = 256 ** begin_interface ** interface_class = 0 ** interface_sub_class = 0 ** interface_protocol = 0 ** end_interface ** end_private ** end_device ** ** AUTHOR: Forrest A. Kenney 19-June-2006 ** ** REVISION HISTORY: ** ** X-2 FAK002 Forrest A. Kenney 21-August-2006 ** Remove the support code from the demo code. Rework the ** interface so the device data block and the like are passed ** as arguments. Add a check status block routine, open_el_usb_2 ** routine. Now to use the logger a progam needs to allocate ** a device data block and pass in a device name to the open ** routine. It will do all the other necessary work to get the ** logger going. ** ** X-1 FAK001 Forrest A. Kenney dd-mmmmmmmm-yyyy ** 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 /* Needed for offsetof */ #include /* Stand I/O routines */ #include /* String functions */ #include #include "ugdef.h" /* UG definitions */ #include "el_usb_2.h" /* Definitions for logger */ /* **++ ** 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); } /* **++ ** check_status_block - Simple sanity check of status block ** ** Functional description: ** ** Perform a simple sanity check of the status block before it is ** written to the device. The checks are not exhaustive it will not ** check that the day is valid for the month only that is is a legal ** value. It does a quicky check on the limits if they are enabled ** and the sample intervals. If it finds a value that does not make ** sense it will return SS$_BADPARAM and the offset into the status ** structure that is questionable. This routine will also set the count ** to zero. ** ** Calling convention: ** ** int check_status_block (STATUS_BLOCK *block, int *item_offset) ** ** Input parameters: ** ** STATUS_BLOCK *block - Pointer to status block to be checked ** int *item - Address to store offset to ** ** Output parameters: ** ** block->count - Set to 0. ** ** Return value: ** ** SS$_NORMAL - Worked ** SS$_BADPARAM - Bad item in the structure ** ** Environment: ** ** IPL 0 user mode ** **-- */ int check_status_block(STATUS_BLOCK *block, int *item_offset) { int status = SS$_NORMAL; if (((int)block->hour >= 0) && (block->hour <= 24)) { if (((int)block->min >= 0) && (block->min <= 60)) { if (((int)block->second >= 0) && (block->second <= 60)) { if ((block->day >= 1) && (block->day <= 31)) { if ((block->month >= 1) && (block->month <= 12)) { if ((block->year >= 06) && (block->year <= 99)) { if ((block->sample_rate >= MIN_SAMPLE_RATE) && (block->sample_rate <= MAX_SAMPLE_RATE)) { block->count = 0; if ((block->units != 0) && (block->units != 1)) { *item_offset = offsetof(STATUS_BLOCK, units); return(SS$_BADPARAM); } if (block->rh_temp_flags.bits.temp_high) { status = check_temp(block->temp_high, block->units); if (status != SS$_NORMAL) { *item_offset = offsetof(STATUS_BLOCK, temp_high); return(status); } } if (block->rh_temp_flags.bits.temp_low) { status = check_temp(block->temp_low, block->units); if (status != SS$_NORMAL) { *item_offset = offsetof(STATUS_BLOCK, temp_low); return(status); } } if ((block->rh_temp_flags.bits.rh_high) && (!(((int)block->rh_high >= MIN_HUMIDITY) && (block->rh_high <= MAX_HUMIDITY)))) { *item_offset = offsetof(STATUS_BLOCK, rh_high); return(SS$_BADPARAM); } if ((block->rh_temp_flags.bits.rh_low) && (!(((int)block->rh_low >= MIN_HUMIDITY) && (block->rh_low <= MAX_HUMIDITY)))) { *item_offset = offsetof(STATUS_BLOCK, rh_low); return(SS$_BADPARAM); } } else { status = SS$_BADPARAM; *item_offset = offsetof(STATUS_BLOCK, sample_rate); } } else { status = SS$_BADPARAM; *item_offset = offsetof(STATUS_BLOCK, year); } } else { status = SS$_BADPARAM; *item_offset = offsetof(STATUS_BLOCK, month); } } else { status = SS$_BADPARAM; *item_offset = offsetof(STATUS_BLOCK, day); } } else { status = SS$_BADPARAM; *item_offset = offsetof(STATUS_BLOCK, second); } } else { status = SS$_BADPARAM; *item_offset = offsetof(STATUS_BLOCK, min); } } else { status = SS$_BADPARAM; *item_offset = offsetof(STATUS_BLOCK, hour); } return(status); } /* **++ ** check_temp - quick temperature check ** ** Functional description: ** ** This code checks that temperature is valid. The check varies ** if we are Celsius. ** ** Calling convention: ** ** int check_temp (unsigned char temp, unsigned char units) ** ** Input parameters: ** ** temp - Temp in F or C ** units - 0 -> F 1 -> C ** ** Output parameters: ** ** None ** ** Return value: ** ** SS$_NORMAL - Temp valid ** SS$_BADPARAM - Illegal temp ** ** Environment: ** ** IPL 0 user mode ** **-- */ int check_temp(unsigned char temp, unsigned char units) { int status = SS$_NORMAL; if (units == 0) { if ((temp < MIN_FAHRENHEIT) || (temp > MAX_FAHRENHEIT)) { status == SS$_BADPARAM; } } else { if ((temp < MIN_CELSIUS) || (temp > MAX_CELSIUS)) { status == SS$_BADPARAM; } } return(status); } /* **++ ** dump_data - Read the logged data from the device ** ** Functional description: ** ** Read all the data points from the logger. The data format is ** pairs of bytes temp and RH. At data pair that starts with a temp ** of 0xff signals that the last pair is the last valid data point. ** ** Calling convention: ** ** int dump_data (DEVICE_DATA *device, LOGGER_DATA *data) ** ** Input parameters: ** ** DEVICE_DATA *device - Pointer to device data ** LOGGER_DATA *data - Pointer to data storage area. ** ** Output parameters: ** ** None ** ** Return value: ** ** SS$_NORMAL - Worked ** USB$_XXX - Failed ** SS$_XXXX - Failed ** ** Environment: ** ** User mode IPL 0. ** **-- */ int dump_data (DEVICE_DATA *device, LOGGED_DATA *data) { char in_msg[4]; char out_msg[4]; unsigned char *buffer = (unsigned char *) data; unsigned short int *data_size; int data_left; int read_size = 513; int status; UG_IOSB iosb; /* ** Command to tell controller to dump data */ out_msg[0] = 0x03; out_msg[1] = 0xff; out_msg[2] = 0xff; out_msg[3] = 0x00; data_size = (unsigned short int *) &in_msg[1]; /* ** Send control message telling device it is in command mode */ status = send_to_device(device, 2, 2, 0, 0, (void *) 0); if (status == SS$_NORMAL) { /* ** Send command */ status = sys$qiow(0, device->channels[device->bulk_out_pipe_index], IO$_WRITEVBLK, &iosb, 0, 0, &out_msg[0], 3, USB$_SHORT_XFER_OK, device->pipe_handles[device->bulk_out_pipe_index], 0, 0); if (status == SS$_NORMAL) { if (iosb.status == SS$_NORMAL) { status = sys$qiow(0, device->channels[device->bulk_in_pipe_index], IO$_READVBLK, &iosb, 0, 0, &in_msg[0], 3, 0, device->pipe_handles[device->bulk_in_pipe_index], 0, 0); if (status == SS$_NORMAL) { if (iosb.status == SS$_NORMAL) { data_left = (int) *data_size; /* ** Read 512 byte chunks until all data is drained from the ** device. To get it to give 512 byte chunks you have to ask ** for 513 bytes. Don't ask it is what is needed to make the ** device happy. */ do { status = sys$qiow(0, device->channels[device->bulk_in_pipe_index], IO$_READVBLK, &iosb, 0, 0, (void *) buffer, read_size, USB$_SHORT_XFER_OK, device->pipe_handles[device->bulk_in_pipe_index], 0, 0); if (status == SS$_NORMAL) { if (iosb.status != SS$_NORMAL) { status = iosb.usb_status; } } data_left = data_left - iosb.msg_len; buffer = buffer + iosb.msg_len; } while ((status == SS$_NORMAL) && (data_left > 0)); } else { status = iosb.usb_status; } } } /* ** Get device out of command mode */ status = send_to_device(device, 2, 4, 0, 0, (void *) 0); } } return(status); } /* **++ ** 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 == 4292) && (device_descriptor.ug$w_idproduct == 2)) { status = SS$_NORMAL; } else { status = SS$_IVDEVNAM; } } else { status = iosb.status; } } } else { status = iosb.status; } } } else { status = iosb.status; } } return(status); } /* **++ ** get_status - Get device status data ** ** Functional description: ** ** Get the device status data. ** ** Calling convention: ** ** void get_status (DEVICE_DATA * device, STATUS_BLOCK *block) ** ** Input parameters: ** ** DEVICE_DATA *data - Pointer to Device Data Block ** STATUS_BLOCK *block - Pointer to status block to hold device data ** ** Output parameters: ** ** None ** ** Return value: ** ** None ** ** Environment: ** ** User mode ** **-- */ int get_status(DEVICE_DATA *device, STATUS_BLOCK *block) { unsigned char msg[3]; unsigned char resp[3]; unsigned short int *data_size = (unsigned short int *) &resp[1]; int request_size; int status; UG_IOSB iosb; msg[0] = 00; msg[1] = 0xff; msg[2] = 0xff; /* ** Send control message telling device we want to get status data */ status = send_to_device(device, 2, 2, 0, 0, (void *) 0); if (status == SS$_NORMAL) { status = sys$qiow(0, device->channels[device->bulk_out_pipe_index], IO$_WRITEVBLK, &iosb, 0, 0, &msg[0], 3, USB$_SHORT_XFER_OK, device->pipe_handles[device->bulk_out_pipe_index], 0, 0); if (status == SS$_NORMAL) { if (iosb.status == SS$_NORMAL) { status = sys$qiow(0, device->channels[device->bulk_in_pipe_index], IO$_READVBLK, &iosb, 0, 0, &resp[0], 3, 0, device->pipe_handles[device->bulk_in_pipe_index], 0, 0); if (status == SS$_NORMAL) { if (iosb.status == SS$_NORMAL) { request_size = *data_size + 1; status = sys$qiow(0, device->channels[device->bulk_in_pipe_index], IO$_READVBLK, &iosb, 0, 0, (void *)block, request_size, USB$_SHORT_XFER_OK, device->pipe_handles[device->bulk_in_pipe_index], 0, 0); if (status == SS$_NORMAL) { if (iosb.status == SS$_NORMAL) { status = send_to_device(device, 2, 4, 0, 0, (void *) 0); } else { status = iosb.status; } } } else { status = iosb.status; } } } else { status = iosb.status; } } } return(status); } /* **++ ** map_pipes - Walk pipes and find directions and type ** ** Functional description: ** ** Walk all the pipes looking for the interrupt pipe index. This is ** needed so we can target reads 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_index; /* ** 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. */ for (pipe_index = 1; pipe_index < device->pipe_count; pipe_index++) { if (device->pipe_type[pipe_index] == UG$_PIPE_TYPE_BULK) { if (device->pipe_direction[pipe_index] == USB$_DIRECTION_IN) { device->bulk_in_pipe_index = pipe_index; } else { device->bulk_out_pipe_index = pipe_index; } } } return(SS$_NORMAL); } /* **++ ** open_el_usb_2 - Get device going ** ** Functional description: ** ** Assign the initial channel to the device and call all the code ** necessary to get the logger ready for action. ** ** Calling convention: ** ** int open_el_usb_2 (DEVICE_DATA *device, char *name, ** void (*rtn)(), unsigned int ast_param); ** ** Input parameters: ** ** DEVICE_DATA *device - Pointer to device data block ** *name - Pointer to device name string ** void (*rtn)() - Address of AST routine to be called if ** device is removed. ** unsigned int ast_param - AST parameter ** ** Output parameters: ** ** None ** ** Return value: ** ** None ** ** Environment: ** ** IPL 0 user mode ** **-- */ int open_el_usb_2(DEVICE_DATA *device, char *name, void (*rtn)(), unsigned int ast_param) { int status; UG_IOSB iosb; device->device_name.dsc$b_dtype = DSC$K_DTYPE_T; device->device_name.dsc$b_class = DSC$K_CLASS_S; device->device_name.dsc$w_length = strlen(name); device->device_name.dsc$a_pointer = name; status = sys$assign(&device->device_name, &device->channels[0], 0, 0, 0); if (status & SS$_NORMAL) { status = get_channels(&device->channels[0], &device->pipe_count, &device->pipe_handles[0]); if (status == SS$_NORMAL) { status = associate_channels(&device->device_name, &device->channels[0], device->pipe_count, &device->pipe_handles[0], &device->pipe_direction[0], &device->pipe_type[0]); if (status == SS$_NORMAL) { status = map_pipes(device); if (status == SS$_NORMAL) { if ((void *)rtn != (void *) 0) { status = sys$qiow(0, device->channels[0], IO$_SETMODE, &iosb, 0, 0, rtn, ast_param, UG$_ENABLE_AST, device->pipe_handles[0], 0, 0); if (status == SS$_NORMAL) { if (iosb.status != SS$_NORMAL) { status = iosb.status; } } } } } } } return(status); } /* **++ ** send_to_device - Send control data to the device ** ** Functional description: ** ** This routine will send the data to the device. ** ** Calling convention: ** ** int send_to_device (DEVICE_DATA *device, unsigned char brequest, ** unsigned short int wval, ** unsigned short int windex, ** unsigned short int length, ** void *buffer); ** ** Input parameters: ** ** device - Pointer to device data block ** brequest - Request type ** wval - Request Value ** windex - Data indes ** length - Data size in bytes ** buffer - Pointer to data buffer ** ** Output parameters: ** ** Buffer is updated ** ** Return value: ** ** SS$_NORMAL Worked ** SS$_XXX Failed ** USB$_XXX Failed ** ** Environment: ** ** User mode ** **-- */ int send_to_device(DEVICE_DATA *device, unsigned char brequest, unsigned short int wval, unsigned short int windex, unsigned short int length, void *buffer) { int status; DEVICE_REQUEST device_request; UG_IOSB iosb; device_request.ug$b_bmrequesttype = 0x40; // Host to Device, Vendor, Device device_request.ug$b_brequest = brequest; device_request.ug$w_wvalue = wval; device_request.ug$w_windex = windex; device_request.ug$w_wlength = length; status = sys$qiow(0, device->channels[0], IO$_SETMODE, &iosb, 0, 0, &device_request, sizeof(DEVICE_REQUEST), UG$_DEVICE_REQUEST, device->pipe_handles[0], buffer, 0); if (status == SS$_NORMAL) { if (iosb.status != SS$_NORMAL) { status = iosb.status; } } return(status); } /* **++ ** setup_logger - Set up the logger ** ** Functional description: ** ** This routine takes a status block and writes it to the logger to ** make those the active settings for the device. ** ** Calling convention: ** ** int setup_logger (DEVICE_DATA *device, STATUS_BLOCK *block, ** int *item_offset) ** ** Input parameters: ** ** DEVICE_DATA *device - Pointer to device data block ** STATUS_BLOCK *block - Pointer to setting block for logger. ** int *item_offset - Offset to item with invalid data ** ** Output parameters: ** ** None ** ** Return value: ** ** SS$_NORMAL - Worked ** SS$_BADPARAM - Invalid data in status block item offset is ** offset to intem in status block in error ** USB$_XXX - Failed ** SS$_XXXX - Failed ** ** Environment: ** ** User mode IPL 0. ** **-- */ int setup_logger(DEVICE_DATA *device, STATUS_BLOCK *block, int *item_offset) { char in_msg[4]; char out_msg[4]; int status; UG_IOSB iosb; /* ** Before we do anything lets make sure the the new data makes sense */ status = check_status_block(block, item_offset); if (status == SS$_NORMAL) { /* ** Send attention message to controller */ status = send_to_device(device, 2, 2, 0, 0, (void *) 0); if(status == SS$_NORMAL) { /* ** Send command to controllers */ out_msg[0] = 0x01; out_msg[1] = 0x40; out_msg[2] = 0x00; out_msg[3] = 0x00; status = sys$qiow(0, device->channels[device->bulk_out_pipe_index], IO$_WRITEVBLK, &iosb, 0, 0, &out_msg[0], 3, USB$_SHORT_XFER_OK, device->pipe_handles[device->bulk_out_pipe_index], 0, 0); if (status == SS$_NORMAL) { if (iosb.status == SS$_NORMAL) { /* ** Send Status block to the controller */ status = sys$qiow(0, device->channels[device->bulk_out_pipe_index], IO$_WRITEVBLK, &iosb, 0, 0, (void *)block, 64, USB$_SHORT_XFER_OK, device->pipe_handles[device->bulk_out_pipe_index], 0, 0); if (status == SS$_NORMAL) { if (iosb.status == SS$_NORMAL) { status = sys$qiow(0, device->channels[device->bulk_in_pipe_index], IO$_READVBLK, &iosb, 0, 0, (void *) &in_msg[0], 3, USB$_SHORT_XFER_OK, device->pipe_handles[device->bulk_in_pipe_index], 0, 0); if (status == SS$_NORMAL) { if (iosb.status != SS$_NORMAL) { status = iosb.usb_status; } } } else { status = iosb.usb_status; } } } else { status = iosb.usb_status; } } /* ** Get device out of attention state */ status = send_to_device(device, 2, 4, 0, 0, (void *) 0); } } return(status); }