The OpenNET Project / Index page

[ новости/++ | форум | wiki | теги ]

Настройка работы CDMA модема через /dev/ttyACM0
Окружение

ОС - Fedora 11 (Russian Remix). Ядро 2.6.29.4-167.fc11.i686.PAE. Mодем Maxon
Minimax MM-5500U (CDC ACM модем). Файлы /etc/wvdial.conf, /etc/resolv.conf
настроены верно. Используется верно настроенная программа дозвона
chestnut-dialer (вер. 0.3.3) (хотя можно и без нее если установлен wvdial).

Ситуация 1.

Загружается ОС. Модем отключен. Затем модем включается и дозвон невозможен,
поскольку отсутствует файл устройства /dev/ttyACM0.

Мониторинг командой udevadm monitor показывает, что его удаляет ядро и затем Udev.

Ситуация 2. 

Загружается ОС. Модем включен. Дозвон возможен (файл устройства /dev/ttyACM0
существует). Модем отключается - файл  устройства /dev/ttyACM0 пропадает. При
повторном включении модема файл устройства отсутствует. В итоге дозвон невозможен.

Решение.

Подобные ситуации возникали, в частности, и в Ubuntu 9.10 и в некоторых других
Linux. Из форумов видно, что проблему рекомендуют решать перезагрузкой модуля
ядра cdc-acm и созданием файла устройства в командном режиме. Рекомендуют также
автоматизацию при помощи правил udev. Возможна даже ситуация когда имеем после
нескольких plug/unplug модема множество /dev/ttyACM0..n. И модем не
распознается. Правила udev помогают не всегда (установлено экспериментально).

Связь с модемом можно установить и при помощи представленной ниже 
специально разработанной программы (тестировалась на Fedora 11 Russian Remix) и
Ubuntu 9.10 (версии udev 141 и 147 соответственно).


 // File minimaxd.c
 //
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 #include <sys/types.h>
 #include <signal.h>
 #include <syslog.h>
 
 #define TMP_FILE__PID 		"/tmp/minimaxd.pid"
 #define TMP_FILE__LSUSB		"/tmp/lsusbminimax.tmp"
 #define PATH_CDC_ACM_MODULE	"/sys/module/cdc_acm/initstate"
 #define PATH_MODEM_TTY		"/dev/ttyACM0"
 #define CHECK_MODEM_PLUG_STR	"lsusb | grep Qualcomm >   /tmp/lsusbminimax.tmp"

 int fileexist(const char* filename);
 void kill_copy_daemon();
 void start_restart();
 void stop();
 int CheckPlugModem();
 void wait_cdc_acm_control();
 int CheckPid();
 void SetPid();
 int GetPid();
 void PrintMessageToLog(char* szMessage);
 
 int main(int argc, char* argv[])
 {
   if(argc==2)
   {
     if((!strcmp("start",argv[1]))||(!strcmp("restart",argv[1]))) 
     {
       kill_copy_daemon();
       wait_cdc_acm_control();
     }
     else
     if(!strcmp("stop",argv[1])) kill_copy_daemon();
   }
   else
   {
      printf("Start/Restart MiniMax Modem Access:\nminimaxd start|stop|restart\n");
     printf("Background start:\nminimaxd start &\n");
   }
   return 0;
 }

 int fileexist(const char* filename)
 {
   int res = 0;
   FILE* f=fopen(filename,"rt");
   if(f!=NULL) { fclose(f); res=1; }
   return res;
 }

 void kill_copy_daemon()
 {
    char cmd[255]="";
    int pid = GetPid();
    if(pid>0) 
    {
       kill((pid_t)(pid), SIGKILL);
       strcpy(cmd,"rm -f "); strcat(cmd,TMP_FILE__PID);   system(cmd);
       strcpy(cmd,"rm -f "); strcat(cmd,TMP_FILE__LSUSB); system(cmd);
       PrintMessageToLog("Modem service is stopped");
    }
 }

 void start_restart()
 {
     system("rm -f /dev/modem");
     system("rm -f /dev/ttyACM*");
     system("rmmod cdc-acm");
     system("modprobe cdc-acm");
     system("mknod /dev/ttyACM0 c 166 0");
     system("chmod 666 /dev/ttyACM*");
     system("ln -s /dev/ttyACM0 /dev/modem");
     PrintMessageToLog("Modem service started");
 }

 void stop()
 {
    system("rmmod cdc-acm");
    system("rm -f /dev/modem");
    system("rm -f /dev/ttyACM*");
    PrintMessageToLog("Modem service is stopped");
 }

 int CheckPlugModem()
 {
   char str[255]="";
   int res = 0;
 
   system(CHECK_MODEM_PLUG_STR);
 
   sleep(2);
 
   FILE* f=fopen(TMP_FILE__LSUSB,"rt");
   if(f!=NULL)
   {
     strcpy(str,"");
     fscanf(f,"%s",str);
     fclose(f);
     if(strlen(str)>0) res = 1;
   }
   return res;
 }
 
 void wait_cdc_acm_control()
 {
   int fl_CDC_ACM_OK=0;
   int fl_ttyACM0_OK=0;
   
   if(!CheckPid())
   {
     SetPid();
    
     while(1)
     {
	fl_CDC_ACM_OK=fileexist(PATH_CDC_ACM_MODULE);
 	if(CheckPlugModem())
	{
	  fl_ttyACM0_OK=fileexist(PATH_MODEM_TTY);
	  
	  if((fl_CDC_ACM_OK==1)&&(fl_ttyACM0_OK==0)) start_restart();
	  else
	  if((fl_CDC_ACM_OK==0)&&(fl_ttyACM0_OK==0)) start_restart();
	}
	else
	{
	  if(fl_CDC_ACM_OK) stop();
	}
	sleep(3);
     }
   }
 }

 int CheckPid()
 {
   return fileexist(TMP_FILE__PID);
 }

 void SetPid()
 {
   pid_t pid = getpid();
   FILE* f=fopen(TMP_FILE__PID,"wt");
   if(f!=NULL)
   {
     fprintf(f,"%d",pid);
     fclose(f);
   }
 }
 
 int GetPid()
 {
   int res = 0;
   FILE* f=fopen(TMP_FILE__PID,"rt");
   if(f!=NULL)
   {
     fscanf(f,"%d",&res);
     fclose(f);
   }
   else res = -3;
   return res;
 }
 
 void PrintMessageToLog(char* szMessage)
 {
   openlog("MINIMAXD", LOG_ODELAY, LOG_USER);
   syslog(LOG_INFO, "%s", szMessage);
   closelog();
 }
 
 
