aboutsummaryrefslogtreecommitdiffstats
path: root/src/main.rs
blob: 01b0f7134822ac651ea0c78c5f9a661ff79889dd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// Copyright 2018 Matthias P. Braendli
// SPDX-License-Identifier: GPL-2.0-only
extern crate libc;

use std::fs::{OpenOptions, File};
use std::ffi::{CStr, CString};
use std::os::unix::io::AsRawFd;
use std::thread;
use std::net::TcpStream;
use std::str::FromStr;
use std::io::{Read, Write};

extern {
    fn kissattach(
        callsign: * const libc::c_char,
        speed: libc::int32_t,
        mtu: libc::int32_t,
        kttyname: * const libc::c_char,
        allow_broadcast: libc::int32_t) -> libc::int32_t;
}

fn create_pts_pair() -> std::io::Result<File> {
    let master_file = OpenOptions::new()
        .read(true)
        .write(true)
        .open("/dev/ptmx")?;

    unsafe {
        let master_fd = master_file.as_raw_fd();
        if libc::grantpt(master_fd) == -1 {
            return Err(std::io::Error::last_os_error());
        }
        if libc::unlockpt(master_fd) == -1 {
            return Err(std::io::Error::last_os_error());
        }
    }

    Ok(master_file)
}

fn usage() {
    eprintln!("fldigi-kissattach");
    eprintln!(" Usage:");
    eprintln!(" fldigi-kissattach CALLSIGN-SSID FLDIGI_IP:FLDIGI_PORT MTU");
    eprintln!("");
    eprintln!(" Example:");
    eprintln!(" fldigi-kissattach HB9EGM-1 127.0.0.1:7342 120");

    std::process::exit(1);
}

fn main() {
    let args : Vec<String> = std::env::args().collect();
    if args.len() != 4 {
        usage();
    }

    let callsign = args[1].clone();
    let fldigi_connect = args[2].clone();
    let mtu = i32::from_str(&args[3]).unwrap();
    eprintln!("Creating PTY pair");

    let mut master_file = match create_pts_pair() {
        Ok(fd) => fd,
        Err(e) => panic!("create_pts_pair failed: {}", e)
    };

    eprintln!("PTS master: {:?}", master_file);

    let slavename;
    unsafe {
        slavename = libc::ptsname(master_file.as_raw_fd());
    }

    if slavename.is_null() {
        panic!("Cannot get PTS slave name");
    }
    unsafe {
        let slice = CStr::from_ptr(slavename);
        eprintln!("PTS slave: {:?}", slice);
    }


    let callsign = CString::new(callsign).expect("Failed to convert callsign to CString");
    let speed : i32 = 9600;
    let allow_broadcast : i32 = 1;

    let success = unsafe {
        kissattach(
            callsign.as_ptr(),
            speed,
            mtu,
            slavename,
            allow_broadcast)
    };

    if success == 0 {
        panic!("kissattach failed");
    }


    let mut radio = TcpStream::connect(fldigi_connect).unwrap();

    let mut pty_tx_side = master_file.try_clone()
        .expect("Cannot clone PTY file");

    let mut radio_tx = radio.try_clone().unwrap();

    let writer = thread::spawn(move || {
        loop {
            // read from pty, write to fldigi

            let mut buf = [0; 1024];
            let num_bytes_rx = pty_tx_side.read(&mut buf).unwrap();

            let txdata = &buf[0..num_bytes_rx];
            radio_tx.write(txdata).unwrap();
        }
    });

    loop {
        // read from fldigi, write to master_file

        let mut buf = [0; 1024];
        let num_bytes_rx = radio.read(&mut buf).unwrap();

        if num_bytes_rx == 0 {
            break;
        }

        let txdata = &buf[0..num_bytes_rx];
        master_file.write(txdata).unwrap();
    }

    writer.join().unwrap();
}