1 /*
2  * Copyright 2018 Oticon A/S
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 #include "bs_pc_base_types.h"
7 #include "bs_pc_base.h"
8 #include "bs_tracing.h"
9 #include "bs_oswrap.h"
10 #include "bs_string.h"
11 #include <signal.h>
12 #include <string.h>
13 #include <dirent.h>
14 #include <sys/stat.h>
15 #include <sys/types.h>
16 #include <errno.h>
17 #include <unistd.h>
18 #include <fcntl.h>
19 #include <pwd.h>
20 #include <errno.h>
21 
22 
23 bool is_base_com_initialized = false; //Used by the BackChannel to avoid deadlocks
24 char *pb_com_path = NULL;
25 int pb_com_path_length = 0;
26 
27 /**
28  * Create a FIFO if it doesn't exist
29  *
30  * Note that it may already exist, and/or that some other program
31  * may be racing to create at the same time
32  */
pb_create_fifo_if_not_there(const char * fifo_path)33 int pb_create_fifo_if_not_there(const char *fifo_path) {
34   if ((access(fifo_path, F_OK) == -1) && (mkfifo(fifo_path, S_IRWXG | S_IRWXU) != 0) && (access(fifo_path, F_OK) == -1)) {
35     bs_trace_warning_line("Can not create %s\n", fifo_path);
36     return -1;
37   }
38   return 0;
39 }
40 
41 /*
42  * Do a quick sanity check of the simulation id. To warn users if it is likely going to create
43  * issues.
44  */
pb_check_sim_id(const char * s)45 bool pb_check_sim_id(const char *s) {
46   int i = 0;
47   while (s[i] != 0) {
48     if ((s[i] == '/')) {
49       bs_trace_warning_line("The sim_id ('%s') has '%c' in it. Please note the sim_id is used to "
50                             "create filesystem paths. Using special characters will likely result "
51                             "in undesired behavior\n", s, s[i]);
52       return true;
53     }
54     i++;
55   }
56   return false;
57 }
58 
59 /**
60  * Create the comm folder if it doesn't exist
61  * And sets its name in cb_com_path
62  * Returns
63  *   the length of the cb_com_path string if it succeeds
64  *   -1 otherwise
65  */
pb_create_com_folder(const char * s)66 int pb_create_com_folder(const char *s) {
67   char *UserName = NULL;
68   int UserNameLength = 0;
69   bool free_UserName = false;
70   struct passwd *pw;
71 
72   uid_t euid = geteuid();
73   errno = 0;
74   pw = getpwuid(euid);
75   if (pw != NULL) {
76     UserName = pw->pw_name;
77   } else if (!((errno == 0) || (errno == ENOENT) || (errno== ESRCH) || (errno== EBADF) || (errno == EPERM))) {
78     bs_trace_warning_line("Warning, completely unexpected error trying to get the user name will try to recover but will likely fail"
79         "(euid=%i, errno=%i)\n", euid, errno);
80   } else {
81     //Otherwise (pw == NULL AND any of those errors), it seems the user is not a real user, or the connection to the NIS server is down or..
82     bs_trace_info_line(2, "You seem to be running this program as a not real user(?). Will try to recover"
83             "(euid=%i, errno=%i)\n", euid, errno);
84   }
85 
86   if (UserName == NULL) {
87     //Let's hope the POSIX required LOGNAME environment variable is set
88     UserName = getenv("LOGNAME");
89   }
90   if (UserName == NULL) {
91     //It was not.. let's hope for USER..
92     UserName = getenv("USER");
93   }
94   if (UserName == NULL) {
95     bs_trace_info_line(2, "Couldn't get the user name to build the tmp path for the interprocess comm (euid=%i)"
96         "(this shouldn't have happened, could not find the environment variables LOGNAME or USER either), errno=%i."
97         "Falling back to using the numeric eid\n", euid, errno);
98     UserName = bs_calloc(sizeof(char), 32);  /* plenty of margin even for 64bits ints */
99     free_UserName = true;
100     sprintf(UserName, "%i", euid);
101   }
102 
103   UserNameLength = strlen(UserName);
104 
105   pb_com_path = (char*)bs_calloc(10 + strlen(s) + UserNameLength, sizeof(char));
106 
107   sprintf(pb_com_path, "/tmp/bs_%s", UserName);
108 
109   if (free_UserName) {
110     free(UserName);
111   }
112 
113   if (bs_createfolder(pb_com_path) != 0) {
114     free(pb_com_path);
115     pb_com_path = NULL;
116     return -1;
117   }
118   sprintf(&pb_com_path[8+UserNameLength], "/%s", s);
119   if (bs_createfolder(pb_com_path) != 0) {
120     free(pb_com_path);
121     pb_com_path = NULL;
122     return -1;
123   }
124   return 9 + UserNameLength + strlen(s);
125 }
126 
pb_send_payload(int ff,void * buf,size_t size)127 void pb_send_payload(int ff, void *buf, size_t size) {
128   if (size) {
129     if (buf==NULL) {
130       bs_trace_error_line("Null pointer!!\n");
131     }
132     write(ff, buf, size);
133   }
134 }
135 
136 //#define NO_LOCK_FILE
137 
138 #if !defined(NO_LOCK_FILE)
lock_file_fill(const char * filename,long int my_pid)139 static void lock_file_fill(const char *filename, long int my_pid){
140   FILE *file;
141 
142   file = bs_fopen(filename, "w");
143   fprintf(file, "%li\n",(long) my_pid);
144 #if defined(__linux)
145   long long unsigned int starttime = bs_get_process_start_time(my_pid);
146   fprintf(file,"%llu\n",starttime);
147 #endif
148   fclose(file);
149 }
150 #endif
151 
152 /**
153  * Check if the lock file exists
154  *  * If it doesn't create one for us
155  *  * If the lock file exists:
156  *    * If the process who created it is still running, we stop.
157  *    * If the process who created it is not running, we print a warning and risk it
158  *    * If we cannot determine who created it, we stop.
159  *
160  * Returns 0 if we consider it safe enough to continue
161  *         Something else otherwise
162  */
pb_test_and_create_lock_file(const char * filename)163 int pb_test_and_create_lock_file(const char *filename) {
164 #if !defined(NO_LOCK_FILE)
165   pid_t my_pid = getpid();
166 
167   if (access(filename, F_OK) == -1) {
168     // The file does not exist. So unless somebody is racing us, we are safe to go
169     lock_file_fill(filename, my_pid);
170     return 0;
171   }
172 
173   bs_trace_warning_line("Previous lock file found %s\n", filename);
174 
175   FILE *file;
176   bool corrupt_file = false;
177   int other_dead = 1;
178   long int his_pid;
179 #if defined(__linux)
180   long long unsigned int his_starttime;
181 #endif
182 
183   file = bs_fopen(filename, "r");
184   if ( fscanf(file, "%li\n", &his_pid) != 1 ) {
185     corrupt_file = true;
186   }
187 #if defined(__linux)
188   if ((corrupt_file == false) && (fscanf(file,"%llu\n", &his_starttime) != 1)) {
189     corrupt_file = true;
190   }
191 #endif
192 
193   fclose(file);
194 
195   if (corrupt_file) { //We are provably racing the other process, we stop
196     bs_trace_warning_line("Found previous lock owned by unknown process, we may be racing each other => aborting\n");
197     return 1;
198   }
199 
200   other_dead = kill(his_pid, 0);
201 
202   if (other_dead == 0) { //a process with the pid exists
203 #if defined(__linux)
204     /* To be sure the pid was not reused, let's check that the process start time matches */
205 
206     uint64_t other_start_time = bs_get_process_start_time(his_pid);
207     if (his_starttime == other_start_time) { //it is the same process
208       bs_trace_warning_line("Found a previous, still RUNNING process w pid %li with the same sim_id and device port which would interfere with this one, aborting\n", his_pid);
209     } else {
210       other_dead = 1;
211     }
212 #else
213     bs_trace_warning_line("Found a previous, still RUNNING process w pid %li with the same sim_id and device port which would interfere with this one, aborting\n", his_pid);
214 #endif
215   }
216 
217   if (other_dead){
218     bs_trace_warning_line("Found previous lock owned by DEAD process (pid was %li), will attempt to take over\n", his_pid);
219     lock_file_fill(filename, my_pid);
220     return 0;
221   } else {
222     return 1;
223   }
224 
225 #else
226   return 0;
227 #endif
228 }
229 
pb_remove_lock_file(char ** file_path)230 void pb_remove_lock_file(char** file_path){
231   if (*file_path) {
232     remove(*file_path);
233     free(*file_path);
234     *file_path = NULL;
235   }
236 }
237 
phy_test_and_create_lock_file(pb_phy_state_t * this,const char * phy_id)238 static int phy_test_and_create_lock_file(pb_phy_state_t *this, const char *phy_id){
239   int flen = pb_com_path_length + 20 + strlen(phy_id);
240   this->lock_path = (char*) bs_calloc(flen, sizeof(char));
241   sprintf(this->lock_path, "%s/%s.phy.lock", pb_com_path, phy_id);
242 
243   int ret = pb_test_and_create_lock_file(this->lock_path);
244   if (ret) {
245     free(this->lock_path);
246     this->lock_path = NULL;
247   }
248   return ret;
249 }
250 
pb_device_test_and_create_lock_file(pb_dev_state_t * this,const char * phy_id,unsigned int dev_nbr)251 int pb_device_test_and_create_lock_file(pb_dev_state_t *this, const char *phy_id, unsigned int dev_nbr){
252   int flen = pb_com_path_length + 20 + strlen(phy_id) + bs_number_strlen(dev_nbr);
253 
254   this->lock_path = (char*) bs_calloc(flen, sizeof(char));
255 
256   sprintf(this->lock_path, "%s/%s.d%i.lock", pb_com_path, phy_id, dev_nbr);
257   int ret = pb_test_and_create_lock_file(this->lock_path);
258   if (ret) {
259     free(this->lock_path);
260     this->lock_path = NULL;
261   }
262   return ret;
263 }
264 
265 
266 /**
267  * Initialize the communication with the devices:
268  *
269  * inputs:
270  *  this Pointer to structure where the connection status will be kept.
271  *        MUST be initialized with zeroes.
272  *  s    String identifying the simulation
273  *  p    String identifying this phy in this simulation
274  *  n    How many devices we expect during the simulation
275  *
276  * returns:
277  *   0 if ok. Any other number on error
278  */
pb_phy_initcom(pb_phy_state_t * this,const char * s,const char * p,uint n)279 int pb_phy_initcom(pb_phy_state_t *this, const char* s, const char *p, uint n) {
280 
281   signal(SIGPIPE, SIG_IGN);
282 
283   if (this->device_connected) {
284     bs_trace_warning_line("%s called twice in a simulation\n", __func__);
285     return -1;
286   }
287 
288   (void)pb_check_sim_id(s);
289 
290   pb_com_path_length = pb_create_com_folder(s);
291 
292   this->device_connected = NULL;
293   this->lock_path = NULL;
294 
295   if ( phy_test_and_create_lock_file(this, p) ) {
296     return -1;
297   }
298 
299   this->n_devices = n;
300   this->device_connected = (bool *) bs_calloc(n, sizeof(bool));
301   this->ff_path_dtp = (char **) bs_calloc(n, sizeof(char *));
302   this->ff_path_ptd = (char **) bs_calloc(n, sizeof(char *));
303   this->ff_dtp = (int *) bs_calloc(n, sizeof(int *));
304   this->ff_ptd = (int *) bs_calloc(n, sizeof(int *));
305 
306   for (int d = 0; d < this->n_devices; d++) {
307     int flen = pb_com_path_length + 30 + strlen(p) + bs_number_strlen(d);
308     this->ff_path_dtp[d] = (char *)bs_calloc(flen, sizeof(char));
309     this->ff_path_ptd[d] = (char *)bs_calloc(flen, sizeof(char));
310     sprintf(this->ff_path_dtp[d], "%s/%s.d%i.dtp", pb_com_path, p, d);
311     sprintf(this->ff_path_ptd[d], "%s/%s.d%i.ptd", pb_com_path, p, d);
312 
313     if ((pb_create_fifo_if_not_there(this->ff_path_dtp[d]) != 0)
314         || (pb_create_fifo_if_not_there(this->ff_path_ptd[d]) != 0)) {
315       pb_phy_disconnect_devices(this);
316       bs_trace_error_line("Could not create FIFOs to device %i\n", d);
317     }
318 
319     if ((this->ff_ptd[d] = open(this->ff_path_ptd[d], O_WRONLY)) == -1) {
320       this->ff_ptd[d] = 0;
321       pb_phy_disconnect_devices(this);
322       bs_trace_error_line("Opening FIFO from phy to device %i failed\n", d);
323     }
324     if ((this->ff_dtp[d] = open(this->ff_path_dtp[d], O_RDONLY)) == -1) {
325       this->ff_dtp[d] = 0;
326       pb_phy_disconnect_devices(this);
327       bs_trace_error_line("Opening FIFO from device %i to phy failed\n", d);
328     }
329 
330     this->device_connected[d] = true;
331     bs_trace_raw(9,"Connected to device %i\n", d);
332   }
333 
334   return 0;
335 }
336 
pb_phy_free_one_device(pb_phy_state_t * this,int d)337 void pb_phy_free_one_device(pb_phy_state_t *this, int d) {
338   if (this->ff_dtp[d]) {
339     close(this->ff_dtp[d]);
340     this->ff_dtp[d] = 0;
341   }
342   if (this->ff_path_dtp[d]) {
343     remove(this->ff_path_dtp[d]);
344     free(this->ff_path_dtp[d]);
345     this->ff_path_dtp[d] = NULL;
346   }
347   if (this->ff_ptd[d]) {
348     close(this->ff_ptd[d]);
349     this->ff_ptd[d] = 0;
350   }
351   if (this->ff_path_ptd[d]) {
352     remove(this->ff_path_ptd[d]);
353     free(this->ff_path_ptd[d]);
354     this->ff_path_ptd[d] = NULL;
355   }
356   this->device_connected[d] = false;
357 }
358 
359 /**
360  * Disconnect all devices we are still connected to,
361  * free all the memory we used and delete the FIFOs
362  *
363  * It is safe to call this function multiple times
364  */
pb_phy_disconnect_devices(pb_phy_state_t * this)365 void pb_phy_disconnect_devices(pb_phy_state_t *this) {
366 
367   if (this->device_connected != NULL) {
368     pc_header_t header = PB_MSG_DISCONNECT;
369     for (int d = 0; d < this->n_devices; d++) {
370       if (this->ff_ptd[d]) {
371         write(this->ff_ptd[d], &header, sizeof(header));
372       }
373       pb_phy_free_one_device(this, d);
374     }
375 
376     if (pb_com_path) {
377       rmdir(pb_com_path);
378       free(pb_com_path);
379       pb_com_path = NULL;
380     }
381 
382     if (this->device_connected) {
383       free(this->device_connected);
384       this->device_connected = NULL;
385     }
386     if (this->ff_path_dtp) {
387       free(this->ff_path_dtp);
388       this->ff_path_dtp = NULL;
389     }
390     if (this->ff_dtp) {
391       free(this->ff_dtp);
392       this->ff_dtp = NULL;
393     }
394     if (this->ff_path_ptd) {
395       free(this->ff_path_ptd);
396       this->ff_path_ptd = NULL;
397     }
398     if (this->ff_ptd) {
399       free(this->ff_ptd);
400       this->ff_ptd = NULL;
401     }
402   }
403   pb_remove_lock_file(&this->lock_path);
404 }
405 
406 /**
407  * Check if we are connected to this device (or any device)
408  * Return 1 if we are
409  */
pb_phy_is_connected_to_device(pb_phy_state_t * this,uint d)410 int pb_phy_is_connected_to_device(pb_phy_state_t *this, uint d){
411   if ((this->device_connected == NULL) || (!this->device_connected[d])) {
412     bs_trace_error_line("Programming error while trying to talk to device %i\n", d);
413     return 0;
414   }
415   return 1;
416 }
417 
418 /**
419  * Respond to the device at the end of wait
420  */
pb_phy_resp_wait(pb_phy_state_t * this,uint d)421 void pb_phy_resp_wait(pb_phy_state_t *this, uint d) {
422   if ( pb_phy_is_connected_to_device(this, d) ) {
423     pc_header_t header = PB_MSG_WAIT_END;
424     write(this->ff_ptd[d], &header, sizeof(header));
425   }
426 }
427 
428 /**
429  * Get (and return) the next request from this device
430  */
pb_phy_get_next_request(pb_phy_state_t * this,uint d)431 pc_header_t pb_phy_get_next_request(pb_phy_state_t *this, uint d) {
432   pc_header_t header = PB_MSG_DISCONNECT;
433 
434   if ( pb_phy_is_connected_to_device(this, d) ) {
435     int n = read(this->ff_dtp[d], &header, sizeof(header));
436     if (n < sizeof(header)) {
437       bs_trace_warning_line("Device %u left the party unsuspectingly.. I treat it as if it disconnected\n", d);
438     }
439 
440     if ((header == PB_MSG_DISCONNECT) || (header == PB_MSG_TERMINATE)) {
441       //if the read failed or the device really wants to disconnect
442       pb_phy_free_one_device(this, d);
443     }
444   }
445   return header;
446 }
447 
pb_phy_get_wait_s(pb_phy_state_t * this,uint d,pb_wait_t * wait_s)448 void pb_phy_get_wait_s(pb_phy_state_t *this, uint d, pb_wait_t *wait_s) {
449   if ( pb_phy_is_connected_to_device(this, d) ) {
450     read(this->ff_dtp[d], wait_s, sizeof(pb_wait_t));
451   }
452 }
453 
454 /**
455  * Initialize the communication interface with the phy
456  *
457  * inputs:
458  *  this  Pointer to structure where the connection status will be keps.
459  *         MUST be initialized with zeroes.
460  *  d     The device number this device will have in this phy
461  *  s     String identifying the simulation
462  *  p     String identifying this phy in this simulation
463  *
464  * returns:
465  *   0 if ok. Any other number on error
466  */
pb_dev_init_com(pb_dev_state_t * this,uint d,const char * s,const char * p)467 int pb_dev_init_com(pb_dev_state_t *this, uint d, const char* s, const char *p) {
468 
469   if (this->connected) {
470     bs_trace_warning_line("%s called twice in a simulation\n", __func__);
471     return -1;
472   }
473 
474   (void)pb_check_sim_id(s);
475 
476   /* In case we fail, we initialize them to "invalid" content*/
477   this->ff_path_dtp = NULL;
478   this->ff_path_ptd = NULL;
479 
480   this->ff_ptd = 0; /*0 == stdin == not one we would have used */
481   this->ff_dtp = 0;
482 
483   if ((s == NULL) || (p  == NULL)) {
484     bs_trace_error_line("The simulation and phy identification strings need to be provided\n");
485   }
486 
487   this->this_dev_nbr = d;
488   pb_com_path_length = pb_create_com_folder(s);
489 
490   if ( pb_device_test_and_create_lock_file(this, p, d) ) {
491     bs_trace_error_line("Failed to get lock\n");
492   }
493 
494   int flen = pb_com_path_length + strlen(p) + bs_number_strlen(d) + 30;
495   this->ff_path_dtp = (char *) bs_calloc(flen, sizeof(char));
496   this->ff_path_ptd = (char *) bs_calloc(flen, sizeof(char));
497   sprintf(this->ff_path_dtp, "%s/%s.d%i.dtp", pb_com_path, p, d);
498   sprintf(this->ff_path_ptd, "%s/%s.d%i.ptd", pb_com_path, p, d);
499 
500   if ((pb_create_fifo_if_not_there(this->ff_path_dtp) != 0)
501       || (pb_create_fifo_if_not_there(this->ff_path_ptd) != 0)) {
502     pb_dev_clean_up(this);
503     bs_trace_error_line("Could not create FIFOs");
504   }
505 
506   if (((this->ff_ptd = open(this->ff_path_ptd, O_RDONLY )) == -1)) {
507     this->ff_ptd = 0;
508     pb_dev_clean_up(this);
509     bs_trace_error_line("Opening FIFO from phy to device failed\n");
510   }
511   if (((this->ff_dtp = open(this->ff_path_dtp, O_WRONLY )) == -1)) {
512     this->ff_dtp = 0;
513     pb_dev_clean_up(this);
514     bs_trace_error_line("Opening FIFO from device to phy failed\n");
515   }
516 
517   this->connected = true;
518   is_base_com_initialized = true;
519   return 0;
520 }
521 
522 /**
523  * Attempt to terminate the simulation and disconnect
524  */
pb_dev_terminate(pb_dev_state_t * this)525 void pb_dev_terminate(pb_dev_state_t *this) {
526   if (this->connected) {
527     pc_header_t header = PB_MSG_TERMINATE;
528 
529     write(this->ff_dtp, &header, sizeof(header));
530     pb_dev_clean_up(this);
531   }
532 }
533 
534 /**
535  * Disconnect from the phy
536  */
pb_dev_disconnect(pb_dev_state_t * this)537 void pb_dev_disconnect(pb_dev_state_t *this) {
538   if (this->connected) {
539     pc_header_t header = PB_MSG_DISCONNECT;
540 
541     write(this->ff_dtp, &header, sizeof(header));
542     pb_dev_clean_up(this);
543   }
544 }
545 
546 /*
547  * Try to delete the FIFOs, clear memory and try to delete the directory
548  *
549  * It is safe to call this function (unnecessarily) several times
550  */
pb_dev_clean_up(pb_dev_state_t * this)551 void pb_dev_clean_up(pb_dev_state_t *this) {
552 
553   pb_remove_lock_file(&this->lock_path);
554 
555   this->connected = false; //we don't want any possible future call to libphycom to attempt to talk with the phy
556 
557   if (this->ff_path_dtp) {
558     if (this->ff_dtp) {
559       close(this->ff_dtp);
560       this->ff_dtp = 0;
561     }
562 
563     remove(this->ff_path_dtp);
564     free(this->ff_path_dtp);
565     this->ff_path_dtp = NULL;
566   }
567 
568   if (this->ff_path_ptd) {
569     if (this->ff_ptd) {
570       close(this->ff_ptd);
571       this->ff_ptd = 0;
572     }
573     remove(this->ff_path_ptd);
574     free(this->ff_path_ptd);
575     this->ff_path_ptd = NULL;
576   }
577 
578   if (pb_com_path != NULL) {
579     rmdir(pb_com_path);
580     free(pb_com_path);
581     pb_com_path = NULL;
582   }
583 }
584 
585 /**
586  * Read from a FIFO n_bytes
587  * returns -1 on failure (it can't read n_bytes, and cleans up),
588  * otherwise returns n_bytes
589  */
pb_dev_read(pb_dev_state_t * this,void * buf,size_t n_bytes)590 int pb_dev_read(pb_dev_state_t *this, void *buf, size_t n_bytes) {
591   int read_b;
592 
593   read_b = read(this->ff_ptd, buf, n_bytes);
594 
595   if (n_bytes == read_b) {
596     return read_b;
597   }
598 
599   bs_trace_warning_line(COM_FAILED_ERROR " (tried to get %i got %i bytes)\n", n_bytes, read_b);
600   pb_dev_clean_up(this);
601   return -1;
602 }
603 
604 /**
605  * Request a non blocking wait to the phy
606  * Note that eventually the caller needs to pick the wait response
607  * from the phy with cb_dev_pick_wait_resp()
608  */
pb_dev_request_wait_nonblock(pb_dev_state_t * this,pb_wait_t * wait_s)609 int pb_dev_request_wait_nonblock(pb_dev_state_t *this, pb_wait_t *wait_s) {
610   CHECK_CONNECTED(this->connected);
611   pb_send_msg(this->ff_dtp, PB_MSG_WAIT, (void *)wait_s, sizeof(pb_wait_t));
612   return 0;
613 }
614 
615 /**
616  * Block until getting a wait response from the phy
617  * If everything goes ok, the phy has just reached the
618  * requested end time and 0 is returned
619  * Otherwise, we should disconnect (-1 will be returned)
620  */
pb_dev_pick_wait_resp(pb_dev_state_t * this)621 int pb_dev_pick_wait_resp(pb_dev_state_t *this) {
622   CHECK_CONNECTED(this->connected);
623 
624   pc_header_t header = PB_MSG_DISCONNECT;
625 
626   if (pb_dev_read(this, &header, sizeof(header)) == -1) {
627     return -1;
628   }
629 
630   if (header == PB_MSG_DISCONNECT) {
631     pb_dev_clean_up(this);
632     return -1;
633   } else if (header == PB_MSG_WAIT_END) {
634     return 0;
635   } else {
636     INVALID_RESP(header);
637     return -1;
638   }
639 }
640 
641 /**
642  * Request a wait to the phy and block until receiving the response
643  * If everything goes ok 0 is returned
644  *
645  * Otherwise, we should disconnect (-1 will be returned)
646  */
pb_dev_request_wait_block(pb_dev_state_t * this,pb_wait_t * wait_s)647 int pb_dev_request_wait_block(pb_dev_state_t *this, pb_wait_t *wait_s) {
648   CHECK_CONNECTED(this->connected);
649   int ret;
650   ret = pb_dev_request_wait_nonblock(this, wait_s);
651   if (ret)
652     return ret;
653   return pb_dev_pick_wait_resp(this);
654 }
655