wtarreau / bootterm

The terminal written for its users by its users
MIT License
215 stars 11 forks source link

macOS support #4

Closed JaCzekanski closed 3 years ago

JaCzekanski commented 3 years ago

(continuation of #2)

Regarding auto-detection, of course /sys/class/* will not work on macos. One discovery method per OS is needed. I have already identified the use of sysctl() for freebsd. Could you please issue sysctl -a|grep ^dev on macos to see if it shares anything with freebsd just in case ?

jakub@jakub-mbp :: 00:23:32 :: ~ 
$ sysctl -a|grep ^dev
jakub@jakub-mbp :: 00:23:39 :: ~ 
$

Sadly, no :(

macOS doesn't have "Serial" hardware type in System information. Only USB devices can be listed:

$ system_profiler SPUSBDataType
USB:

    USB 3.0 Bus:

      Host Controller Driver: AppleUSBXHCILPTH
      PCI Device ID: 0x8c31 
      PCI Revision ID: 0x0005 
      PCI Vendor ID: 0x8086 

(...)

        CP2102 USB to UART Bridge Controller:

          Product ID: 0xea60
          Vendor ID: 0x10c4  (Silicon Laboratories, Inc.)
          Version: 1.00
          Serial Number: 0001
          Speed: Up to 12 Mb/s
          Manufacturer: Silicon Labs
          Location ID: 0x14200000 / 6
          Current Available (mA): 500
          Current Required (mA): 100
          Extra Operating Current (mA): 0

but that still doesn't give us any direct info about Serial without doing VID/PID lookups.

Diff of /dev before and after connecting CP2102.

$ diff noserial.txt serial.txt 
268a269,270
> cu.SLAB_USBtoUART
> cu.usbserial-0001
462a465,466
> tty.SLAB_USBtoUART
> tty.usbserial-0001

(Weird that it is visible as two separate devices, maybe it's something related to my system setup/drivers) [edit] That was a driver issue - I had installed both Apple and Silabs kexts, uninstalling the later fixed the issue.

and here's for the FTDI

$ diff noserial.txt ftdi.txt 
268a269
> cu.usbserial-A9APTBNR
463a465
> tty.usbserial-A9APTBNR

Looks like the Arduino IDE went the easy way and just lists all ^cu\. devices.

Screenshot 2020-12-16 at 00 41 51
JaCzekanski commented 3 years ago

Scanning /dev works pretty good :)

int scan_ports()
{
    struct dirent *ent;
    char ftmp[PATH_MAX];
    struct stat st;
    DIR *dir = NULL;
    char *link, *driver, *desc, *name;

    nbports = 0;

#ifdef __APPLE__
    dir = opendir("/dev");
    if (!dir)
        goto end;

    while (nbports < MAXPORTS) {
        ent = readdir(dir);
        if (!ent)
            break;

        if (in_list(exclude_list, ent->d_name))
            continue;

        if (restrict_list && !in_list(restrict_list, ent->d_name))
            continue;

        if (strstr(ent->d_name, "cu.") != ent->d_name)
            continue;

        snprintf(ftmp, sizeof(ftmp), "/dev/%s", ent->d_name);
        if (stat(ftmp, &st) == 0) {
            serial_ports[nbports].name = strdup(ent->d_name);
            serial_ports[nbports].driver = NULL;
            serial_ports[nbports].desc = NULL;
            serial_ports[nbports].ctime = st.st_ctime;
            nbports++;
            continue;
        }
    }
#else
$ ./bin/bt -n
2 ports found, waiting for a new one...
 port |  age (sec) | device     | driver           | description          
------+------------+------------+------------------+----------------------
    2 |          0 | cu.usbserial-0001 |                  |                  

Trying port cu.usbserial-0001... Connected to cu.usbserial-0001 at 115200 bps.
Escape character is 'Ctrl-]'. Use escape followed by '?' for help.
Hello world!

BootTerm supports several single-character commands after the escape character:
  H h ?      display this help
  Q q .      quit
  P p        show port status
  D d        flip DTR pin
  R r        flip RTS pin
  F f        flip both DTR and RTS pins
  B b        send break
  C c        enable / disable capture
Enter the escape character again after this menu to use these commands.

Port name: cu.usbserial-0001    Speed: 115200 bps    Pins: dtr RTS cts dsr cd ri

Port name: cu.usbserial-0001    Speed: 115200 bps    Pins: dtr rts cts dsr cd ri

Port name: cu.usbserial-0001    Speed: 115200 bps    Pins: DTR RTS cts dsr cd ri
Sent break

Not sure whether you're okay with #ifdefing parts of the code or would you rather have separate functions (scan_ports_osx, scan_ports_unix, etc) or even implementations in a separate .c file (selected in Makefile)

wtarreau commented 3 years ago

Perfect! I was indeed thinking about using ifdefs for the scan, as I'm having some experimental code working on freebsd that will also have to use ifdefs. Thus my idea was to have an ifdef linux for the current version, an ifdef freebsd for the second one once done, and an else using a generic scan of /dev/. Then as we improve our knowledge of certain OSes, we can improve the situation for them while still keeping the fallback like you did above. We could then even add other ifdefs around the test for the device name.

However, please put the ifdefs around the full function. It will be easier regarding the local variables and generally more readable.

For macOS I found some resources here but I don't have such a machine and the discovery process looks fairly complex: https://developer.apple.com/documentation/iokit/1514494-ioservicegetmatchingservices?language=objc http://mirror.informatimago.com/next/developer.apple.com/documentation/DeviceDrivers/Conceptual/AccessingHardware/AH_Finding_Devices/chapter_4_section_2.html

Thus I think that macOS will stay on /dev for a while :-)

Thanks!

JaCzekanski commented 3 years ago

For macOS I found some resources here but I don't have such a machine and the discovery process looks fairly complex: developer.apple.com/documentation/iokit/1514494-ioservicegetmatchingservices?language=objc http://mirror.informatimago.com/next/developer.apple.com/documentation/DeviceDrivers/Conceptual/AccessingHardware/AH_Finding_Devices/chapter_4_section_2.html

Screenshot 2020-12-16 at 11 02 31 This might work, but I doubt it's worth the effort - macOS APIs often require you to write Obj-C (not sure if it also applies to kernel APIs) and in this case, we don't get any more information besides what's available by scanning /dev.

there's also a cmd line tool that can probe device information:

$ ioreg -l -r -c IOSerialBSDClient
+-o IOSerialBSDClient  <class IOSerialBSDClient, id 0x10003b90a, registered, matched, active, busy 0 (0 ms), retain 5>
    {
      "IOClass" = "IOSerialBSDClient"
      "CFBundleIdentifier" = "com.apple.iokit.IOSerialFamily"
      "IOProviderClass" = "IOSerialStreamSync"
      "IOTTYBaseName" = "usbserial-"
      "IOSerialBSDClientType" = "IOSerialStream"
      "IOProbeScore" = 1000
      "IOResourceMatch" = "IOBSD"
      "IOCalloutDevice" = "/dev/cu.usbserial-0001"
      "IOMatchCategory" = "IODefaultMatchCategory"
      "IOTTYDevice" = "usbserial-0001"
      "IODialinDevice" = "/dev/tty.usbserial-0001"
      "CFBundleIdentifierKernel" = "com.apple.iokit.IOSerialFamily"
      "IOTTYSuffix" = "0001"
    }

+-o IOSerialBSDClient  <class IOSerialBSDClient, id 0x1000004e7, registered, matched, active, busy 0 (0 ms), retain 5>
    {
      "IOClass" = "IOSerialBSDClient"
      "CFBundleIdentifier" = "com.apple.iokit.IOSerialFamily"
      "IOProviderClass" = "IOSerialStreamSync"
      "IOTTYBaseName" = "Bluetooth-Incoming-Port"
      "IOSerialBSDClientType" = "IORS232SerialStream"
      "IOProbeScore" = 1000
      "IOResourceMatch" = "IOBSD"
      "IOCalloutDevice" = "/dev/cu.Bluetooth-Incoming-Port"
      "IOMatchCategory" = "IODefaultMatchCategory"
      "IOTTYDevice" = "Bluetooth-Incoming-Port"
      "IODialinDevice" = "/dev/tty.Bluetooth-Incoming-Port"
      "CFBundleIdentifierKernel" = "com.apple.iokit.IOSerialFamily"
      "IOTTYSuffix" = ""
    }

+-o IOSerialBSDClient  <class IOSerialBSDClient, id 0x1000004ea, registered, matched, active, busy 0 (0 ms), retain 5>
    {
      "IOClass" = "IOSerialBSDClient"
      "CFBundleIdentifier" = "com.apple.iokit.IOSerialFamily"
      "IOProviderClass" = "IOSerialStreamSync"
      "IOTTYBaseName" = "Khronos-SPPDev"
      "IOSerialBSDClientType" = "IORS232SerialStream"
      "IOProbeScore" = 1000
      "IOResourceMatch" = "IOBSD"
      "IOCalloutDevice" = "/dev/cu.Khronos-SPPDev"
      "IOMatchCategory" = "IODefaultMatchCategory"
      "IOTTYDevice" = "Khronos-SPPDev"
      "IODialinDevice" = "/dev/tty.Khronos-SPPDev"
      "CFBundleIdentifierKernel" = "com.apple.iokit.IOSerialFamily"
      "IOTTYSuffix" = ""
    }

Perfect! I was indeed thinking about using ifdefs for the scan, as I'm having some experimental code working on freebsd that will also have to use ifdefs.

Do you want me to include fixes in the code and submit them as a Pull request?

wtarreau commented 3 years ago

Thanks for checking. The interesting part there would be the base name but it's already used in the tty device name so it's explicit enough I think.

Feel free to send your proposed changes!

wtarreau commented 3 years ago

By the way, I suggest that you place the whole function in a #else block and that only the tests on "cu." is tested for macos. Ah and one thing, please use strncmp() instead of strstr(), it's a bit cleaner :-)

wtarreau commented 3 years ago

Merged your patches, closing now. Feel free to reopen if I broke anything!