Skip to content

Commit

Permalink
chore: add qmi apdu driver
Browse files Browse the repository at this point in the history
  • Loading branch information
damonto committed Jul 24, 2024
1 parent 2036f03 commit 157bc62
Show file tree
Hide file tree
Showing 10 changed files with 97 additions and 11 deletions.
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,22 @@ Once done, you can run the program with root privileges:
sudo ./telegram-sms -bot-token=YourTelegramToken --admin-id=YourTelegramChatID
```

### QMI

`libqmi` doesn't cut a new release yet, so if you want to use the QMI driver, you should compile `libqmi` by yourself.

> https://modemmanager.org/docs/libqmi/building/building-meson/
```bash
# Arch Linux (dependencis)
# pacman -S meson ninjia bash-completion gobject-introspection help2man
git clone https://gitlab.freedesktop.org/mobile-broadband/libqmi.git
cd libqmi
meson setup build --prefix=/usr
ninja -C build
ninja -C build install
```

If you wish to run the program in the background, you can utilize the `systemctl` command. Here is an example of how to achieve this:

1. Start by creating a service file in the `/etc/systemd/system` directory. For instance, you can name the file `telegram-sms.service` and include the following content:
Expand Down
2 changes: 1 addition & 1 deletion internal/app/handler/chip.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func HandleChipCommand(c telebot.Context) error {
func (h *ChipHandler) handle(c telebot.Context) error {
h.modem.Lock()
defer h.modem.Unlock()
usbDevice, err := h.modem.GetAtPort()
usbDevice, err := h.GetUsbDevice()
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion internal/app/handler/download.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func (h *DownloadHandler) download(c telebot.Context) error {

h.modem.Lock()
defer h.modem.Unlock()
usbDevice, err := h.modem.GetAtPort()
usbDevice, err := h.GetUsbDevice()
if err != nil {
return err
}
Expand Down
8 changes: 8 additions & 0 deletions internal/app/handler/handler.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package handler

import (
"github.com/damonto/telegram-sms/internal/pkg/config"
"github.com/damonto/telegram-sms/internal/pkg/modem"
"github.com/damonto/telegram-sms/internal/pkg/state"
"gopkg.in/telebot.v3"
Expand All @@ -16,3 +17,10 @@ func (h *handler) init(c telebot.Context) {
h.modem = c.Get("modem").(*modem.Modem)
h.stateManager = c.Get("state").(*state.StateManager)
}

func (h *handler) GetUsbDevice() (string, error) {
if config.C.APDUDriver == config.APDUDriverAT {
return h.modem.GetAtPort()
}
return h.modem.GetQMIDevice()
}
10 changes: 9 additions & 1 deletion internal/app/handler/modem.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"log/slog"
"strings"

"github.com/damonto/telegram-sms/internal/pkg/config"
"github.com/damonto/telegram-sms/internal/pkg/lpac"
"github.com/damonto/telegram-sms/internal/pkg/modem"
"github.com/damonto/telegram-sms/internal/pkg/util"
Expand Down Expand Up @@ -40,7 +41,7 @@ EID: %s

var EID string
if m.IsEuicc {
usbDevice, err := m.GetAtPort()
usbDevice, err := getUsbDevice(m)
if err != nil {
slog.Error("failed to get AT port", "error", err)
}
Expand All @@ -66,3 +67,10 @@ EID: %s
}
return c.Send(util.EscapeText(strings.TrimRight(message, "\n")), &telebot.SendOptions{ParseMode: telebot.ModeMarkdownV2})
}

func getUsbDevice(modem *modem.Modem) (string, error) {
if config.C.APDUDriver == config.APDUDriverAT {
return modem.GetAtPort()
}
return modem.GetQMIDevice()
}
10 changes: 5 additions & 5 deletions internal/app/handler/profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func HandleProfilesCommand(c telebot.Context) error {
func (h *ProfileHandler) handle(c telebot.Context) error {
h.modem.Lock()
defer h.modem.Unlock()
usbDevice, err := h.modem.GetAtPort()
usbDevice, err := h.GetUsbDevice()
if err != nil {
return err
}
Expand Down Expand Up @@ -127,7 +127,7 @@ func (h *ProfileHandler) handleAction(c telebot.Context) error {
func (h *ProfileHandler) handleAskAction(c telebot.Context) error {
h.modem.Lock()
defer h.modem.Unlock()
usbDevice, err := h.modem.GetAtPort()
usbDevice, err := h.GetUsbDevice()
if err != nil {
return err
}
Expand Down Expand Up @@ -185,7 +185,7 @@ func (h *ProfileHandler) handleActionDelete(c telebot.Context) error {
}
h.modem.Lock()
defer h.modem.Unlock()
usbDevice, err := h.modem.GetAtPort()
usbDevice, err := h.GetUsbDevice()
if err != nil {
return err
}
Expand All @@ -202,7 +202,7 @@ func (h *ProfileHandler) handleActionDelete(c telebot.Context) error {

func (h *ProfileHandler) handleActionEnable(c telebot.Context) error {
h.modem.Lock()
usbDevice, err := h.modem.GetAtPort()
usbDevice, err := h.GetUsbDevice()
if err != nil {
return err
}
Expand All @@ -223,7 +223,7 @@ func (h *ProfileHandler) handleActionEnable(c telebot.Context) error {
func (h *ProfileHandler) handleActionRename(c telebot.Context) error {
h.modem.Lock()
defer h.modem.Unlock()
usbDevice, err := h.modem.GetAtPort()
usbDevice, err := h.GetUsbDevice()
if err != nil {
return err
}
Expand Down
12 changes: 11 additions & 1 deletion internal/pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,16 @@ import (
"errors"
)

const (
APDUDriverAT string = "at"
APDUDriverQMI string = "qmi"
)

type Config struct {
BotToken string
AdminId int64
Dir string
APDUDriver string
Version string
DontDownload bool
Verbose bool
Expand All @@ -16,12 +22,16 @@ type Config struct {
var C = &Config{}

var (
ErrBotTokenRequired = errors.New("bot token is required")
ErrBotTokenRequired = errors.New("bot token is required")
ErrUnsupportedAPDUDriver = errors.New("unsupported apdu driver")
)

func (c *Config) IsValid() error {
if c.BotToken == "" {
return ErrBotTokenRequired
}
if c.APDUDriver != APDUDriverAT && c.APDUDriver != APDUDriverQMI {
return ErrUnsupportedAPDUDriver
}
return nil
}
9 changes: 7 additions & 2 deletions internal/pkg/lpac/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,13 @@ func NewCmd(ctx context.Context, usbDevice string) *Cmd {

func (c *Cmd) Run(arguments []string, dst any, progress Progress) error {
cmd := exec.CommandContext(c.ctx, filepath.Join(config.C.Dir, "lpac"), arguments...)
cmd.Env = append(cmd.Env, "LPAC_APDU=at")
cmd.Env = append(cmd.Env, "AT_DEVICE="+c.usbDevice)
cmd.Env = append(cmd.Env, "LPAC_APDU="+config.C.APDUDriver)
if config.C.APDUDriver == config.APDUDriverAT {
cmd.Env = append(cmd.Env, "AT_DEVICE="+c.usbDevice)
} else {
cmd.Env = append(cmd.Env, "QMI_DEVICE="+c.usbDevice)
}
slog.Debug("running lpac command", "command", cmd.String(), "env", cmd.Env)

stderr := bytes.Buffer{}
cmd.Stderr = &stderr
Expand Down
38 changes: 38 additions & 0 deletions internal/pkg/modem/modem.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,19 @@ import (
"fmt"
"log/slog"
"os"
"os/exec"
"regexp"
"strings"
"syscall"

"github.com/damonto/telegram-sms/internal/pkg/config"
"github.com/maltegrosse/go-modemmanager"
"golang.org/x/sys/unix"
)

var (
ErrNoATPortFound = errors.New("no at port found")
ErrNoQMIDeviceFound = errors.New("no QMI device found")
ErrRestartCommandNotFound = errors.New("restart command not found")

restartCommands = map[string]string{
Expand All @@ -34,6 +37,27 @@ func (m *Modem) Unlock() {
}

func (m *Modem) isEuicc() bool {
if config.APDUDriverAT == config.C.APDUDriver {
return m.checkByATCommand()
}
return m.checkByQmicli()
}

func (m *Modem) checkByQmicli() bool {
qmiDevice, err := m.GetQMIDevice()
if err != nil {
slog.Error("failed to get QMI device", "error", err)
return false
}
result, err := exec.Command("qmicli", "-d", qmiDevice, "-p", "--uim-get-slot-status").Output()
if err != nil {
slog.Error("failed to get sim slot info", "error", err)
return false
}
return strings.Contains(string(result), "Is eUICC: yes")
}

func (m *Modem) checkByATCommand() bool {
m.RunATCommand("AT+CCHC=1")
m.RunATCommand("AT+CCHC=2")
m.RunATCommand("AT+CCHC=3")
Expand Down Expand Up @@ -153,6 +177,20 @@ func (m *Modem) GetAtPort() (string, error) {
return "", ErrNoATPortFound
}

func (m *Modem) GetQMIDevice() (string, error) {
ports, err := m.modem.GetPorts()
if err != nil {
return "", err
}

for _, port := range ports {
if port.PortType == modemmanager.MmModemPortTypeQmi {
return fmt.Sprintf("/dev/%s", port.PortName), nil
}
}
return "", ErrNoQMIDeviceFound
}

func (m *Modem) GetManufacturer() (string, error) {
return m.modem.GetManufacturer()
}
Expand Down
1 change: 1 addition & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ func init() {
}
flag.StringVar(&config.C.BotToken, "bot-token", "", "telegram bot token")
flag.Int64Var(&config.C.AdminId, "admin-id", 0, "telegram admin id")
flag.StringVar(&config.C.APDUDriver, "apdu-driver", "at", "the APDU driver to use (at or qmi)")
flag.StringVar(&config.C.Version, "version", "v2.0.2", "the version of lpac to download")
flag.StringVar(&config.C.Dir, "dir", "/tmp/telegram-sms", "the directory to store lpac")
flag.BoolVar(&config.C.DontDownload, "dont-download", false, "don't download lpac binary")
Expand Down

0 comments on commit 157bc62

Please sign in to comment.