드라이버를 사용하여 USB를 통해 Arduino Uno를 연결했습니다 cdc_acm
. 에서 찾을 수 있습니다 /dev/ttyACM0
.
Arduino 직렬 인터페이스의 규칙은 DTR
통합 직렬-USB 어댑터를 사용할 때 또는 RS-232 케이블을 사용할 때 핀 4 또는 5와 같은 재설정 신호(DTR/RTS/DSR/CTS 신호)를 사용하는 것입니다. 6 또는 8) 이 RESET
핀에 연결됩니다.
이 재설정 접근 방식의 중요한 장점은 그렇지 않은 경우정말적어도 밴드 밖에서는아주 근접한-failsafe(일반적으로 사용자가 제어할 수 없는 감시 회로가 있는 항상 대역 외 직렬 컨트롤러를 통한 구현으로 인해) 및할 수 있는물리적으로 비활성화되면(모델에 따라 커패시터 또는 저항기를 핀에 연결하여 RESET
) 이렇게 하면 이 중요한 킬 스위치 및 모든 관련 유틸리티가 완전히 파괴됩니다.
안타깝게도,것 같다, 현재 Linux는 프로그램이 어떤 이유로든 ACM 장치에 연결될 때 항상 이 신호를 보냅니다.윈도우와는 다르게,) 이러한 일이 발생하는 것을 방지하기 위해 모호하게 알려진 신뢰할 수 있는 방법조차 제공하지 않습니다.
(현재 -hupcl
,"마지막 프로세스가 tty를 닫을 때 끊기 신호를 보냅니다."그리고 -clocal
,"모뎀 제어 신호 비활성화"하다아니요이 신호가 전송되지 않도록 차단매번장치는열려 있는.)
/dev/ttyACM0
tl;dr: DTR/RTS/DSR/CTS 신호를 보내지 않고(하드웨어 수준에서 신호를 차단하는 것 제외) 액세스 권한을 얻으 려면 어떻게 해야 합니까 ?
답변1
Linux는 기본적으로 사용자 공간 프로세스가 직렬 장치(예: /dev/ttyS0
또는 ) 를 열 때 /dev/ttyACM0
이 줄을 발생시키고 닫을 때 해당 줄을 삭제합니다.DTR/RTS
dtr_rts
이는 드라이버 정의 콜백을 호출하여 수행됩니다.
불행하게도 이 성가신 동작을 비활성화할 수 있는 sysctl 또는 이와 유사한 기능은 아직 없으므로(현재는 거의 사용되지 않음) 작동하는 유일한 방법은 tty_port_operations
드라이버 구조에서 해당 콜백을 제거하고 드라이버 모듈을 다시 컴파일하는 것입니다.
cdc-acm
드라이버에 대해 주석을 달아 이 작업을 수행 할 수 있습니다 .이 줄:
--- drivers/usb/class/cdc-acm.c~
+++ drivers/usb/class/cdc-acm.c
@@ -1063,7 +1063,7 @@
}
static const struct tty_port_operations acm_port_ops = {
- .dtr_rts = acm_port_dtr_rts,
+ /* .dtr_rts = acm_port_dtr_rts, */
.shutdown = acm_port_shutdown,
.activate = acm_port_activate,
.destruct = acm_port_destruct,
이것은 것이다아니요DTR/RTS
직렬 ioctl(예: TIOCMSET
, ) 을 통해 와이어를 사용하는 것을 방지합니다 TIOCMBIC
.TIOCMBIS
acm_tty_tiocmset()
acm_ops
평소와 같이 구조에서 콜백을 기다립니다 .
비슷한 해킹을 다른 드라이버에도 사용할 수 있습니다. 개인적으로 PL2303
USB -> 직렬 드라이버와 함께 사용했습니다.
[차이점은 정보를 제공하기 위한 것입니다. 사이트가 탭과 공백을 나누기 때문에 직접 적용되지는 않습니다.]
답변2
이 문제에 대한 좋은 해결 방법이 있다고 생각합니다. 장치 /dev/ttyUSB0 또는 /dev/ttyACM0에서 데이터를 읽는 대신 명명된 파이프(예: /tmp/arduino)에서 데이터를 읽는 것이 가능합니다. 간단한 프로그램(아래)은 장치에서 데이터를 다음으로 복사합니다. 파이프를 연결하고 장치를 열린 상태로 저장합니다(따라서 DTR을 높게 설정하는 것을 방지). 이는 또한 장치에서 읽기를 처리하는 데 따른 모든 어려움을 방지합니다. 명명된 파이프를 사용하면 tty를 제어하기 위해 ioctl 명령을 실행할 필요 없이 cat, less -f 또는 표준 열기 + 읽기 기능이 있는 모든 프로그램과 같은 도구를 사용할 수 있습니다. 장치를 파이프에 복사하는 데 사용되는 프로그램은 시작 서비스로 실행되고 장치에서 파이프로 데이터를 복사합니다(그리고 일부 로그도 생성할 수 있음). 프로그램은 파이프 판독기 프로세스가 닫힐 때 종료되는 것을 방지하기 위해 SIGPIPE 신호를 처리해야 합니다. fcntl을 통한 의도적인 입력 차단으로 인해 서버의 부하가 미미합니다. 테스트 해봤는데 잘 작동하는 것 같습니다. 실제로 Arduino를 연결하여 동일한 문제를 해결하려고 합니다. 좋은 부작용은 DTR을 높게 설정하기 때문에 단순히 시작 서비스를 다시 시작하면 필요할 때 Arduino가 다시 시작된다는 것입니다.
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <fcntl.h>
/* for simplicity, most error handling is ommited, make sure you add it before using in production code */
void ignore_signal(int sig)
{
static struct sigaction _sigact;
memset(&_sigact, 0, sizeof(_sigact));
_sigact.sa_handler = SIG_IGN;
sigaction(sig, &_sigact, NULL);
}
int main()
{
ignore_signal(SIGPIPE);
int flags;
flags = fcntl(0, F_GETFL, 0) & ~O_NONBLOCK;
fcntl(0, F_SETFL, flags);
char c;
int n;
while(1)
{
n = read(0,&c,1);
if(n!=1)
{
sleep(1);
}
else
{
write(1,&c,1); /* ignoring the case that return code = -1 and errno = EPIPE means that data from Arduino are lost whenever pipe is not read */
write(2,&c,1);
}
}
return 0;
}
이를 시작하는 쉘 스크립트는 다음과 같습니다(업스타트 서비스로 다시 구현해야 함).
#!/bin/bash
DEV=/dev/ttyUSB0
PIPE=/tmp/arduino
LOG=/var/log/arduino.log
if test ! -p $PIPE
then
rm -f $PIPE
mkfifo $PIPE
fi
./my_dd <$DEV >$PIPE 2>>$LOG &
dd if=$PIPE of=/dev/null count=0 bs=1
파일이 아직 열려 있으므로 copytruncate를 사용하여 logrotate 파일을 복사해야 한다고 생각합니다(이것을 테스트할 기회가 없었습니다).
/var/log/arduino.log {
rotate 5
daily
compress
missingok
notifempty
create 640 root root
copytruncate
}
추가 참고 사항: 동시에 파이프 손상을 방지하려면 SIGPIPE 신호도 필터링하는 PIPE 매개변수와 함께 트랩 명령과 함께 cat 또는 dd를 사용하면 이를 달성할 수 있다는 것을 깨달았습니다. 위의 솔루션이 저에게 효과적이었기 때문에 비슷한 결과를 얻기 위해 트랩 명령을 사용하지 않았습니다.