Recent

Author Topic: kernel 6.8 and checking serial ports - failure to match driver name  (Read 5215 times)

robert rozee

  • Full Member
  • ***
  • Posts: 197
Re: kernel 6.8 and checking serial ports - failure to match driver name
« Reply #15 on: November 27, 2024, 03:43:28 pm »
well, below is my attempt at using just the output of dmesg, validated against /dev/tty* to eliminate USB devices that may have been unplugged. it actually seems to work reasonably well. note that i generally also provide the user with an "edit" tick box that allows them to enter any port name that is not in the list.


cheers,
rob   :-)

addendum: fixed up some small defects in the below code that falsely triggered on words like "getty"

Code: Pascal  [Select][+][-]
  1. program project1;                                                              // Robert Rozee, 2024
  2.  
  3. uses SysUtils, Process, Classes;
  4.  
  5. var PortList:TStringList;
  6.      I, J, K:integer;
  7.         S, T:string;
  8.         flag:boolean;
  9.  
  10. const letters=['a'..'z', 'A'..'Z'];
  11.       numbers=['0'..'9'];
  12. begin
  13.   PortList:=TStringList.Create;                                                // create object to hold list of port names
  14.   PortList.Sorted:=true;                                                       // list always remains sorted
  15.   PortList.Duplicates:=dupIgnore;                                              // .Duplicates requires Sorted:=true
  16.  
  17. //if RunCommand('/bin/bash',['-c','dmesg | grep "tty"'], S) then               // the 'strictly correct' version
  18.   if RunCommand('bash -c dmesg | grep "tty"', S) then                          // shorter version seems to work fine!
  19.   begin
  20.     I:=pos('tty', S);                                                          // look for first occurrence of "tty"
  21.     while I<>0 do
  22.     begin
  23.       if (I>1) and (S[I-1] in letters) then delete(S, 1, I+2) else             // eg, matches "getty" -> discard up to "y"
  24.       begin
  25.         delete(S, 1, I-1);                                                     // delete everything preceding the port name
  26.  
  27.         I:=1;
  28.         while S[I] in letters do inc(I);                                       // skip over letters, such as "ttyUSB"
  29.         while S[I] in numbers do inc(I);                                       // ... now skip over any trailing numbers
  30.  
  31.         PortList.Add(copy(S, 1, I-1));                                         // add the found port name to the list of ports
  32.         delete(S, 1, I-1)                                                      // remove found port name from input string
  33.       end;
  34.       I:=pos('tty', S)                                                         // look for next occurrence of "tty"
  35.     end
  36.   end;
  37.  
  38.   if PortList.Find('tty', I) then PortList.Delete(I);                          // .Find also requires Sorted:=true
  39.   if PortList.Find('tty0', I) then PortList.Delete(I);                         // "tty" and "tty0" are not useful to us
  40.  
  41.   PortList.Sorted:=false;                                                      // we don't need this setting for any of the below code
  42.  
  43.  
  44.   for I:=PortList.Count-1 downto 0 do                                          // remove any tty devices that are not present in /dev/tty*
  45.     if not FileExists('/dev/'+PortList.Strings[I]) then PortList.Delete(I);    // these are (generally) going to be removable USB devices
  46.                                                                                // that have been unplugged
  47.  
  48.   flag:=true;
  49.   while (PortList.Count>1) and flag do                                         // excessively complicated sort routine, tries to ensure the
  50.   begin                                                                        // 'fixed' serial ports appear first, and that port numbers
  51.     flag:=false;                                                               // are ordered correctly: 0,1,2...,8,9,10,11, etc.
  52.     for I:=0 to PortList.Count-2 do
  53.     begin
  54.       S:=PortList[I];
  55.       T:=PortList[I+1];
  56.       J:=1+length(S);
  57.       K:=1+length(T);
  58.  
  59.       if (J-K)<0 then begin                                                    // pack S with zeros to left of numeric part
  60.                         while (J>1) and (S[J-1] in ['0'..'9']) do dec(J);
  61.                         while length(S)<length(T) do insert('0', S, J)
  62.                       end else
  63.       if (K-J)<0 then begin                                                    // pack T with zeros to left of numeric part
  64.                         while (K>1) and (T[K-1] in ['0'..'9']) do dec(K);
  65.                         while length(T)<length(S) do insert('0', T, K)
  66.                       end;
  67.  
  68.       J:=pos('ttyS',S);                                                        // =1 if is a 'fixed' serial port
  69.       K:=pos('ttyS',T);                                                        // =1 if is a 'fixed' serial port
  70.  
  71.       if ((J<>1) and (K=1)) or                                                 // bubble ttyS* ports to top of the list
  72.          ((J=K) and (S>T)) then                                                // within respective groups sort alphabetically
  73.       begin
  74.         PortList.Exchange(I, I+1);
  75.         flag:=true                                                             // flag set if at least one swap during this pass
  76.       end
  77.     end
  78.   end;
  79.  
  80.  
  81.   if PortList.Count=0 then writeln('no serial ports found')
  82.                       else for I:=0 to PortList.Count-1 do writeln(PortList.Strings[I]);
  83.   PortList.Free
  84. end.
  85.  

