Source file: /~heha/mb-iwp/Bergwerk/fba-rpi-230421.zip/usbi2c.cpp

#include "heha_print.h"	// heha::
#include <dirent.h>	// opendir()
#include <unistd.h>	// read()
#include <fcntl.h>	// open()
#include <string.h>	// memcmp()
#include <sys/ioctl.h>
#include <linux/usbdevice_fs.h>

typedef unsigned char byte;

// USB-Gerät per Filterfunktion finden.
// Die Filterfunktion bekommt die 3 Deskriptoren in einem Stück.
// Sowie das Handle um ggf. String-Deskriptoren zu holen.
int findUsbDevice(bool(*cb)(void*,int,byte*,int),void*cbd) {
 static const char buslist[]="/dev/bus/usb";
 DIR*dBus = opendir(buslist);
 if (!dBus) return -1;
 while (dirent*busent=readdir(dBus)) {
  char dname[32];
  heha::snprint(dname,sizeof dname,"%s/%s",buslist,busent->d_name);
  DIR*dDev = opendir(dname);	// dname muss ein Verzeichnis sein
  if (!dDev) continue;
  while (dirent*devent=readdir(dDev)) {
   char fname[32];
   heha::snprint(fname,sizeof fname,"%s/%s",dname,devent->d_name);
   int h = open(fname,O_RDWR); // fname muss ein lesbares Zeichengerät sein
   if (h<0) h = open(fname,O_RDONLY);
   if (h<0) continue;
   byte desc[256];
   int l = read(h,desc,sizeof desc);
   if (l>=18+9 && cb(cbd,h,desc,l)) {
    closedir(dDev);
    closedir(dBus);
    return h;	// offenes Handle liefern
   }else{
    close(h);
   }
  }
  closedir(dDev);
 }
 closedir(dBus);
 return -1;
}

struct I2c{
 virtual bool queryLong(const void*,int,void*,int,const char* =0) const =0;
 virtual ~I2c() {};
};

struct UsbI2c: public I2c{
 int h;
 int n;
 char iface;

 operator bool() const {return h>=0;}

 static bool FindCb(void*cbd,int h,byte*desc,int dlen) {
  return ((UsbI2c*)cbd)->Finder(h,desc,dlen);
 }

 bool Finder(int h,byte*desc,int dlen) {
#define W(x) (x)&0xFF,(x)>>8
 static const byte matchdesc[36]={
   18, 1, W(0x0200), 0, 0, 0, 64,	// device desc
   W(0x16C0), W(0x27D8), W(0x0200),
   1,2,3,1,
   9,2,W(18),1,1,0,0x80,10,		// config desc
   9,4,0,0,0,0xFF,0,0,0};		// iface desc
#undef W
/*
00000000  12 01 00 02 00 00 00 40  c0 16 d8 27 00 02 01 02  |.......@...'....|
00000010  03 01 09 02 12 00 01 01  00 80 0a 09 04 00 00 00  |................|
00000020  ff 00 00 00                                       |....|
*/
// Hier: Das Gerät ist per VID+PID bekannt, und deshalb müssen die Deskriptoren nicht weiter geparst werden
  return dlen==sizeof matchdesc
  && !memcmp(desc,matchdesc,sizeof matchdesc)
  && !--n;
 }

 UsbI2c():h(-1),n(1),iface(1) {
  h = findUsbDevice(FindCb,this);
  if (h>=0) {
   if (ioctl(h,USBDEVFS_CLAIMINTERFACE,iface)<0)
   heha::print("ClaimInterface(%u): Fehler %u: %s\n",iface,errno,strerror(errno));
  }
 }

 bool queryLong(const void*s,int sl,void*r,int rl,const char* =0) const override{
  if (h<0) return false;
  const unsigned to=100;
  if (sl<=2) {
   usbdevfs_ctrltransfer xfer ={
    0b11000000,	// bmRequestType (read/vendor/device)
    0xA0,	// bRequest
    __u16(0x5D|sl<<12),	//wValue = I²C-Adresse (7 oder 10 Bit) + Subadresslänge
    *(__u16*)(s),	//wIndex = die ersten beiden Bytes
    __u16(rl),	//wLength
    to,		//timeout
    r};		//data
   if (ioctl(h,USBDEVFS_CONTROL,xfer)<0) {
    heha::print("queryLong(wl<=2): %u = %s\n",errno,strerror(errno));
    return false;
   }
   return true;
  }
  if (!rl) {
   usbdevfs_ctrltransfer xfer ={
    0b01000000,	// bmRequestType (write/vendor/device)
    0xA0,	// bRequest
    0x5D,	// wValue = I²C-Adresse (7 oder 10 Bit)
    0,		// wIndex
    __u16(sl),	// wLength
    to,		// timeout
    const_cast<void*>(s)};		// data
   if (ioctl(h,USBDEVFS_CONTROL,xfer)<0) {
    heha::print("queryLong(rl=0): %u = %s\n",errno,strerror(errno));
    return false;
   }
   return true;
  }
  return false; // sollte hier nie auftreten!
 }
 unsigned getLastError() const{
  if (h<0) return 1021;
  byte b;
  usbdevfs_ctrltransfer xfer ={
   0b11000000,	// bmRequestType (read/vendor/device)
   0xA1,	// bRequest
   0,		// wValue (ungenutzt)
   0,		// wIndex (ungenutzt)
   1,		// wLength (1 Byte)
   100,		// timeout
   &b};		// data
  if (ioctl(h,USBDEVFS_CONTROL,xfer)<0) {
   heha::print("getLastError: %u = %s\n",errno,strerror(errno));
   return 1000+errno;
  }
  return b;
 }
 ~UsbI2c() override{
  if (h>=0) {
   ioctl(h,USBDEVFS_RELEASEINTERFACE,iface);
   close(h);
  }
 }
};

int main() {
 UsbI2c test;
 if (test) {
  heha::print("Gefunden!\n");
  byte buf[6];
  if (test.queryLong("\x01\x60",2,buf,sizeof buf))	// Uhrzeit holen
    for (byte b:buf) heha::print("%02X ",b);
  else heha::print("Keine Antwort, Fehler %u!\n",test.getLastError());
 }else heha::print("Nicht gefunden!\n");
}
Detected encoding: UTF-80