[ mikikg @ 26.12.2019. 15:16 ] @
Pozdrav,

pravim neki desktop program za Linux u C++ koji treba da prica sa nekim USB periferijama (seriski terminali, USB->serial) i trenutno imam potrebu da simuliram to ponasanje periferija jer trenutno nemam te uredjaje, imam samo protokol kako radi i koji cu da isprogramiram u simulatoru.

Interesuje me kako da simuliram /dev/NESTO da bude istog tipa/osobina kao i ovi USB-seriski konverteri?

Trenutno koristim ovako nesto za pisanje na "pravi" device iz glavnog programa:

Code:
    // ...

    //open port
    cout << "Opening MCU on " << dev_mcu << endl;
    fd_mcu = open(dev_mcu.c_str(), O_RDWR);
    if (fd_mcu < 0) {
        std::cerr << "ERROR opening MCU on " << dev_mcu << std::endl;
    } else {
        struct termios options;
        tcgetattr(fd_mcu, &options);
        options.c_cflag = B9600 | CS8 | CLOCAL | CREAD;        //<Set baud rate
        options.c_iflag = IGNPAR;
        options.c_oflag = 0;
        options.c_lflag = 0;
        tcflush(fd_mcu, TCIFLUSH);
        tcsetattr(fd_mcu, TCSANOW, &options);
        std::cout << "Configured MCU on " << dev_mcu << " as B9600 CS8 IGNPAR"  << std::endl;
    }
    
    // ...
    
    //write to port
    if (write(fd_mcu, cmd_buff_mcu, cmd_buff_mcu_len) != cmd_buff_mcu_len) {
        std::cerr << "ERROR writing CMD to MCU device!" << std::endl;
    }


Kako mogu tu u simulatoru da se "podmetnem" a da ne menjam osnovni code-e za komunikaciju (eventulano promenim samo ime uredjaja) ?
[ mikikg @ 26.12.2019. 17:16 ] @
Mora da postoji neka caka za ovo u Linuxu, preko nodova + character device, nesto da se preusmeri tamo-vamo, loopback ... oko toga se vrti, kao da sam otvorio file descriptor na /dev/null, necu u prazno nego da ide kod "mene" u poseban program? :)

Hocu da preskocim fizicki sloj, naravno da mogu da spojim fizicki i da nakrcam usb-serial konvertera (x2) ali nemam toliko, ni konvertera ni portova a i sto bi to radio kad je sve na istom PC? :)
[ bogdan.kecman @ 26.12.2019. 17:27 ] @
zavisi sta app koristi, ako samo otvara fajl obican pipe resava problem,
ako radi cpio onda jbg moras imas seriski port :D

seriski port mozes napravis sa "socat"

npr:

socat pty,rawer,echo=0 pty,rawer,echo=0
[ bogdan.kecman @ 26.12.2019. 17:30 ] @
evo sad gledam, mozes da dodas i opciju link=/dev/ttyS2 ...

socat PTY,link=/dev/ttyS10 PTY,link=/dev/ttyS11

vidi: https://exceptionshub.com/virtual-serial-port-for-linux.html
[ mikikg @ 26.12.2019. 18:25 ] @
Izgleda da radi tako, napravi se link izmedju jednog i drugog pseudo terminala, probao sam radi sa jedne strane sa onim gore komandama, radice i sa druge strane onda tako, to moram da nacukam ...

Code:

socat -d -d pty,raw,echo=0 pty,raw,echo=0
2019/12/26 19:12:07 socat[20685] N PTY is /dev/ttys019
2019/12/26 19:12:07 socat[20685] N PTY is /dev/ttys022
2019/12/26 19:12:07 socat[20685] N starting data transfer loop with FDs [5,5] and [7,7]
[ bogdan.kecman @ 26.12.2019. 18:53 ] @
yup radi to super, nisam probao ima mnogo godina ali radi super, ti sa
druge stgrane na terminalu glumis device i kucas sta bi device slao
[ mikikg @ 27.12.2019. 03:02 ] @
Evo minimalisticki primer za simulator.