sample output:

user@user-DH61BE:~/pascal/Serial Watcher mk2$
user@user-DH61BE:~/pascal/Serial Watcher mk2$ ./project1
ttyS0
ttyS4
ttyS5
ttyACM0
ttyUSB0
ttyUSB1
user@user-DH61BE:~/pascal/Serial Watcher mk2$
« Last Edit: November 28, 2024, 02:31:12 pm by robert rozee »

robert rozee

  • Full Member
  • ***
  • Posts: 197
Re: kernel 6.8 and checking serial ports - failure to match driver name
« Reply #16 on: November 27, 2024, 03:53:25 pm »
Maybe irrelevant, but here's the function I use to find the USB ports (in my case AVR Arduino) in Linux:

Code: Pascal  [Select][+][-]
  1.    If (FindFirst('/dev/ttyUSB*', (faAnyFile And Not faDirectory), SearchResult) = 0) or
  2.       (FindFirst('/dev/ttyACM*', (faAnyFile And Not faDirectory), SearchResult) = 0) Then

hi Dzandaa,
    your approach will likely be perfectly adequate for the vast majority of folks, as these days the number of machines with fixed ttySxx ports is likely to be rapidly shrinking - and pretty much everything else 'appears' when plugged into a USB port and 'disappears' when unplugged. btw, your code should probably have two separate loops, one for /dev/ttyUSB* and another for /dev/ttyACM* as otherwise it will only look for ports of one or the other variety, but not both.


cheers,
rob   :-)

MarkMLl

  • Hero Member
  • *****
  • Posts: 8082
Re: kernel 6.8 and checking serial ports - failure to match driver name
« Reply #17 on: November 27, 2024, 03:55:38 pm »
Maybe irrelevant, but here's the function I use to find the USB ports (in my case AVR Arduino) in Linux:

The difference is that what we're doing is trying to get the kernel driver module so that we know what each port actually /is/. The ttyACM devices are particularly ratty, since than name gets assigned to /anything/ that claims to implement the USB serial class.

The key appears to be to walk the /sys/devices tree to the extent that you can find what bus something's on, then using that knowledge read a symlink at a relative location determined by the bus type. Then do a substring match (not a full match) to see if the driver is pnp/drivers/serial (for legacy devices), pci/drivers/serial and so on.

There's lots of devils in the details, but that's the gist.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

Dzandaa

  • Sr. Member
  • ****
  • Posts: 404
  • From C# to Lazarus
Re: kernel 6.8 and checking serial ports - failure to match driver name
« Reply #18 on: November 27, 2024, 04:05:18 pm »
@MarkMLI

O.K, I understand the problem.

B->
Regards,
Dzandaa

MarkMLl

  • Hero Member
  • *****
  • Posts: 8082
Re: kernel 6.8 and checking serial ports - failure to match driver name
« Reply #19 on: November 27, 2024, 04:41:27 pm »
well, below is my attempt at using just the output of dmesg, validated against /dev/tty* to eliminate USB devices that may have been unplugged. it actually seems to work reasonably well. note that i generally also provide the user with an "edit" tick box that allows them to enter any port name that is not in the list.

