@@ -18,6 +18,27 @@ import {
18
18
import * as mi from './mi' ;
19
19
import { DebugProtocol } from '@vscode/debugprotocol' ;
20
20
import { spawn , ChildProcess } from 'child_process' ;
21
+ import { SerialPort , ReadlineParser } from 'serialport' ;
22
+ import { Socket } from 'net' ;
23
+
24
+ interface UARTArguments {
25
+ // Path to the serial port connected to the UART on the board.
26
+ serialPort ?: string ;
27
+ // Target TCP port on the host machine to attach socket to print UART output (defaults to 3456)
28
+ socketPort ?: string ;
29
+ // Baud Rate (in bits/s) of the serial port to be opened (defaults to 115200).
30
+ baudRate ?: number ;
31
+ // The number of bits in each character of data sent across the serial line (defaults to 8).
32
+ characterSize ?: 5 | 6 | 7 | 8 ;
33
+ // The type of parity check enabled with the transmitted data (defaults to "none" - no parity bit sent)
34
+ parity ?: 'none' | 'even' | 'odd' | 'mark' | 'space' ;
35
+ // The number of stop bits sent to allow the receiver to detect the end of characters and resynchronize with the character stream (defaults to 1).
36
+ stopBits ?: 1 | 1.5 | 2 ;
37
+ // The handshaking method used for flow control across the serial line (defaults to "none" - no handshaking)
38
+ handshakingMethod ?: 'none' | 'XON/XOFF' | 'RTS/CTS' ;
39
+ // The EOL character used to parse the UART output line-by-line.
40
+ eolCharacter ?: 'LF' | 'CRLF' ;
41
+ }
21
42
22
43
export interface TargetAttachArguments {
23
44
// Target type default is "remote"
@@ -31,6 +52,8 @@ export interface TargetAttachArguments {
31
52
port ?: string ;
32
53
// Target connect commands - if specified used in preference of type, parameters, host, target
33
54
connectCommands ?: string [ ] ;
55
+ // Settings related to displaying UART output in the debug console
56
+ uart ?: UARTArguments ;
34
57
}
35
58
36
59
export interface TargetLaunchArguments extends TargetAttachArguments {
@@ -80,6 +103,11 @@ export class GDBTargetDebugSession extends GDBDebugSession {
80
103
protected gdbserver ?: ChildProcess ;
81
104
protected killGdbServer = true ;
82
105
106
+ // Serial Port to capture UART output across the serial line
107
+ protected serialPort ?: SerialPort ;
108
+ // Socket to listen on a TCP port to capture UART output
109
+ protected socket ?: Socket ;
110
+
83
111
/**
84
112
* Define the target type here such that we can run the "disconnect"
85
113
* command when servicing the disconnect request if the target type
@@ -271,6 +299,99 @@ export class GDBTargetDebugSession extends GDBDebugSession {
271
299
} ) ;
272
300
}
273
301
302
+ protected initializeUARTConnection (
303
+ uart : UARTArguments ,
304
+ host : string | undefined
305
+ ) : void {
306
+ if ( uart . serialPort !== undefined ) {
307
+ // Set the path to the serial port
308
+ this . serialPort = new SerialPort ( {
309
+ path : uart . serialPort ,
310
+ // If the serial port path is defined, then so will the baud rate.
311
+ baudRate : uart . baudRate ?? 115200 ,
312
+ // If the serial port path is deifned, then so will the number of data bits.
313
+ dataBits : uart . characterSize ?? 8 ,
314
+ // If the serial port path is defined, then so will the number of stop bits.
315
+ stopBits : uart . stopBits ?? 1 ,
316
+ // If the serial port path is defined, then so will the parity check type.
317
+ parity : uart . parity ?? 'none' ,
318
+ // If the serial port path is defined, then so will the type of handshaking method.
319
+ rtscts : uart . handshakingMethod === 'RTS/CTS' ? true : false ,
320
+ xon : uart . handshakingMethod === 'XON/XOFF' ? true : false ,
321
+ xoff : uart . handshakingMethod === 'XON/XOFF' ? true : false ,
322
+ autoOpen : false ,
323
+ } ) ;
324
+
325
+ this . serialPort . on ( 'open' , ( ) => {
326
+ this . sendEvent (
327
+ new OutputEvent (
328
+ `listening on serial port ${ this . serialPort ?. path } ` ,
329
+ 'Serial Port'
330
+ )
331
+ ) ;
332
+ } ) ;
333
+
334
+ const SerialUartParser = new ReadlineParser ( {
335
+ delimiter : uart . eolCharacter === 'LF' ? '\n' : '\r\n' ,
336
+ encoding : 'utf8' ,
337
+ } ) ;
338
+
339
+ this . serialPort
340
+ . pipe ( SerialUartParser )
341
+ . on ( 'data' , ( line : string ) => {
342
+ this . sendEvent ( new OutputEvent ( line , 'Serial Port' ) ) ;
343
+ } ) ;
344
+
345
+ this . serialPort . on ( 'close' , ( ) => {
346
+ this . sendEvent (
347
+ new OutputEvent (
348
+ 'closing serial port connection' ,
349
+ 'Serial Port'
350
+ )
351
+ ) ;
352
+ } ) ;
353
+
354
+ this . serialPort . open ( ) ;
355
+ } else if ( uart . socketPort !== undefined ) {
356
+ this . socket = new Socket ( ) ;
357
+ this . socket . setEncoding ( 'utf-8' ) ;
358
+
359
+ const eolChar : string = uart . eolCharacter === 'LF' ? '\n' : '\r\n' ;
360
+
361
+ let tcpUartData = '' ;
362
+ this . socket . on ( 'data' , ( data : string ) => {
363
+ for ( const char of data ) {
364
+ if ( char === eolChar ) {
365
+ this . sendEvent ( new OutputEvent ( tcpUartData , 'Socket' ) ) ;
366
+ tcpUartData = '' ;
367
+ } else {
368
+ tcpUartData += char ;
369
+ }
370
+ }
371
+ } ) ;
372
+ this . socket . on ( 'close' , ( ) => {
373
+ this . sendEvent ( new OutputEvent ( tcpUartData , 'Socket' ) ) ;
374
+ this . sendEvent (
375
+ new OutputEvent ( 'closing socket connection' , 'Socket' )
376
+ ) ;
377
+ } ) ;
378
+ this . socket . connect (
379
+ // Putting a + (unary plus operator) infront of the string converts it to a number.
380
+ + uart . socketPort ,
381
+ // Default to localhost if target.host is undefined.
382
+ host ?? 'localhost' ,
383
+ ( ) => {
384
+ this . sendEvent (
385
+ new OutputEvent (
386
+ `listening on tcp port ${ uart ?. socketPort } ` ,
387
+ 'Socket'
388
+ )
389
+ ) ;
390
+ }
391
+ ) ;
392
+ }
393
+ }
394
+
274
395
protected async startGDBAndAttachToTarget (
275
396
response : DebugProtocol . AttachResponse | DebugProtocol . LaunchResponse ,
276
397
args : TargetAttachRequestArguments
@@ -338,6 +459,10 @@ export class GDBTargetDebugSession extends GDBDebugSession {
338
459
339
460
await this . gdb . sendCommands ( args . initCommands ) ;
340
461
462
+ if ( target . uart !== undefined ) {
463
+ this . initializeUARTConnection ( target . uart , target . host ) ;
464
+ }
465
+
341
466
if ( args . imageAndSymbols ) {
342
467
if ( args . imageAndSymbols . imageFileName ) {
343
468
await this . gdb . sendLoad (
@@ -380,9 +505,13 @@ export class GDBTargetDebugSession extends GDBDebugSession {
380
505
_args : DebugProtocol . DisconnectArguments
381
506
) : Promise < void > {
382
507
try {
508
+ if ( this . serialPort !== undefined && this . serialPort . isOpen )
509
+ this . serialPort . close ( ) ;
510
+
383
511
if ( this . targetType === 'remote' ) {
384
512
await this . gdb . sendCommand ( 'disconnect' ) ;
385
513
}
514
+
386
515
await this . gdb . sendGDBExit ( ) ;
387
516
if ( this . killGdbServer ) {
388
517
await this . stopGDBServer ( ) ;
0 commit comments