Prvo se pokrene socat i tu ispise koja dva terminala je rezervisao (u primeru /dev/ttys019 i /dev/ttys022, menja se):
Code:

socat -d -d pty,raw,echo=0 pty,raw,echo=0
2019/12/26 19:12:07 socat[20685] N PTY is /dev/ttys019
2019/12/26 19:12:07 socat[20685] N PTY is /dev/ttys022
2019/12/26 19:12:07 socat[20685] N starting data transfer loop with FDs [5,5] and [7,7]


Dakle jedan (pravi) program gadja /dev/ttys022 dok se simulator podesi na drugi /dev/ttys019 i onda dalje ide vec po protokolu ...

Code:

//
// sim_mcu.cpp
// Created on 12/27/19.
// g++ sim_mcu.cpp sim_mcu
//
#include <iostream>
#include <unistd.h>
#include <sys/termios.h>
#include <fcntl.h>

using namespace std;

int main() {

    char buf [100];
    int n = 0, cnt = 0;
    int fd_mcu;
    string dev_mcu = "/dev/ttys019";

    //-------------------------------------
    //HW I/O | USB MCU 9600,8,n,1 ---------
    //-------------------------------------
    //cout << "Opening MCU on " << dev_mcu << endl;
    fd_mcu = open(dev_mcu.c_str(), O_RDWR);
    if (fd_mcu < 0) {
        std::cerr << "ERROR opening MCU on " << dev_mcu << std::endl;
    } else {
        struct termios options;
        tcgetattr(fd_mcu, &options);
        options.c_cflag = B9600 | CS8 | CLOCAL | CREAD;        //<Set baud rate
        options.c_iflag = IGNPAR;
        options.c_oflag = 0;
        options.c_lflag = 0;
        tcflush(fd_mcu, TCIFLUSH);
        tcsetattr(fd_mcu, TCSANOW, &options);
        std::cout << "Configured MCU on " << dev_mcu << " as B9600 CS8 IGNPAR"  << std::endl;
    }

    while (cnt++ < 100) {
        n = read(fd_mcu, buf, sizeof buf); //blocking!
        cout << cnt << ": " << "got " << n << " chars in buffer: [ ";
        for (int x=0; x<n; x++) {printf("%02X ", buf[x]);}
        cout << "]" << endl;
    }
    return 0;
}
[ mikikg @ 03.02.2020. 15:49 ] @
Dodatno pitanje oko ovoga, glupo je ali ne znam kako da resim, naime hocu da spojim "socat" komandu sa samim simulatorom, sve da bude u istom programu, dakle kada pokrenem simulator da on vec otvori socat i da se sam prepodesi i da mi napise na koji to drugi port da ja prikljucim svoju aplikaciju.

Trebaju mi threadovi, malo regex-a i treba mi startovanje/stopiranje treada koji je ustvari "socat -d -d pty,raw,echo=0 pty,raw,echo=0" komanda (ta komanda je stalno aktivna - dok je aktivna radi).

Sad bez ovog resenja stalno moram da menjam portove i to na 4 mesta (imam dva posebna simulatora) sto je smor ...
[ Branimir Maksimovic @ 03.02.2020. 15:56 ] @
Zar ne mozes bolje sa starim dobrim forkom?
[ mikikg @ 03.02.2020. 16:00 ] @
Citat:
Branimir Maksimovic: Zar ne mozes bolje sa starim dobrim forkom?