It's obviously your choice what you use. But apart from anything else you need to appreciate that some distreaux make dmesg a root-only operation, and the kernel buffer is of limited size which will wrap once full. You might possibly do better looking at /var/log/syslog*, which will at least be visible if you add the relevant user(s) to the adm group.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

robert rozee

  • Full Member
  • ***
  • Posts: 197
Re: kernel 6.8 and checking serial ports - failure to match driver name
« Reply #20 on: November 27, 2024, 09:31:41 pm »
some distreaux make dmesg a root-only operation, and the kernel buffer is of limited size which will wrap once full

well, that relegates dmesg to the category of a "simple solution that does not work"!

i found reference online to a useful location, /proc/tty/driver/serial, but it is only readable by root, see below. at least it shows the information exists. note: it is mostly the ttySxx ports that are causing me problems.


cheers,
rob   :-)

Code: [Select]
user@user-DH61BE:~$
user@user-DH61BE:~$ sudo cat /proc/tty/driver/serial
serinfo:1.0 driver revision:
0: uart:16550A port:000003F8 irq:4 tx:0 rx:0 RTS|DTR
1: uart:unknown port:000002F8 irq:3
2: uart:unknown port:000003E8 irq:4
3: uart:unknown port:000002E8 irq:3
4: uart:16550A port:0000E000 irq:16 tx:0 rx:0 RTS|DTR
5: uart:16550A port:0000E008 irq:16 tx:0 rx:0 RTS|DTR
6: uart:unknown port:00000000 irq:0
7: uart:unknown port:00000000 irq:0
8: uart:unknown port:00000000 irq:0
9: uart:unknown port:00000000 irq:0
10: uart:unknown port:00000000 irq:0
11: uart:unknown port:00000000 irq:0
12: uart:unknown port:00000000 irq:0
13: uart:unknown port:00000000 irq:0
14: uart:unknown port:00000000 irq:0
15: uart:unknown port:00000000 irq:0
16: uart:unknown port:00000000 irq:0
17: uart:unknown port:00000000 irq:0
18: uart:unknown port:00000000 irq:0
19: uart:unknown port:00000000 irq:0
20: uart:unknown port:00000000 irq:0
21: uart:unknown port:00000000 irq:0
22: uart:unknown port:00000000 irq:0
23: uart:unknown port:00000000 irq:0
24: uart:unknown port:00000000 irq:0
25: uart:unknown port:00000000 irq:0
26: uart:unknown port:00000000 irq:0
27: uart:unknown port:00000000 irq:0
28: uart:unknown port:00000000 irq:0
29: uart:unknown port:00000000 irq:0
30: uart:unknown port:00000000 irq:0
31: uart:unknown port:00000000 irq:0
user@user-DH61BE:~$

MarkMLl

  • Hero Member
  • *****
  • Posts: 8082
Re: kernel 6.8 and checking serial ports - failure to match driver name
« Reply #21 on: November 27, 2024, 09:49:37 pm »
i found reference online to a useful location, /proc/tty/driver/serial, but it is only readable by root, see below. at least it shows the information exists. note: it is mostly the ttySxx ports that are causing me problems.

In any event, using /proc for anything other than per-process information is generally considered an embarrassment and it's probably best not relied on.

Walking the /sys tree and using substring matches (rather than precise comparisons) is almost certainly the way to go. There's a couple of places where you have to apply a bit of magic to know where to start looking:

Code: Pascal  [Select][+][-]
  1. (* This represents a hack that I'm not entirely happy about. Having found a
  2.   serial device name in the tree such as USB0 or ACM0, it appears to be
  3.   necessary to step back by a varying amount (../.. or ../../.. in the case of
  4.   those two examples) before starting to look for attributes such as idVendor.
  5.   The layout is probably dependent on the device driver (kernel module), but
  6.   it's not possible to identify this without knowing the tree layout; making
  7.   things worse, it's not possible to simply use a deep search every time since
  8.   that might pick up spurious USB hubs.
  9. *)
  10. function prefixPath(const device: string): string;
  11.  
  12. begin
  13.   if Pos('ttyACM', device) > 0 then
  14.     result := '../../../'
  15.   else
  16.     result := '../../'
  17. end { prefixPath } ;
  18.  
  19.  
  20. (* The position of the driver symlink relative to the device name will vary
  21.   depending on the bus type. For the moment, I'm assuming that this can also
  22.   refer to the device name rather than having to refer to the cached bus type.
  23. *)
  24. function driverPath(const device: string): string;
  25.  
  26. begin
  27.   if Pos('ttyS', device) > 0 then
  28.     result := 'device/'
  29.   else
  30.     result := ''
  31. end { driverPath } ;
  32.  

