liblabpro
Portable C library for data collection from Vernier LabPro devices
liblabpro.c
Go to the documentation of this file.
1 /* liblabpro, a C library for using Vernier LabPro devices.
2  * Based on the original FreeLab Ruby implementation by Ben Crowell.
3  *
4  * * www.lightandmatter.com/freelab
5  * * liblabpro.sf.net
6  *
7  * Copyright (C) 2018 Matthew Trescott <matthewtrescott@gmail.com>
8  *
9  * liblabpro is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * liblabpro is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with liblabpro. If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 #include "liblabpro.h"
24 #include <libusb-1.0/libusb.h>
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <stdbool.h>
29 
30 #ifdef WIN32
31 #include <windows.h>
32 #endif
33 #ifdef __unix__
34 #include <unistd.h>
35 #endif
36 
37 void LabPro_sleep(unsigned int milliseconds) {
38 #ifdef WIN32
39  Sleep(milliseconds);
40 #endif
41 #ifdef __unix__
42  sleep(milliseconds);
43 #endif
44 }
45 
46 int LabPro_init(LabPro_Context *context) {
47  int errorcode = libusb_init(&(*context).usb_link);
48  if (errorcode != LIBUSB_SUCCESS) {
49  printf("[liblabpro FATAL] Error initializing liblabpro: %s\n", libusb_strerror(errorcode));
50  }
51 #ifdef DEBUG_MODE
52  libusb_set_debug((*context).usb_link, LIBUSB_LOG_LEVEL_DEBUG);
53 #else
54  libusb_set_debug((*context).usb_link, LIBUSB_LOG_LEVEL_WARNING);
55 #endif
56  return errorcode;
57 }
58 
59 void LabPro_exit(LabPro_Context* context) {
60  libusb_exit((*context).usb_link);
61 }
62 
64  libusb_device** usb_list;
65  LabPro_List lp_list;
66  ssize_t cnt;
67 
68  cnt = libusb_get_device_list((*context).usb_link, &usb_list);
69  if (cnt <= 0 ) {
70  lp_list.num = 0;
71  }
72  else {
73  lp_list.num = 0;
74  struct libusb_device_descriptor desc;
75 
76  for (ssize_t i = 0; i < cnt && lp_list.num < 5; ++i) { // We won't support more than 5 connected LabPros at once, that would be ridiculous
77  libusb_get_device_descriptor(usb_list[i], &desc);
78  if (desc.idVendor == 0x08f7 && desc.idProduct == 1) {
79  // Attempt to open the device
80  libusb_device_handle *dev_handle = NULL;
81  int open_err;
82  if ((open_err = libusb_open(usb_list[i], &dev_handle)) != LIBUSB_SUCCESS) {
83  printf("[liblabpro ERR] Unable to open libusb device: %s\n", libusb_strerror(open_err));
84  }
85  else {
86  // Detach kernel driver if necessary
87  if (libusb_kernel_driver_active(dev_handle, 0) == 1) {
88  int detach_error = libusb_detach_kernel_driver(dev_handle, 0);
89  if (detach_error != 0) {
90  printf("[liblabpro ERR] Unable to detach kernel driver from interface 0 of LabPro %d: %s\n", lp_list.num, libusb_strerror(detach_error));
91  libusb_close(dev_handle);
92  continue;
93  }
94  else {
95  printf("[liblabpro MSG] Successfully detached kernel driver.\n");
96  }
97  }
98 
99  /* I don't think that LabPros have more than one USB configuration.
100  * freelab doesn't manually set the configuration probably because the
101  * kernel sometimes chooses a configuration automatically.
102  */
103  int config_setting_err = libusb_set_configuration(dev_handle, 1);
104  if (config_setting_err != 0) {
105  printf("[liblabpro ERR] Unable to set configuration to 1 on LabPro %d: %s\n", lp_list.num, libusb_strerror(config_setting_err));
106  libusb_close(dev_handle);
107  continue;
108  }
109 
110  // Claim the interface in order to be able to write to endpoints.
111  int interface_claim_error;
112  if ((interface_claim_error = libusb_claim_interface(dev_handle, 1)) != 0) {
113  printf("[liblabpro ERR] Unable to claim interface 0 of LabPro %d: %s\n", lp_list.num, libusb_strerror(interface_claim_error));
114  libusb_close(dev_handle);
115  continue;
116  }
117 
118  // Find endpoints
119  struct libusb_config_descriptor* config;
120  int config_desc_err;
121  if ((config_desc_err = libusb_get_config_descriptor(usb_list[i], 0, &config)) != 0) {
122  printf("[liblabpro ERR] Unable to get descriptor of first configuration of LabPro %d: %s\n", lp_list.num, libusb_strerror(config_desc_err));
123  libusb_close(dev_handle);
124  continue;
125  }
126  struct libusb_endpoint_descriptor ep_desc;
127  unsigned char in_addr;
128  unsigned char out_addr;
129  bool in_endpt_found = false;
130  bool out_endpt_found = false;
131  for (uint8_t i = 1; i <= config->interface[0].altsetting[0].bNumEndpoints; ++i) {
132  ep_desc = config->interface[0].altsetting[0].endpoint[i];
133 
134  // Make sure we're dealing with only bulk endpoints.
135  if ((ep_desc.bmAttributes & 0b00000011) != LIBUSB_TRANSFER_TYPE_BULK)
136  {
137  printf("[liblabpro ERR] LabPro %d had unexpected non-bulk endpoint.\n", lp_list.num);
138  continue;
139  }
140 
141  /* I'm not sure whether this would fail on big-endian machines or not. The libusb docs
142  * are not clear about whether the descriptor values get converted to big-endian on
143  * big-endian systems, but I do not think they do.
144  * Additionally, the docs talk about "bits 0:3 and 4:6" but don't specify whether bit 0
145  * is the LSB or MSB. Based on freelab, I have to assume the former.
146  */
147  unsigned char address = ep_desc.bEndpointAddress & 0b00001111;
148  if ((ep_desc.bEndpointAddress & 0b10000000) == LIBUSB_ENDPOINT_IN) {
149  in_addr = address;
150  in_endpt_found = true;
151  }
152  else {
153  out_addr = address;
154  out_endpt_found = true;
155  }
156  }
157  if (!in_endpt_found && !out_endpt_found) {
158  printf("[liblabpro ERR] No bulk endpoints found for LabPro %d.\n", lp_list.num);
159  libusb_close(dev_handle);
160  continue;
161  }
162 
163 
164  LabPro *labpro = (LabPro*)malloc(sizeof(LabPro));
165  labpro->device_handle = dev_handle;
166  labpro->is_open = true;
167  labpro->in_endpt_addr = in_addr;
168  labpro->out_endpt_addr = out_addr;
169  labpro->timeout = 5000; // This is what freelab uses
170  ++lp_list.num;
171  lp_list.labpros[i] = labpro;
172  }
173  }
174  }
175  }
176  libusb_free_device_list(usb_list, 1);
177  return lp_list;
178 }
179 
181  libusb_release_interface(labpro->device_handle, 0);
182  libusb_close(labpro->device_handle);
183  labpro->is_open = false;
184 }
185 
186 int LabPro_reset(LabPro* labpro, bool force) {
187  if (!labpro->is_open)
188  return LABPRO_ERR_NOT_OPEN;
189 
190  if (labpro->is_busy && !force)
191  return LABPRO_ERR_BUSY;
192 
193  if (labpro->is_collecting_data && !force)
195 
196  unsigned char *cmd_str = (unsigned char *)malloc(6);
197  snprintf((char*)cmd_str, 6, "s{%d}\r", LABPRO_RESET);
198  int transferred;
199 
200 
201  return LabPro_send_raw(labpro, (char*)cmd_str, &transferred);
202 }
203 
204 int LabPro_check_data_session(LabPro_Data_Session* session, int** errors) {
205  int count = 0;
206  int *found_errors = (int *)calloc(5, sizeof(int));
207 
208  if ((session->analog_op != 0 && session->channel > 4) || (session->sonic_op != 0 && session->channel <= 4)) {
209  found_errors[count] = LABPRO_ERR_OP_MISMATCH;
210  count++;
211  }
212  if (session->postproc != 0 && session->sampling_mode == LABPRO_SAMPMODE_REALTIME) {
213  found_errors[count] = LABPRO_ERR_POSTPROC_ON_REALTIME;
214  count++;
215  }
216  if (session->postproc != 0 && session->channel > 4) {
217  found_errors[count] = LABPRO_ERR_POSTPROC_ON_SONIC;
218  count++;
219  }
220 
221  *errors = found_errors;
222  return count;
223 }
224 
225 int LabPro_send_raw(LabPro* labpro, char* command, int* length_transferred) {
226  if (!labpro->is_open)
227  return LABPRO_ERR_NOT_OPEN;
228 
229  if ((command = realloc(command, strlen(command) + 2)) == NULL)
230  return LABPRO_ERR_NO_MEM;
231 
232  char* real_command = strcat(command, "\r");
233  *length_transferred = 0;
234  int len = strlen(real_command);
235  int numbytes; // How many bytes to transfer
236  int transferred; // Number of bytes actually transferred, as reported by libusb
237  int status; // Return value from libusb_bulk_transfer()
238  int numerrors = 0; // How many times libusb has returned an error
239  int numpackets = len / 64;
240  if (len % 64 > 0)
241  ++numpackets;
242 
243  for (int i = 1; i <= numpackets; ++i) {
244  LabPro_sleep(50);
245 
246  if (i == numpackets && len % 64 != 0)
247  numbytes = len % 64;
248  else
249  numbytes = 64;
250 
251  status = libusb_bulk_transfer(
252  labpro->device_handle,
253  labpro->out_endpt_addr,
254  (unsigned char *)real_command + (64 * (i - 1)),
255  numbytes,
256  &transferred,
257  labpro->timeout
258  );
259  *length_transferred += transferred;
260 
261  if (status == LIBUSB_ERROR_NO_DEVICE) {
263  return LIBUSB_ERROR_NO_DEVICE;
264  }
265 
266  if (status != LIBUSB_SUCCESS) {
267  ++numerrors;
268  printf("[liblabpro ERR] Error writing to USB: %s", libusb_strerror(status));
269  printf("[liblabpro WARN] There have been %d errors for this write function so far.", numerrors);
270 
271  if (numerrors > 5) {
272  printf("[liblabpro ERR] LabPro_send_raw: Error limit reached; aborting.");
273  return status;
274  }
275  }
276  }
277 
278  return LABPRO_OK;
279 }
280 
281 int LabPro_read_raw(LabPro* labpro, char** string, int* length) {
282  if (!labpro->is_open)
283  return LABPRO_ERR_NOT_OPEN;
284 
285  int retval = LABPRO_OK;
286  int status; // The status code libusb_bulk_transfer returns
287  int transferred; // Number of bytes transferred. This should always be either 0 or 64
288  int numpackets = 1; // Number of packets we've transferred so far.
289  *length = 0;
290  int numerrors = 0; // Number of times libusb has returned an error
291  unsigned char* data = malloc(64 * sizeof(char)); // The data buffer
292  if (data == NULL)
293  return LABPRO_ERR_NO_MEM;
294 
295  do {
296  LabPro_sleep(50);
297  status = libusb_bulk_transfer(
298  labpro->device_handle,
299  labpro->in_endpt_addr,
300  data + (64 * (numpackets - 1)), // Write into the proper offset
301  64,
302  &transferred,
303  labpro->timeout
304  );
305 
306  if (status == LIBUSB_ERROR_NO_DEVICE) {
308  memset(data + (64 * numpackets), 0, 64); // NULL-terminate the string since we allocated this memory and didn't fill it.
309  retval = LIBUSB_ERROR_NO_DEVICE;
310  break;
311  }
312 
313  if (status != LIBUSB_SUCCESS && status != LIBUSB_ERROR_TIMEOUT) {
314  ++numerrors; // We do not need to check transferred because the transfer is atomic in this case. https://sourceforge.net/p/libusb/mailman/message/36289834/
315  printf("[liblabpro ERR] Error reading from USB: %s", libusb_strerror(status));
316  printf("[liblabpro WARN] There have been %d errors for this read function call so far.", numerrors);
317 
318  if (numerrors > 5) {
319  printf("[liblabpro ERR] LabPro_read_raw: error limit reached; aborting.");
320  memset(data + (64 * numpackets), 0, 64); // NULL-terminate the string since we allocated this memory and didn't fill it.
321  retval = status;
322  break;
323  }
324  continue;
325  }
326  else if (status == LIBUSB_ERROR_TIMEOUT) { // There is no more data to read; so we can return
327  memset(data + (64 * numpackets), 0, 64); // NULL-terminate the string since we allocated this memory and didn't fill it.
328  break;
329  }
330 
331  // This only gets executed on successful reads
332  *length += transferred;
333  ++numpackets;
334 
335  unsigned char* previous_data = data;
336  data = realloc(data, 64 * numpackets); // Allocate memory for the next read.
337  if (data == NULL) {
338  data = previous_data;
339  retval = LABPRO_ERR_NO_MEM;
340  break;
341  }
342 
343  } while (transferred == 64 && status == LIBUSB_SUCCESS);
344 
345  *string = (char*)data;
346 
347  return retval;
348 }
349 
350 int LabPro_trim_response(char* string) {
351  char* cr = strstr(string, "\r");
352  if (cr == NULL)
353  return 1; // Not found
354  else
355  *cr = '\0';
356  return 0;
357 }
358 
360  printf("[liblabpro WARN] LabPro_handle_device_disconnect(): stub\n");
361  return;
362 }
unsigned int timeout
How long libusb waits before timing out on a transfer. Default is 5000.
Definition: liblabpro.h:350
unsigned short num
Number of available LabPros. Max is 5.
Definition: liblabpro.h:367
void LabPro_exit(LabPro_Context *context)
De-initialize liblabpro.
Definition: liblabpro.c:59
int LabPro_init(LabPro_Context *context)
Initialize liblabpro. Currently just a wrapper around libusb_init().
Definition: liblabpro.c:46
bool is_collecting_data
Whether data is currently being collected. This doesn&#39;t determine whether we should be sending comman...
Definition: liblabpro.h:341
libusb_device_handle * device_handle
Definition: liblabpro.h:318
enum LabPro_Sonic_Chan_Operations sonic_op
The type of data to collect (sonic channels). I do NOT recommend using anything but LABPRO_DISTANCE_A...
Definition: liblabpro.h:411
The underlying libusb_device is not open.
Definition: liblabpro.h:256
Struct acting as an array of LabPros.
Definition: liblabpro.h:365
Clear RAM and reset the LabPro.
Definition: liblabpro.h:61
void LabPro_close_labpro(LabPro *labpro)
Close the LabPro.
Definition: liblabpro.c:180
Have the LabPro send datapoints as they&#39;re collected without storing them.
Definition: liblabpro.h:223
LabPro_List LabPro_list_labpros(LabPro_Context *context)
Obtain a list connected LabPro devices.
Definition: liblabpro.c:63
bool is_busy
Whether there is a pending transfer request. Some commands do not return data so it&#39;s OK to send them...
Definition: liblabpro.h:328
enum LabPro_Channels channel
The channel to use.
Definition: liblabpro.h:391
The post-processing was set to a nonzero value on a realtime capture. This is not allowed...
Definition: liblabpro.h:289
enum LabPro_Analog_PostProc postproc
Post processing LabPro can perform the first and second derivatives with respect to time for you...
Definition: liblabpro.h:423
The LabPro is transferring data, so we cannot send a command that requires a response from the LabPro...
Definition: liblabpro.h:261
int LabPro_trim_response(char *string)
Trim trailing junk that the LabPro sent Since the LabPro always returns data in multiples of 64 bytes...
Definition: liblabpro.c:350
int LabPro_reset(LabPro *labpro, bool force)
Send a reset command to the specified LabPro.
Definition: liblabpro.c:186
liblabpro couldn&#39;t allocate memory.
Definition: liblabpro.h:253
Struct representing a LabPro device.
Definition: liblabpro.h:317
int LabPro_send_raw(LabPro *labpro, char *command, int *length_transferred)
Send a raw command to the LabPro.
Definition: liblabpro.c:225
enum LabPro_Analog_Chan_Operations analog_op
The type of data to collect (analog channels). For most analog sensors you should use LABPRO_CHANOP_A...
Definition: liblabpro.h:397
Thin wrapper around libusb_context.
Definition: liblabpro.h:310
void LabPro_sleep(unsigned int milliseconds)
Definition: liblabpro.c:37
unsigned char out_endpt_addr
The USB "out" endpoint address.
Definition: liblabpro.h:347
int LabPro_check_data_session(LabPro_Data_Session *session, int **errors)
Check a data session for problems before running it on the LabPro.
Definition: liblabpro.c:204
bool is_open
Whether the underlying USB device handle is open.
Definition: liblabpro.h:321
Struct representing a "data session" Data sessions are an abstraction over the LabPro&#39;s command-orien...
Definition: liblabpro.h:389
enum LabPro_Sampling_Modes sampling_mode
Sampling mode: realtime or non-realtime. The names are a bit misleading, because LabPro itself will a...
Definition: liblabpro.h:442
void LabPro_handle_device_disconnect(LabPro *labpro)
Definition: liblabpro.c:359
LabPro * labpros[5]
Array of LabPro.
Definition: liblabpro.h:370
unsigned char in_endpt_addr
The USB "in" endpoint address.
Definition: liblabpro.h:344
The post-processing was set to a nonzero value for a sonic data capture. For sonic data like from mot...
Definition: liblabpro.h:284
int LabPro_read_raw(LabPro *labpro, char **string, int *length)
Read raw bytes from the LabPro.
Definition: liblabpro.c:281
OK status.
Definition: liblabpro.h:250