#pragma module UG_SCALE_EXAMPLE "X-1" /* ******************************************************************************** ** ** ** © 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 is a simple program to drive the Radio Shack electronic ** USB scale. This scale had radio shack part number 26-950. It shows ** up to the USB stack as a Human Interface device with a vendor specific ** protocol. While the UGDRIVER was not designed to allow support of HID ** devices this program will show many of the steps needed to support a HID ** device. No example can cover all the steps needes to fully support a random ** device. This example tries to cover the major steps. ** ** The steps this programs takes are as follows: ** ** 1) Assign a channel to the UG device. This channel becomes ** the control pipe for the device. ** 2) Calls the get_channels to find out how many pipes ** the device has. get_channels gets opaque handles to each ** of the pipes. Finally it does a quick check by reading ** the device descriptor to make sure we are talking to the ** correct device. ** 3) Calls the associate_channels to bind OpenVMs channels to ** pipes. ** 4) Calls the configure_device routine. This does sevral steps ** ** a) reads the HID descriptor. This is needed to know ** how large the report descriptor is. ** b) get the device idle rate this is how often ** the device will interrupt the system. ** c) set the idle rate to 0 which say only interrupt ** when something changes. For the scale this is ** really a noop. The AD converters in the scale ** jitter and will interrupt about as fast as we are ** willing to read date. ** d) gets the input report descriptor. This is needed to ** know how much and what kind of data is returned ** by the scale. Then calls a routine to decode and ** output the report descriptor. This is far from a ** complete decoded but gives a starting point. You ** need to read the HID specification to understand ** how HID reports are made and parsed. ** ** 5) Calls the calibrate routine. This routine samples the ** scale a number of times to get a zero point. Note if ** something is on the scale that will be the zero weight. ** 6) Calls get_weight. This routine loops reading the scale ** on a regular basis and displays the results. Note the ** scale has a little jitter and seeing a 1 or 2 gram change ** is not unexpected. ** ** This program was not meant to be the ultimate scale tool there are ** lots of things that could be done to it. It is an attempt to cover most ** of the major steps needed to use UGDRIVER to control a USB HID device but ** be simple and compact enough to be useful. There are two magic constants ** used to get get reading into grams and ounces. These were determined by ** weighing items with the scale and a postal scale and doing the math. They ** are not perfect but provided a good correlation across a modest sample of ** items. There is noting the scale reports back to tell you what these ** constants are. ** ** To get UGDRIVER to associated to the scale 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 = "Radio Shack Scale" ** name = ug ** driver = sys$ugdriver ** begin_private ** usb_config_type = interface ** vendor_id = 8755 ** product_id = 25379 ** begin_interface ** interface_class = 3 ** interface_sub_class = 0 ** interface_protocol = 0 ** end_interface ** end_private ** end_device ** ** AUTHOR: Forrest A. Kenney ** ** Your Name 23-October-2005 ** ** REVISION HISTORY: ** ** X-1 FAK001 Forrest A. Kenney 26-October-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 interrupt_pipe_index; struct dsc$descriptor_s device_name; UG_IOSB iosb; unsigned char interrupt_data[10]; } DEVICE_DATA; #define CHANGE_MIN 3 // Reading has to change by this much to be meaningful #define REPORT_MAX 512 #define SAMPLE_SIZE 10 unsigned char report_desc[REPORT_MAX]; int report_size; int sample_count; uint64 sample_data[SAMPLE_SIZE]; uint32 zero; float grams_const = 0.3715; // Scale units to Grams float ounces_const = 0.01308; // Scale units to OZ DEVICE_DATA device_1; /* 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 map_pipes (DEVICE_DATA *device); int ask_y_n (char *question); int calibrate(); int get_weight(); void decode_report(); /* **++ ** 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 index; int status; /* ** Erase the display and outpt some data about the program */ printf("\033[2J\033[1;1f"); printf("\tThis program allows you to use the Radio Shack USB Electronic\n"); printf("Scale. It will first output a the HID report descriptor this can easily\n"); printf("removed but is left in to show what it looks like.\n"); printf("\n"); printf("\tIt will then wait for you to enter a y or n followed by a to \n"); printf("calibrate the scale. The code will happily set its ZERO weight to pretty much\n"); printf("any value so if there is something on the scale that will be the ZERO weight.\n"); printf("\n"); printf("\tThe scale is limited to 5 Kilograms so please do not overload it!\n"); printf("\n\tTo end the program use CTRL-C or CTRL-Y\n\n"); if (argc >= 2) { 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) { 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 = 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 = configure_device(device_1.channels[0], device_1.pipe_handles[0]); if (status == SS$_NORMAL) { status = map_pipes(&device_1); if (status == SS$_NORMAL) { status = calibrate(); do { status = get_weight(); } while (status == SS$_NORMAL); } else { printf("Unable to map pipes\n"); } } } 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("Not enough device specified\n"); status = SS$_NOSUCHDEV; } return status; } /* **++ ** catch_reset - Catch CTRL-C ** ** Functional description: ** ** Catch the CTRL-C signal free up the device and exit. ** ** 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; } } printf("\033[0m"); status = sys$exit(SS$_NORMAL); } /* **++ ** 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 == 8755) && (device_descriptor.ug$w_idproduct == 25379)) { 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 get the HID descriptor and HID report for the device. ** ** 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 idle = 0; int status = SS$_NORMAL; DEVICE_REQUEST device_request; UG_IOSB iosb; typedef struct _hid_descriptor { unsigned char hiddscp$b_length; /* Size of this descriptor in bytes */ unsigned char hiddscp$b_descriptortype; /* HID descriptor type (assigned by USB) */ unsigned short int hiddscp$w_bcdhid; /* HID Class Specification release number in binary-coded decimal */ unsigned char hiddscp$b_countrycode; /* Hardware target country */ unsigned char hiddscp$b_numdescriptors; /* Number of HID class descriptors to follow */ unsigned char hiddscp$b_reportdescriptortype; /* Report descriptor type */ unsigned char hiddscp$b_descriptorlength1; /* Total length of report descriptor */ unsigned char hiddscp$b_descriptorlength2; /* Total length of report descriptor */ char hiddscp$b_fill_0_ [7]; } HID_DESC; HID_DESC desc; /* ** Read the hid descriptor we need that to read the report data. We will ** pick the size out of the report. */ device_request.ug$b_bmrequesttype = 0X81; // Device to host, interface device_request.ug$b_brequest = 0x06; // get descriprtor device_request.ug$w_wvalue = (0X21 << 8) | 0; device_request.ug$w_windex = 0; device_request.ug$w_wlength = sizeof(HID_DESC); status = sys$qiow(0, channel, IO$_SETMODE, &iosb, 0, 0, &device_request, sizeof(DEVICE_REQUEST), UG$_DEVICE_REQUEST, pipe_handle, &desc, USB$_SHORT_XFER_OK); if (status == SS$_NORMAL) { if (iosb.status == SS$_NORMAL) { report_size = (desc.hiddscp$b_descriptorlength2 << 8) | (desc.hiddscp$b_descriptorlength1); /* ** Get the idle rate */ device_request.ug$b_bmrequesttype = 0XA1; device_request.ug$b_brequest = 0x02; // GET_IDLE device_request.ug$w_wvalue = 0; device_request.ug$w_windex = 1; device_request.ug$w_wlength = 1; status = sys$qiow(0, channel, IO$_SETMODE, &iosb, 0, 0, &device_request, sizeof(DEVICE_REQUEST), UG$_DEVICE_REQUEST, pipe_handle, &idle, 0); if (status == SS$_NORMAL) { if (iosb.status == SS$_NORMAL) { /* ** Try to set the idle rate to only interrupt when something ** changes the device is free to ignore this request */ device_request.ug$b_bmrequesttype = 0X21; device_request.ug$b_brequest = 0x0a; // SET_IDLE device_request.ug$w_wvalue = 0; device_request.ug$w_windex = 0; // rate Upper 8 bits Reporte ID lower device_request.ug$w_wlength = 0; status = sys$qiow(0, channel, IO$_SETMODE, &iosb, 0, 0, &device_request, sizeof(DEVICE_REQUEST), UG$_DEVICE_REQUEST, pipe_handle, 0, 0); if (status == SS$_NORMAL) { if (iosb.status == SS$_NORMAL) { /* ** Get the Report Descriptor */ device_request.ug$b_bmrequesttype = 0X81; // Device to host, interface device_request.ug$b_brequest = 0x06; // get descriprtor device_request.ug$w_wvalue = (0X22 << 8) | 1; // Report descriptor for input device_request.ug$w_windex = 0; device_request.ug$w_wlength = report_size; status = sys$qiow(0, channel, IO$_SETMODE, &iosb, 0, 0, &device_request, sizeof(DEVICE_REQUEST), UG$_DEVICE_REQUEST, pipe_handle, &report_desc, USB$_SHORT_XFER_OK); if (status == SS$_NORMAL) { if (iosb.status == SS$_NORMAL) { decode_report(); } else { printf("Cannot get Report Descriptor VMS status %08x USB status %08x\n", iosb.status, iosb.usb_status); status = iosb.status; } } else { printf("Cannot read Report Descriptor reason %08x\n", status); } } else { printf("Cannot set idle rate VMS status %08x USB status %08x\n", iosb.status, iosb.usb_status); status = iosb.status; } } else { printf("Could not set idle reason %08x\n", status); } } else { printf("Cannot read idle rate VMS status %08x USB status %08x\n", iosb.status, iosb.usb_status); status = iosb.status; } } else { printf("Cannot read device idle rate reason %08x\n", status); } } else { printf("Reading HID descriptor VMS status %08x USB status %08X\n", iosb.status, iosb.usb_status); status = iosb.status; } } else { printf("Cannot read HID DEscriptor reason %08x\n", 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; 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) { device->interrupt_pipe_index = pipe_index; } } else { status = iosb.status; break; } } else { break; } } return(status); } /* **+ ** 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); } /* **++ ** calibrate - Set zero point for scale. ** ** Functional description: ** ** This routine will ask if the user is ready to calibrate (set the ** zero point) for the scale. Once they answer yes it reads 10 samples ** each .1 second apart. It then averages these to get a zero point. ** ** Note the scale returns 64 bits of data but in reality the low ** 24 bits are all that are needed to cover the whole range the scale ** can cover. ** ** Calling convention: ** ** int calibrate(); ** ** Input parameters: ** ** None ** ** Output parameters: ** ** None ** ** Return value: ** ** SS$_NORMAL Worked ** SS$_XXX Failed ** ** Environment: ** ** user mode ** **-- */ int calibrate() { uint32 data; int float_t = 4; int index; int loop_count; int status; uint64 total = 0; float wait = 0.1; index = device_1.interrupt_pipe_index; status = ask_y_n("Calibrate Scale (y/n) "); if (status) { printf("\033[2J\033[1;1fSetting Zero point for scale\n"); printf("\033[2;1f\033[1;7m"); for (loop_count = 0; loop_count < SAMPLE_SIZE; loop_count++) { status = sys$qiow(0, device_1.channels[index], IO$_READVBLK, &device_1.iosb, 0, 0, &device_1.interrupt_data[0], 8, USB$_SHORT_XFER_OK, device_1.pipe_handles[index], 0, 0, 0); if ((status == SS$_NORMAL) && (device_1.iosb.status == SS$_NORMAL)) { /* ** Ignore upper 32 bits of data in fact the upper 48 never really ** change. */ sample_data[loop_count] = ((uint64)device_1.interrupt_data[4] << 24) | ((uint64)device_1.interrupt_data[5] << 16) | ((uint64)device_1.interrupt_data[6] << 8) | (uint64)device_1.interrupt_data[7]; total += sample_data[loop_count]; printf(" "); status = lib$wait(&wait, 0, &float_t); if (status != SS$_NORMAL) { printf("Cannot wait for next sample reason %08x\n", status); break; } } else { if (status == SS$_NORMAL) { printf("Calibration failed on pass %d VMS status %08x USB status %08x\n", loop_count, device_1.iosb.status, device_1.iosb.usb_status); status = device_1.iosb.status; break; } else { printf("Calibration failed on pass %d VMS status %08x \n", loop_count, status); break; } } } /* ** If things worked compute the average reading */ if (status == SS$_NORMAL) { printf("\033[0m"); zero = (uint32)((total / SAMPLE_SIZE) & 0xffffffff); printf("\033[3;1fZero point set\n"); ask_y_n("Continue Y/N "); } } printf("\033[0m"); return(status); } /* **++ ** get_weight - Spin reading scale and displaying results ** ** Functional description: ** ** Spin reading data at a regular interval and display results. ** ** ** Calling convention: ** ** int calibrate(); ** ** Input parameters: ** ** None ** ** Output parameters: ** ** None ** ** Return value: ** ** SS$_NORMAL Worked ** SS$_XXX Failed ** ** Environment: ** ** user mode ** **-- */ int get_weight() { int float_t = 4; int grams; int index; float ounces = 0; int positive = 1; int pounds = 0; int status; float total_ounces = 0; uint32 weight = 0; float wait = 0.25; index = device_1.interrupt_pipe_index; printf("\033[2J\033[1;1fWaiting for item\n"); /* ** Spin reading and displaying values */ do { status = sys$qiow(0, device_1.channels[index], IO$_READVBLK, &device_1.iosb, 0, 0, &device_1.interrupt_data[0], 8, USB$_SHORT_XFER_OK, device_1.pipe_handles[index], 0, 0, 0); if ((status == SS$_NORMAL) && (device_1.iosb.status == SS$_NORMAL)) { weight = ((uint32)device_1.interrupt_data[4] << 24) | ((uint32)device_1.interrupt_data[5] << 16) | ((uint32)device_1.interrupt_data[6] << 8) | (uint32)device_1.interrupt_data[7]; if (weight < zero) { positive = -1; } else { positive = 0; } weight = weight - zero; grams = (((int) weight) * grams_const); total_ounces = (((int) weight) * ounces_const); pounds = total_ounces / 16; if (positive = 1) { ounces = total_ounces - (pounds * 16); } else { ounces = (total_ounces + (pounds * 16)) * -1; } printf("\033[12;1f\033[1m"); printf("\033#3Weight is %4d grams %d %3.1f OZ \n", grams, pounds, ounces); printf("\033#4Weight is %4d grams %d %3.1f OZ \n", grams, pounds, ounces); printf("\033[0m"); status = lib$wait(&wait, 0, &float_t); if (status != SS$_NORMAL) { printf("Cannot wait for next sample reason %08x\n", status); break; } } else { if (status == SS$_NORMAL) { printf("Weight reading failed VMS status %08x USB status %08x\n", device_1.iosb.status, device_1.iosb.usb_status); status = device_1.iosb.status; break; } else { printf("Weight reading failed VMS status %08x \n", status); break; } } } while (1); return(status); } /* **++ ** decode_report - Decode Report Descriptor ** ** Functional description: ** ** Simple minded output of report descriptor this is by no means ** complete. It is sufficient to display what the scale returns no more. ** ** For full details of what HID report looks like you need to look ** at the HID specification. The SCALE has only short item tags so the code ** makes no attempt to deal with long item tags. The way the report works ** is there a short or long tag followed by 0 or more bytes of data. The ** short tag is encoded at follows: ** ** Bits Meaning ** 0 & 1 Length 0 = 0, 1 = 1, 2 = 2, 3 = 4 bytes of data ** 2 & 3 Item type 0 = Main, 1 = Global, 2 = Local ** 2 - 7 Encode what the item is ** ** This is glossing over a lot of details but covers the high points ** of the data format. We are not really parsing it so much as decoding it. ** What we are looking for is the report size in bits and the report count. ** That will tell us how much data we are dealing with. ** ** Calling convention: ** ** void decode_report () ** ** Input parameters: ** ** None ** ** Output parameters: ** ** None ** ** Return value: ** ** None ** ** Environment: ** ** User mode ** **-- */ void decode_report() { unsigned char byte_data; unsigned char current_tag; unsigned int longword_data; unsigned short int word_data; int end = report_size; // Last item in the list int i; int indent = 0; int item_length = 0; int next_item = 0; // Next tag to process int position = 0; // Where in Report we are typedef struct __short_item { unsigned char bsize : 2, /* 0 = 0, 1 = 1, 2 = 2, 3 = 4 bytes */ btype : 2, /* 0 = Main, 1 = Global, 2 = Local */ btag : 4; } SHORT_ITEM; SHORT_ITEM *item; printf("Item\033[65CHex Value\n"); printf("--------------------------------------------------------------------------------\n"); do { item = (SHORT_ITEM *) &report_desc[next_item]; current_tag = report_desc[next_item] & 0xfc; // Clear off length /* ** Get item size only handle short items for the time being */ switch (item->bsize) { case 0 : item_length = 0; break; case 1 : item_length = 1; byte_data = report_desc[next_item + 1]; break; case 2 : item_length = 2; word_data = (report_desc[next_item + 1] << 8) | report_desc[next_item + 2]; break; case 3 : item_length = 4; longword_data = (report_desc[next_item + 1] << 24) | (report_desc[next_item + 2] << 16) | (report_desc[next_item + 3] << 8) | report_desc[next_item + 4]; break; default : printf("Error illegal tag\n"); return; } /* ** Ouput the data we got from the device, and set cursor to where we ** it to output the description of the item */ printf("\033[69C"); printf("%02x", report_desc[position]); i = 1; while (i <= item_length) { printf(" %02x", report_desc[position + i]); i++; } printf("\015"); i = 0; while (i < indent) { printf(" "); i++; } /* ** Now that we know size of the item output it */ switch (item->btype) { case 0 : // Main Item switch (current_tag) { case 0x80 : printf("Input ("); if (item_length == 2) { if (word_data & 1) { printf("Constant,"); } else { printf("Data,"); } if (byte_data & 2) { printf(" Variable,"); } else { printf(" Array,"); } if (byte_data & 4) { printf(" Relative,"); } else { printf(" Absolute,"); } if (byte_data & 8) { printf(" Wrap,"); } else { printf(" No Wrap,"); } if (byte_data & 16) { printf(" Non Linear,"); } else { printf(" Linear,"); } /* Wrap the output */ printf("\n"); i = 0; while (i < indent) { printf(" "); i++; } if (byte_data & 32) { printf(" No Preffered,"); } else { printf(" Preffered State,"); } if (byte_data & 64) { printf(" Null State,"); } else { printf(" No Null,"); } if (byte_data & 256) { printf(" Buffered"); } else { printf(" Bit Field"); } printf(")"); } else { printf("No decoding done"); } break; case 0x90 : printf("Output ("); if (item_length == 2) { if (word_data & 1) { printf("Constant,"); } else { printf("Data,"); } if (byte_data & 2) { printf(" Variable,"); } else { printf(" Array,"); } if (byte_data & 4) { printf(" Relative,"); } else { printf(" Absolute,"); } if (byte_data & 8) { printf(" Wrap,"); } else { printf(" No Wrap,"); } if (byte_data & 16) { printf(" Non Linear,"); } else { printf(" Linear,"); } /* Wrap the output */ printf("\n"); i = 0; while (i < indent) { printf(" "); i++; } if (byte_data & 32) { printf(" No Preffered,"); } else { printf(" Preffered State,"); } if (byte_data & 64) { printf(" Null State,"); } else { printf(" No Null,"); } if (byte_data & 256) { printf(" Buffered"); } else { printf(" Bit Field"); } printf(")"); } else { printf("No decoding done"); } break; case 0xa0 : printf("Collection "); if (item_length == 1) { if (byte_data == 0) { printf("Physical"); } else if (byte_data == 1) { printf("Application"); } else if (byte_data == 2) { printf("Logical"); } else if ((byte_data >= 0x3) && (byte_data >= 0x7f)) { printf("Reserved"); } else { printf("Vendor-Defined"); } indent = 3; } else { printf("Illegal collection data"); } break; case 0xc0 : printf("\015End Collection"); indent = 0; break; default : printf("Unhandled Main item"); break; } break; case 1 : // Global Item switch (current_tag) { case 4 : /* ** Useage page is generally 2 bytes */ printf("Usage Page"); if (word_data == 0x00ff) { printf(" (reserved)"); } else { printf(" Unknown page "); } break; case 0x14 : printf("Logical Minimum "); if (item_length == 1) { printf(" (%02x)", byte_data); } else if (item_length == 2) { printf(" (%04x)", word_data); } break; case 0x24 : printf("Logical Maximum "); if (item_length == 1) { printf(" (%02x)", byte_data); } else if (item_length == 2) { printf(" (%04x)", word_data); } break; case 0x34 : printf("Physical Minimum"); if (item_length == 1) { printf(" (%02x)", byte_data); } else if (item_length == 2) { printf(" (%04x)", word_data); } break; case 0x44 : printf("Physical Maximum"); if (item_length == 1) { printf(" (%02x)", byte_data); } else if (item_length == 2) { printf(" (%04x)", word_data); } break; case 0x54 : printf("Unit Exponent"); break; case 0x64 : printf("Unit"); break; case 0x74 : printf("Report Size"); if (item_length == 1) { printf(" (%02x)", byte_data); } else if (item_length == 2) { printf(" (%04x)", word_data); } break; case 0x84 : printf("Report Id"); if (item_length == 1) { printf(" (%02x)", byte_data); } else if (item_length == 2) { printf(" (%04x)", word_data); } break; case 0x94 : printf("Report Count"); if (item_length == 1) { printf(" (%02x)", byte_data); } else if (item_length == 2) { printf(" (%04x)", word_data); } break; case 0xa4 : printf("Push"); break; case 0xb4 : printf("Pop"); break; default : // Reserveditems printf("Reserved items"); break; } break; case 2 : // Local Item switch (current_tag) { case 8 : printf("Usage"); /* ** Useage is tied to Usage page we will not do any decode ** for this one. We will call it reserved because the Usage ** page is reserved */ printf(" (reserved)"); break; default : printf("Unhandled local item"); break; } break; defaut : printf("Unknown type"); break; } /* ** Point to next item and reset cursor for output */ next_item += item_length + 1; position = next_item; printf("\n"); } while (position < end); return; }