I'm not sure yet whether your discovery regarding kernel 6.8 requires that the second of those needs to be modified: I suspect not since the variable number of ../s in the symlink giving you problems will be accommodated by a substring match.

I'm very much still working on this. My code is oriented more towards finding the serial device representing a specific instrument etc. rather than describing everything, but so far the changes I've made are on the periphery: the core walkDirs() function is standing up well.

More tomorrow hopefully.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

MarkMLl

  • Hero Member
  • *****
  • Posts: 8082
Re: kernel 6.8 and checking serial ports - failure to match driver name
« Reply #22 on: November 28, 2024, 11:29:31 am »
OK, I've uploaded some changes to https://github.com/MarkMLl/serialcomms . Nothing major, but includes detection of actual 16550s (on both pnp and pci busses) and dubious 8250s (on the platform bus).

I find that I'm able to open a non-present device (which in my case is a platform 8250 on ttyS2 or ttyS3), but not to write to it let alone read a loopback. I'd expect existing but exclusively-opened devices to fail at the open stage.

The "stepping back" problem looks like... a problem, as per yesterday evening's note I see that I'm having to handle that differently but am able to distinguish based on the device name (ttyACMx etc.). One way of tackling this would be to

* Starting at /sys/devices, find a named device e.g. ttyS0.

* From there step back until a particular combination of items (e.g. power) is found.

* From there always do a substring match working forwards.

The specimen units are oriented towards finding the port associated with a particular device, e.g. a multimeter. It's easy enough to get a list of all units that match a specific pattern (see the 8250 and 16550 units), I've not put significant work into getting a single list of all possible serial devices: it's superficially easy, but might get involved if one wants to retain all the info about bus type etc.

I'm not entirely happy with the overall situation, since I feel we're very much at the mercy of how udev (hence possibly systemd) behaves. I've had some success in the past detecting plug/unplug events (note to self: jds6600code.pas) where those pass an environment which gives information on bus position etc., but it's inescapable that it's necessary to build up a list of what devices are already available when a program starts.

Updated: this might be a viable alternative:

Code: Text  [Select][+][-]
  1. $ ls -l /dev/ttyS0
  2.  
  3. crw-rw---- 1 root dialout 4, 64 Nov 23 15:19 /dev/ttyS0
  4.  
  5. $ ls -l /sys/dev/char/4:64
  6.  
  7. lrwxrwxrwx 1 root root 0 Nov 23 15:20 /sys/dev/char/4:64 -> ../../devices/pnp0/00:07/tty/ttyS0
  8.  
  9. $ udevadm info -a -p /sys/devices/pnp0/00:07/tty/ttyS0 | grep DRIVERS | grep -v '""'
  10.  
  11.     DRIVERS=="serial"
  12.  

...and so on. That's working by parsing the attributes which are passed to the hotplug rules in /etc/udev/rules.d etc., which are much less likely to change than are arbitrary logged messages (dmesg, syslog, systemctl output and so on).

MarkMLl
« Last Edit: November 28, 2024, 02:02:02 pm by MarkMLl »
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

robert rozee

  • Full Member
  • ***
  • Posts: 197
Re: kernel 6.8 and checking serial ports - failure to match driver name
« Reply #23 on: November 28, 2024, 02:25:47 pm »
does the below help with your stepping-back problem? it expands symlinks while proceeding down through the tree.


cheers,
rob   :-)


