вторник, 28 августа 2012 г.

FFI в Лиспворкс

В общем, потребовалось из лиспа общаться с железкой на MSP430, которая подключена к компу по USB и торчит в системе в виде ttyACM. Чтобы с оным агрегатом общаться, нужно, как минимум, установить бодрейт. В Лиспворксе есть пакет serial-port, который, к сожалению, доступен только под виндой, поэтому юниксоидам нужно делать дополнительные движения.

Сначала я решил не париться, и открыть пайп, на другом конце которого сидит cu из uucp и делает за меня всё tty-специфичное. Но на машинах, где это дело крутиться будет, uucp нет, а для его устанавки нужно делать много действий, которые включают в себя ответы на кучу фиктивных багрепортов. Ещё можно вызывать stty и устанавливать флажки им, а в софте использовать обычный open. Однако, на деле выяснилось, что стандартному open в Лиспворксе от открытия файла устройства в режиме :io плохеет. В общем, надо открывать устройство позиксовым open, крутить tty-ручки и создавать на его основе лисповый stream.

У LispWorks есть foreign language interface, с помощью которого можно описать сишечные функции, структуры, переменные, подгрузить библиотеку и прочие типичные для такой задачи действия, доступные, например, в оупенсорсных UFFI/CFFI. Соответственно, можно или написать обёртку на Си, которая открывает дескриптор, дёргает termios-вызовы и возвращает причёсанный дескриптор в лисп, или вскочить на коня, выхватить шашку и описать нужное подмножество termios.h при помощи FLI. Впрочем, при чтении документации на FLI выяснилась приятная неожиданность: в LW есть встроенный парсер сишных заголовков, который сам всё описывает. Вот мой пример с termios:

(require "foreign-parser")

(foreign-parser:process-foreign-file "/usr/include/termios.h" :dff "/path/to/project/termios.lisp" :package :unix)


Всё. Это даже круче, чем sb-grovel в SBCL! Единственное, что константы, объявленные препроцессором (через #define) автоматически не конвертятся. Ну да ладно.

А вот функция, открывающая в LW tty-девайс:

(defun open-device ()
  (let ((fd (sys::unix-open *dev* 2)))
    (fli:with-dynamic-foreign-objects ((term unix::termios))
      (unix::tcgetattr fd term)
      (unix::cfsetospeed term #o10004) ;; 460800
      (unix::tcsetattr fd 0 term)      ;; TCSANOW
      (unix::tcflush fd 2))            ;; TCOFLUSH
    (sys:attach-stream fd :direction :io :automatically-close t)))

3 комментария:

  1. Круто, не знал про "foreign-parser". Надо будет потыкать.

    ОтветитьУдалить
  2. шутка про ёжиков и кактусы...

    ОтветитьУдалить