Компиляция командой:

   gcc ./minimaxd.c -o minimaxd

Для 64-разрядной версии:

   gcc ./minimaxd.c -m64 -o minimaxd

Полученный minimaxd копируем в каталог /usr/local/bin/
Назначаем владельцем root.

Тестирование.

Запуск программы из коммандной строки выполняем так:
   
   # minimaxd start

или в фоне:

   # minimaxd start &

Подключаем модем к usb-порту, ждем инициализации около 5 сек. Затем пробуем
дозвон. Прерываем дозвон.

Отсоединяем модем, подсоединяем заново и через 5 сек. повторяем процедуру. Если
все Ok, то завершаем процедуру тестирования.
Для завершения работы minimaxd, работающей в фоне, в другой консоли выполняем:

   # minimaxd stop

Запуск при загрузке ОС.

В каталоге /etc/rc.d/init.d создаем упрощенный скрипт (файл minimaxdaemon) для
менеджмента "демона":


 #!/bin/sh
 # startup script for Minimax daemon (/usr/local/bin/minimaxd)

 DAEMON=/usr/local/bin/minimaxd

 minimaxdaemon_start ()
 {
     echo -n "Starting ${DAEMON}: "
     ${DAEMON} start &
 }
 
 minimaxdaemon_stop ()
 {
     echo -n "Shutting down ${DAEMON}: "
     ${DAEMON} stop
 }
 
 minimaxdaemon_restart ()
 {
     echo -n "Restarting ${DAEMON}: "
     ${DAEMON} restart &
 }
  
 
 case $1 in
 
 	start)
 		minimaxdaemon_start
 		;;
 		
 	stop)
 		minimaxdaemon_stop
 		;;
 	
 	status)
 		echo "${DAEMON}:" `pidof ${DAEMON}`
 		;;
 	
 	restart)
 		minimaxdaemon_restart
 		;;
 	
 	*)
 		echo "Usage: minimaxdaemon  {start|stop|restart|status}"
		exit 1
		;;
 esac
 exit 0


Создаем на minimaxdaemon символические ссылки в каталогах rc?.d. Например, для
Fedora 11 (Russion Remix):

   ln -s /etc/rc.d/init.d/minimaxdaemon /etc/rc.d/rc0.d/K01minimaxdaemon
   ln -s /etc/rc.d/init.d/minimaxdaemon /etc/rc.d/rc6.d/K01minimaxdaemon
   ln -s /etc/rc.d/init.d/minimaxdaemon /etc/rc.d/rc5.d/S96minimaxdaemon

Теперь можно проверить работу minimaxd после перезагрузки.

Заключение