Code: Pascal  [Select][+][-]
  1. program project1;
  2.  
  3. uses SysUtils, BaseUnix, StrUtils;
  4.  
  5.  
  6. function normalize(S:string):string;           // expands any symlink at end of S
  7. var T:string;
  8.     n:integer;
  9. begin
  10.   T:=fpReadLink(S);                            // try to follow a symlink (returns '' if fails)
  11.   if T='' then result:=S else                  // if T is empty, was not a symbolic link
  12.   begin
  13.     n:=0;
  14.     while pos('../', T)=1 do                   // strip off as many leading "../" from T as possible
  15.     begin
  16.       delete(T,1,3);
  17.       inc(n)                                   // keep count of number of steps
  18.     end;
  19.  
  20.     inc(n);                                    // +1 to account for old filename at end of S
  21.     while n<>0 do                              // strip off filename and directories from end of S
  22.     begin
  23.       setlength(S, length(S)-1);               // trim one character at a time
  24.       if S[length(S)]='/' then dec(n)          // count down for each "/" removed (leaving a trailing "/")
  25.     end;
  26.     result:=S+T                                // return the two trimmed strings, concatinated together
  27.   end
  28. end;
  29.  
  30.  
  31. var DeviceName, DriverName, S:string;
  32.                            SR:TSearchRec;
  33.  
  34.  
  35. begin
  36.   if FindFirst('/dev/tty*', faAnyFile , SR) = 0 then
  37.   repeat
  38.     DeviceName:=SR.Name;
  39.     if (DeviceName<>'.') and (DeviceName<>'..') then
  40.     if FileExists('/sys/class/tty/'+DeviceName+'/device/driver')  or              // this suffices with FPC prior to 3.20
  41.        DirectoryExists('/sys/class/tty/'+DeviceName+'/device/driver')  then       // from FPC 3.20 onwards we need this instead
  42.     begin
  43.       S:='/sys';
  44.       S:=normalize(S+'/class');
  45.       S:=normalize(S+'/tty');
  46.       S:=normalize(S+'/'+DeviceName);
  47.       S:=normalize(S+'/device');
  48.       S:=normalize(S+'/driver');
  49.  
  50.       DriverName:=S;
  51.       writeln(PadRight(DeviceName, 9),'-->  ', DriverName);
  52.  
  53.     end;
  54.  
  55.   until FindNext(SR) <> 0;
  56.   FindClose(SR)
  57. end.


output on my desktop:

Code: [Select]
ttyS0    -->  /sys/bus/serial-base/drivers/port
ttyS1    -->  /sys/bus/serial-base/drivers/port
ttyS2    -->  /sys/bus/serial-base/drivers/port
ttyS3    -->  /sys/bus/serial-base/drivers/port
ttyS6    -->  /sys/bus/serial-base/drivers/port
ttyS7    -->  /sys/bus/serial-base/drivers/port
ttyS8    -->  /sys/bus/serial-base/drivers/port
ttyS9    -->  /sys/bus/serial-base/drivers/port
ttyS10   -->  /sys/bus/serial-base/drivers/port
ttyS11   -->  /sys/bus/serial-base/drivers/port
ttyS12   -->  /sys/bus/serial-base/drivers/port
ttyS13   -->  /sys/bus/serial-base/drivers/port
ttyS14   -->  /sys/bus/serial-base/drivers/port
ttyS15   -->  /sys/bus/serial-base/drivers/port
ttyS16   -->  /sys/bus/serial-base/drivers/port
ttyS17   -->  /sys/bus/serial-base/drivers/port
ttyS18   -->  /sys/bus/serial-base/drivers/port
ttyS19   -->  /sys/bus/serial-base/drivers/port
ttyS20   -->  /sys/bus/serial-base/drivers/port
ttyS21   -->  /sys/bus/serial-base/drivers/port
ttyS22   -->  /sys/bus/serial-base/drivers/port
ttyS23   -->  /sys/bus/serial-base/drivers/port
ttyS24   -->  /sys/bus/serial-base/drivers/port
ttyS25   -->  /sys/bus/serial-base/drivers/port
ttyS26   -->  /sys/bus/serial-base/drivers/port
ttyS27   -->  /sys/bus/serial-base/drivers/port
ttyS28   -->  /sys/bus/serial-base/drivers/port
ttyS29   -->  /sys/bus/serial-base/drivers/port
ttyS30   -->  /sys/bus/serial-base/drivers/port
ttyS31   -->  /sys/bus/serial-base/drivers/port
ttyS4    -->  /sys/bus/serial-base/drivers/port
ttyS5    -->  /sys/bus/serial-base/drivers/port
ttyUSB1  -->  /sys/bus/usb-serial/drivers/cp210x
ttyACM0  -->  /sys/bus/usb/drivers/cdc_acm
ttyUSB0  -->  /sys/bus/usb-serial/drivers/cp210x

