Search code examples
haskelllibusb

System.USB: serial port from Device


I'm using usb-1.3.0.4 / System.USB. Having scanned and selected a relevant serial USB device, how do I find the corresponding serial port (e.g. COM3 on Windows)?

Here is an example of what I'm after:

module Main (main) where

import System.USB
import System.Hardware.Serialport
import qualified Data.Vector as V
import Data.Maybe

main :: IO ()
main = do
    devicePort 0x2341 0x0043 >>= either putStrLn usePort
    where usePort p = do
              s <- openSerial p defaultSerialSettings
              putStrLn $ "opened serial port " ++ p
              closeSerial s

-- | Find port for attached USB serial device
devicePort :: VendorId -> ProductId -> IO (Either String FilePath)
devicePort vid pid = do
    ctx <- newCtx
    findDevice ctx vid pid >>= \md -> case md of
        Just dev -> fmap (maybe (Left "not a serial device") Right) $ serialPort dev
        Nothing -> return $ Left "device not found"

-- | Scan for first device with given vendor and product identifiers
findDevice :: Ctx -> VendorId -> ProductId -> IO (Maybe Device)
findDevice ctx vid pid = fmap (listToMaybe . V.toList) $ V.filterM p =<< getDevices ctx
    where p x = do
              d <- getDeviceDesc x
              return $ deviceVendorId d == vid && deviceProductId d == pid

serialPort :: Device -> IO (Maybe FilePath)
serialPort dev = undefined

What is a possible implementation of the last function?


Solution

  • You need to search for your device in the registry. Under the key HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum there are multiple keys (in most cases its USB but sometimes the drivers install it in a different subkey) which themselves contain keys in the format VID_xxxx&PID_xxxx. You have to find this key first. It will be most likely something like HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\USB\VID_xxxx&PID_xxxx.

    This key contains itself new subkeys. And these subkeys contain a key called Device Parameters which contains the needed key value pair PortName with the port something like COM3.

    Sadly I have no idea how this is done in Haskell.