Следует отметить, что это все верно в случае, если в ядре существует поддержка
модуля cdc-acm. Задержки (функции sleep) могут быть выбраны иные. Пример можно
рассматривать и для других подобных модемов, работающих через cdc-acm-модуль и
использующих /dev/ttyACM0-файл. Однако, надо учесть, что для определения в
системе модема используется команда lsusb и в данном случае
она в программе такая:

   lsusb | grep Qualcomm > /tmp/lsusbminimax.tmp

Т.е. подразумевается, что модем определяется, например, как:

   Bus 009 Device 003: ID 05c6:3196 Qualcomm, Inc. CDMA Wireless Modem

Поэтому в программе и фильтруем по слову "Qualcomm".
В случае других производителей управляющую команду надо будет изменить.
 
13.11.2009 , Автор: Игорь Гаркуша
Ключи: modem, kernel, linux, module, hardware, udev, winmodem, softmodem, cdma / Лицензия: CC-BY
Раздел:    Корень / Администратору / Система / Поддержка аппаратного обеспечения

Обсуждение [ RSS ]
 
  • 1.1, Аноним, 15:21, 13/11/2009 [ответить] [смотреть все]
  • +/
    Безграмотный (как и "инные") костыль.
     
     
  • 2.2, Аноним, 16:14, 13/11/2009 [^] [ответить] [смотреть все]
  • +/
    Ну что ж, Аноним, не всем правильно тексты писать Зато прога добрая А udev час... весь текст скрыт [показать]
     
  • 1.3, kkk, 13:07, 14/11/2009 [ответить] [смотреть все]  
  • +/
    Просто волшебно !

    Такого идиотского кода на C я не видел лет 15.

    Самые перлы:
    strcpy(cmd,"rm -f "); strcat(cmd,TMP_FILE__PID);   system(cmd);
    system("rm -f /dev/modem");
    system("mknod /dev/ttyACM0 c 166 0");
    system("ln -s /dev/ttyACM0 /dev/modem");
    fscanf без проверки ошибок.
    волшебные sleep()

    Афтар, напесал бы еще свой шелл-скрипт на ассемблере !

     
     
  • 2.4, Igor, 13:53, 14/11/2009 [^] [ответить] [смотреть все]  
  • +/
    kkk - да я погляжу Вы Эксперт Понимаю Ваш юмор У меня б наверное первая реакци... весь текст скрыт [показать]
     
     
  • 3.5, kkk, 14:20, 14/11/2009 [^] [ответить] [смотреть все]  
  • +/
    >kkk - да я погляжу Вы Эксперт!

    У Вас Видение !

    system("rm -f /dev/modem"); <-- unlink(2)
    system("mknod /dev/ttyACM0 c 166 0"); <-- mknod(8)
    system("ln -s /dev/ttyACM0 /dev/modem");  <-- symlink(2)

    int fileexist(const char* filename)
    {
       int res = 0;
       FILE* f=fopen(filename,"rt");
       if(f!=NULL) { fclose(f); res=1; }
       return res;
    } <-- stat(2)

    void kill_copy_daemon();
    void start_restart(); <-- такая чудная вещь, как прототипы

    написали бы трех-строчную C-программку, которая бы делала execve(2) скрипта

     
     
  • 4.6, Igor, 14:28, 14/11/2009 [^] [ответить] [смотреть все]  
  • +/
    :)
    Не хотелось скрипт за программой тягать.
     
  • 4.7, Igor, 14:33, 14/11/2009 [^] [ответить] [смотреть все]  
  • +/
    На счет stat(2) - это да. Верно. Даже как-то
    и не сообразил.
    Про unlink(2) и symlink(2) - каюсь.
    Спасибо.
     
     
  • 5.8, kkk, 14:59, 14/11/2009 [^] [ответить] [смотреть все]  
  • +/
    mknod(2), конечно же.

    Код
       if(!CheckPid())
       {
         SetPid();
    содержит race(), почитайте про флаг O_EXCL у open(2)

     
     
  • 6.9, Igor, 15:19, 14/11/2009 [^] [ответить] [смотреть все]  
  • +/
    По поводу unlink, mknod, symlink - откровенно говоря было
    лень переделывать строки из шелл-скрипта.

    >> Код ... содержит race()

    Имеется ввиду, что может возникнуть блокировка файла /tmp/minimaxd.pid?
    Возможно. Но каким образом.
    К файлу обращается только одна запущенная копия процесса. Это
    достигается вызовом в программе функции kill_copy_daemon().
    Или Вы подразумеваете, что какой-либо другой процесс
    может обратиться к этому файлу?
    В любом случае, спасибо за дельные советы.

     

    Ваш комментарий
    Имя:         
    E-Mail:      
    Заголовок:
    Текст:



      Закладки на сайте
      Проследить за страницей
    Created 1996-2017 by Maxim Chirkov  
    ДобавитьРекламаВебмастеруГИД  
    Hosting by Ihor