MarkMLl

  • Hero Member
  • *****
  • Posts: 8082
Re: kernel 6.8 and checking serial ports - failure to match driver name
« Reply #24 on: November 28, 2024, 02:41:22 pm »
Well, I've "put it to bed" for the moment but the main problem is finding out- for all related devices- whether there actually is a parent directory that contains some recognisable marker... 'power' appears to be a good candidate. So for the kernel I've got here:

Code: Text  [Select][+][-]
  1. $ find . -name ttyS2
  2.  
  3. ./platform/serial8250/tty/ttyS2
  4.  
  5. $ ls ./platform/serial8250/tty/ttyS2/../..
  6.  
  7. driver  driver_override  modalias  power  subsystem  tty  uevent
  8.  

The prefixPath() function I posted yesterday evening seems to suggest that the number of steps backwards depends on the kernel module being used as the driver, but at least having some recognisable marker means that we can potentially call it an algorithm rather than an arbitrary hack.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

vangli

  • New Member
  • *
  • Posts: 47
Re: kernel 6.8 and checking serial ports - failure to match driver name
« Reply #25 on: November 28, 2024, 03:08:54 pm »
Hi
On my ubuntu 24.04 (kernel 6.8.0-49) it seems very consistent that the directory "/dev/serial/" shows which serial ports are active. However, I have only USB types of such, and hasn't been able to test with a true RS232 serial port using the build in UART on motherboard, which is reported other places. The directory will not be there if no (USB) serial ports are connected or active (powered??) as far as I can see, or is bounded to only plug and play devices.



Regards Bent

MarkMLl

  • Hero Member
  • *****
  • Posts: 8082
Re: kernel 6.8 and checking serial ports - failure to match driver name
« Reply #26 on: November 28, 2024, 03:54:56 pm »
/dev/serial is a recent innovation, and might not be portable to other distreaux.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

robert rozee

  • Full Member
  • ***
  • Posts: 197
Re: kernel 6.8 and checking serial ports - failure to match driver name
« Reply #27 on: December 03, 2024, 12:27:12 pm »
with further digging...

in /usr/share/fpcsrc/3.2.2/rtl/linux/termio.pp at line 37 we find:

Code: Pascal  [Select][+][-]
  1. {We can implement ttyname more efficiently using proc than by including the
  2.  generic ttyname.inc file.}
  3.  
  4. function TTYName(Handle:cint):string;
  5.  
  6. { Return the name of the current tty described by handle f.
  7.   returns empty string in case of an error.}
  8.  
  9. var s:string[32];
  10.     t:string[64];
  11.  
  12. begin
  13.   ttyname:='';
  14.   if isatty(handle)=1 then
  15.     begin
  16.       str(handle,s);
  17.       t:='/proc/self/fd/'+s+#0;
  18.       ttyname[0]:=char(fpreadlink(@t[1],@ttyname[1],255));
  19.     end;
  20. end;

is this use of data from within /proc by the FPC RTL unwise?


and applying IsATTY(FD) to each of my /dev/ttySxx correctly identifies those ports that are real:

Code: Pascal  [Select][+][-]
  1. program project1;
  2.  
  3. uses SysUtils, BaseUnix, TermIO;
  4.  
  5. var DeviceName:string;
  6.             SR:TSearchRec;
  7.             FD:longint;
  8.  
  9. begin
  10.   if FindFirst('/dev/tty*', faAnyFile , SR) = 0 then
  11.   repeat
  12.     DeviceName:=SR.Name;
  13.  
  14.     if (DeviceName<>'.') and (DeviceName<>'..') then
  15.     if FileExists('/sys/class/tty/'+DeviceName+'/device/driver')  or           // this suffices with FPC prior to 3.20
  16.        DirectoryExists('/sys/class/tty/'+DeviceName+'/device/driver')  then    // from FPC 3.20 onwards we need this instead
  17.     begin
  18.       FD:=fpOpen('/dev/'+DeviceName, O_RDWR or O_NONBLOCK or O_NOCTTY);
  19.       if FD>0 then
  20.       begin
  21.         if IsATTY(FD)<>0 then writeln(TTYname(FD));
  22.         fpclose(FD)
  23.       end
  24.     end
  25.   until FindNext(SR) <> 0;
  26.   FindClose(SR)
  27. end.