Da li moze praktican primer?
Gore ima source kako je osmisljeno (to je baza, nadogradio sam je, nebitno, tako je trenutno realizovano).
[ Branimir Maksimovic @ 03.02.2020. 16:11 ] @
Pa kako si rekao treba ti socat komanda pretpostavio sam da ti treba fork/exec klasika u tom slucaju. Tu malo dodje i pipe, dup da redirektujes io i to je to. Koliko shvatam hoces da hvatas sta ti
socat vraca?
[ mikikg @ 03.02.2020. 16:20 ] @
Da, treba mi za pocetak std::cout od socat da bi izvukao koja to dva terminala je rezervisao (stalno ih menja, trazi slobodne, ne mogu to da fiksiram jer mogu da "uletim" u neki pravi aktivan terminal").

Ostalo u pozadini socat-a je manje vise jasno, preuzmeravanje portova i reade/write na FD ...
[ bogdan.kecman @ 03.02.2020. 16:24 ] @
cek ti oces trosis socat u finalnoj verziji ili ?

posto ono ugradis sors socata kod tebe u app .. i kontrolises sve
[ mikikg @ 03.02.2020. 16:28 ] @
Ma socat je tu nuzno zlo, da je moglo bez njega (jel moze uopste bez njega?) ja bih napravio ...

Jos jednom, imam neku APP koja prica sa dva USB-serial uredjaja, sve to ok i radi lepo.
Fora je u tome sto nemam kod sebe te uredjaje i zbog toga sam napravio simulator, jednostavno samo hocu da mojoj APP kazem device1=/dev/putanja1 i device2=/dev/putanja2 kako bi je preusmerio da ne ide na fizicke uredjaje nego na moj simulator.

Ako to moze bez socat tim bolje :)
[ mikikg @ 03.02.2020. 17:06 ] @
Mozda treba izmestiti problem na drugo mesto, da ne cackam socat i njegovo startovanje iz C/C++ nego u Bash-u da to sve lepo skockam, izparsujem, uzmem pid-ove (da bi mogao da ih pogasim) i bitne info prosledim kao argumente mojoj APP i simulatorima ...
[ bogdan.kecman @ 03.02.2020. 18:46 ] @
da da zaboravih detalje, pa ako se dobro secam socatu mozes da fixiras
portove das opisna imena etc etc..
[ mikikg @ 24.02.2020. 10:08 ] @
Da kompletiram primer, evo bash skipta za pokretanje aplikacije sa simulatorom (specificirana "-s" opcija) i tu se vidi logika i redosled operacija sa socat (onaj 'sleep 2' je obavezan!):

Code:


# ...

# NEED root privileges to access USB0/1 !!!
[ "$UID" -eq 0 ] || exec sudo bash "$0" "$@"

##################################################
# Gasenje simulatora
# function called by trap (ctrl + c)
exit_commands() {
    pkill sim_mcu sim_vai socat
}
trap 'exit_commands' SIGINT

# Aktivacija simulatora
if [ "$1" == "-s" ]; then
  echo "***** SIMULATOR MODE! *****"
  export INTF_DEV_VAISALA=/dev/simVAII
  export INTF_DEV_MCU=/dev/simMCUI

  echo "Starting socat process for MCU ... "
  socat pty,link=/dev/simMCUI pty,link=/dev/simMCUS &
  echo "Starting socat process for VAISALA ... "
  socat pty,link=/dev/simVAII pty,link=/dev/simVAIS &

  sleep 2

  echo "Starting sim_mcu process ..."
  ./sim_mcu -d /dev/simMCUS -n -v &
  echo "Starting sim_vai process ..."
  ./sim_vai -d /dev/simVAIS -n -P 1000.10 -V 50.10 -T 20.10 -v &
fi

# Starting application
echo
echo "***** Starting application *****"
./myapp

# Gasenje simulatora
exit_commands


