# # GitHub Pull Request: https://github.com/pyserial/pyserial/pull/778 # From cc99655f0969486eab63c80add5911c246ded0f8 Mon Sep 17 00:00:00 2001 # From: Poul-Henning Kamp # Date: Mon, 21 Oct 2024 09:50:28 +0000 # Subject: [PATCH 1/2] Implement FreeBSD list_ports using devinfo(8) # --- serial/tools/list_ports_freebsd.py.orig 2025-09-29 15:56:08 UTC +++ serial/tools/list_ports_freebsd.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python +# +# This is a module that gathers a list of serial ports including details on +# GNU/Linux systems. +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C) 2011-2015 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause + +from __future__ import absolute_import + +import glob +import os + +import subprocess + +from serial.tools import list_ports_common + +class DevInfo(list_ports_common.ListPortInfo): + def __init__(self, line): + self.props = {} + for n, i in enumerate(line.split()): + if n == 0: + self.description = i + continue + f = i.split('=', maxsplit=1) + if len(f) == 2: + self.props[f[0]] = f[1] + else: + self.props[f[0]] = True + self.device = "/dev/cua" + self.props["ttyname"] + if "vendor" in self.props: + self.vid = int(self.props["vendor"], 16) + self.manufacturer = self.vid + if "product" in self.props: + self.pid = int(self.props["product"], 16) + if "sernum" in self.props: + self.serial_number = self.props["sernum"] + if "ugen" in self.props: + self.location = self.props["ugen"] + self.subsystem = "usb" + self.apply_usb_info() + else: + self.subsystem = "uart" + self.hwid = self.description + + def usb_description(self): + return self.props["ugen"] + +def comports(include_links=False): + x = subprocess.run(["/usr/sbin/devinfo", "-rv"], capture_output=True) + seen = set() + for line in x.stdout.decode('utf-8').split('\n'): + if "ttyname" in line: + d = DevInfo(line) + seen.add(d.device) + yield d + for fn in sorted(glob.glob("/dev/cua*[!.init][!.lock]")): + if fn not in seen: + d = DevInfo(fn[5:] + " ttyname=" + fn[8:]) + seen.add(d.device) + yield d + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# test +if __name__ == '__main__': + for info in sorted(comports()): + print("{0}: {0.subsystem}".format(info)) # From 119cfbe54dcdbd6b4ffa87e7f4cc8d9163cc10b6 Mon Sep 17 00:00:00 2001 # From: Poul-Henning Kamp # Date: Tue, 22 Oct 2024 19:08:10 +0000 # Subject: [PATCH 2/2] Use absolute path to devinfo(8) --- serial/tools/list_ports_posix.py.orig 2025-09-29 15:59:42 UTC +++ serial/tools/list_ports_posix.py @@ -50,11 +50,7 @@ elif plat[:3] == 'bsd' or plat[:7] == 'freebsd': return [list_ports_common.ListPortInfo(d) for d in devices] elif plat[:3] == 'bsd' or plat[:7] == 'freebsd': - def comports(include_links=False): - devices = glob.glob('/dev/cua*[!.init][!.lock]') - if include_links: - devices.extend(list_ports_common.list_links(devices)) - return [list_ports_common.ListPortInfo(d) for d in devices] + from serial.tools.list_ports_freebsd import comports elif plat[:6] == 'netbsd': # NetBSD def comports(include_links=False):