Code: [Select]
user@user-DH61BE:~/pascal/Serial Watcher/Serial Watcher mk4$ ./project1
/dev/ttyS0
/dev/ttyS4
/dev/ttyS5
/dev/ttyACM0
/dev/ttyUSB1
/dev/ttyUSB0
user@user-DH61BE:~/pascal/Serial Watcher/Serial Watcher mk4$

IsATTY just uses TCGetAttr, which in turn makes a TCGETS ioctl call:

Code: Pascal  [Select][+][-]
  1. Function TCGetAttr(fd:cint;var tios:TermIOS):cint; inline;
  2. begin
  3.   TCGetAttr:=fpIOCtl(fd,TCGETS,@tios);
  4. end;
  5.  
  6. // [...]
  7.  
  8. Function IsATTY (Handle:cint):cint;
  9. {
  10.   Check if the filehandle described by 'handle' is a TTY (Terminal)
  11. }
  12. var
  13.   t : Termios;
  14. begin
  15.   if TCGetAttr(Handle,t)=0 then
  16.     IsAtty:=1
  17.   else
  18.     IsAtty:=0;
  19. end;

fyi, the above are all using Lazarus 3.6/FPC3.2.2, Linux Mint XFCE 22 x86_64, gtk2.


cheers,
rob   :-)


« Last Edit: December 03, 2024, 12:30:09 pm by robert rozee »

MarkMLl

  • Hero Member
  • *****
  • Posts: 8082
Re: kernel 6.8 and checking serial ports - failure to match driver name
« Reply #28 on: December 03, 2024, 12:39:27 pm »
is this use of data from within /proc by the FPC RTL unwise?

No, because self is a symlink to the current pid so is basically what /proc is intended for in "unix doctrine". I don't know whether /proc/self (as distinct from /proc/$pid) is portable to other than Linux, and there will obviously be variability once you're looking at the per-process data.

The problem with /proc is the extent to which it's been allowed to accumulate non-process stuff related to network status, attached devices such as SCSI (and its successors) and so on: other unixes that I've seen are far stricter about its only containing per-process stuff.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

robert rozee

  • Full Member
  • ***
  • Posts: 197
Re: kernel 6.8 and checking serial ports - failure to match driver name
« Reply #29 on: December 03, 2024, 02:03:27 pm »
can you think of any way we can perhaps make use of /proc/self/fd to get hold of the same (or similar) information held in the 'file' /proc/tty/driver/serial? it seems every path i follow ends up steering me back to the old code that worked pre-6.8 where there was the check for the driver being called "serial8250"; i'd be happy on just checking the UART is not called "unknown".

(contents of my /proc/tty/driver/serial)
Code: [Select]
serinfo:1.0 driver revision:
0: uart:16550A port:000003F8 irq:4 tx:0 rx:0
1: uart:unknown port:000002F8 irq:3
2: uart:unknown port:000003E8 irq:4
3: uart:unknown port:000002E8 irq:3
4: uart:16550A port:0000E000 irq:16 tx:0 rx:0
5: uart:16550A port:0000E008 irq:16 tx:0 rx:0
6: uart:unknown port:00000000 irq:0
7: uart:unknown port:00000000 irq:0
8: uart:unknown port:00000000 irq:0
9: uart:unknown port:00000000 irq:0
etc...

while IsATTY(FD)<>0 seems to work fine as a test, i'm still wary that it could (because of the ioctl call) introduce excessive delays when checking bluetooth devices; perhaps i should use the 'old test' for pre-6.8 kernels, and then for 6.8+ use dmesg | grep "tty" with checks for the buffer having not wrapped, with a fallback to using IsATTY().


cheers,
rob   :-)
« Last Edit: December 03, 2024, 02:05:18 pm by robert rozee »

 

TinyPortal © 2005-2018