[ mikikg @ 28.02.2020. 06:12 ] @
Eh, sad mi treba sve ovo isto samo obrnuto za Android, druga sprava je u pitanju, konkretno opet USB-seriski adapter ali pod QEMU, hocu da linkujem moj USB device (tj bilo koji, probao razne) i nesto nece da radi, ne vidi ga Android, nista ne reaguje u aplikaciji (probao na pravom telefonu radi app) dok se u konzoli vidi warning kada izvadim USB stik, vratim probam isto, izvadim izadnje warning, on ga skonta tacno po mom VID:PID ali App truba, i druge app koje inace rade, sto to nece da radi??? ... :(
Bilo je i problema sa permisijama pa sam uradio kextunload za te drajvere od uredjaja i onda je prosao start, pre toga sam imao:

Code:

libusb: error [darwin_claim_interface] USBInterfaceOpen: another process has device opened for exclusive access
libusb: error [darwin_claim_interface] interface not found


Code:


lsusb 
Bus 020 Device 027: ID 1eaf:0004 1eaf Maple 

sudo emulator -avd novX86 -qemu -usb -device usb-host,vendorid=0x1eaf,productid=0x0004

Started GRPC server at 127.0.0.1:8554
...
libusb: warning [darwin_release_interface] USBInterfaceClose: no connection to an IOService
libusb: warning [darwin_release_interface] USBInterfaceClose: no connection to an IOService
libusb: warning [darwin_close] USBDeviceClose: no connection to an IOService
...
libusb: warning [darwin_release_interface] USBInterfaceClose: no connection to an IOService
libusb: warning [darwin_release_interface] USBInterfaceClose: no connection to an IOService
...


[ mikikg @ 28.02.2020. 14:50 ] @
Ovo je zaglup sa samim Androidom i njegovom verzijom, sad sam proverio ovo radi samo do API28 (Android 9.0), API29 nece jer ne moze da odradi `remount`, konkretno mora da se doda nov fajl (po default je u emulatoru iskljucen USB!) `/system/etc/permissions/android.hardware.usb.host.xml`

sa ovim sadrzajem:

Code:
<permissions><feature name="android.hardware.usb.host"/></permissions>


A da bi moglo uopste da se tu dodaje mora da se imaju root privilegije, a da bi njih dobio mora da se startuje emulator sa `-writable-system`

Code:

./emulator -writable-system -avd a9 -qemu -usb -device usb-host,vendorid=0x483,productid=0x3748

adb root
adb remount # Remount filesystem in write mode
adb shell
echo '<permissions><feature name="android.hardware.usb.host"/></permissions>' > /system/etc/permissions/android.hardware.usb.host.xml


[ mikikg @ 02.03.2020. 13:53 ] @
Aahhh, (skoro) sve mi super proradilo ali jedna funkcija u Android aplikaciji mi pravi problem i ne znam sta da radim :(

Code:

UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); //pass
UsbDeviceConnection connection = mUsbManager.openDevice(device); //pass
UsbInterface intf2 = device.getInterface(2); //pass

connection.claimInterface(intf2, true); //<< fails here!


Ne prolazi mi funcija `claimInterface()`.
Dakle App vidi USB, vidi njegov descriptor i ostale stvari ali ne moze da dobije ekskluzivno pravo da koristi taj USB.

U pitanju je neki custom Audio interface, pojavi mi se tamo u listi Audio uredjaja na host-u pa sam mislio da treba da izbacim drajver i to sam uradio sa:

Code:

sudo kextunload /System/Library/Extensions/AppleUSBAudio.kext


Posle toga taj interface nije vise na spisku aktivnih Audio uredjaja (valjda bi trebalo da bude tako) ali se sve isto i dalje ponasa u emulatoru, ne prolazi `claimInterface()` ...

Probao sam sve sto sam znao, sa `sudo`, sa premestanjem portova, najcrnje je sto taj code radi na pravom Android telefonu ali u emulatoru nece!

Kako da resim ovaj problem?
[ mikikg @ 07.03.2020. 00:03 ] @
Pod QEMU rade trenutno samo USB seriski CDC/ACM uredjaji, njima moze da se pristupi i da se rade standardne I/O stvari, custom uredjaji ne rade, ali zato radi u VMWare sa Android 9 x86_64 i to radi odlicno, na primer HSope APP + STM32 BluePill koji glumi osciloskop zakacen na Signal Generator ...