ZTE MF180

Материал из Linux Wiki
Перейти к навигацииПерейти к поиску

Сабж

Билайновский модем, куплен в августе 2010 за 700 с чем-то RUR

Заставляем систему видеть

Для корректной работы проще всего поставить ядро с поддержкой драйвера option (чтобы не мучаться с usbserial и udev).

Швейцарский нож

Далее, необходимо (вернее, можно обойтись и посылкой AT-команд, но мы же слишком ленивы для этого) использовать небольшой, но очень полезный скрипт:

Скрипт
#!/usr/bin/perl -w

#    v 07122010
#    скрипт для выполнения ussd и других команд 3G модема ZTE MF100 (возможно будет работать и на других модемах, не проверено...)
#    подробнее параметры коммандной строки:
#    mf100.pl help

#    29-04-2012
#    Отправка USSD-команд вида #102#
#    Раскодирование ответа UCS-2 -> UTF-8
#     Ivan Krylov <aitap@jabber.ru>

#     Copyright (C) 2010  Mikhail Burshtynskiy  (mikhail@m-blog.pp.ua)
 
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
 
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
 
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
############################################################################################
# настройка модема на примере Debian lenny версия ядра: 2.6.30-bpo.2-686; Ubuntu 9.10   
# Включаем модем, он определяется как USB CD-ROM диск( /dev/scd0 или /dev/scd1 и т.д) на котором софт для windows
# отключаем USB CD-ROM командой:
# $ eject /dev/scd0
# модем отключается, секунд через 30 (примерно) включается, появляются новые устройства:
# /dev/ttyUSB0
# /dev/ttyUSB1
# /dev/ttyUSB2
# инет работает на /dev/ttyUSB2
# далее два варианта:
# 1. если модем работает стабильно, то просто отключаем автозагрузку USB CD-ROM диска воспользовавшись этим скриптом
# введите команду:
# $ ./mf100.pl cdstop      #(чтобы включить обратно: $ ./mf100.pl cdrun )
# 2. если модем периодически отключается то переключаем модем в режим только модема,
# при этом отключается кардридер и доступ к USB CD-ROM диску.
# ОСТОРОЖНО! при переключении меняется идентификатор модема, если у вас ядро версии 2.6.29 или выше то нет проблем.
# введите команду:
# $ ./mf100.pl modem      #(чтобы переключить обратно: $ ./mf100.pl storage )
# настраиваем инет на /dev/ttyUSB2
# всё...
#
# если при подключении модема появляется только /dev/ttyUSB0
# вам поможет команда: # rmmod option
############################################################################################
 
use strict;
use File::Basename;
use POSIX qw(O_RDWR O_NOCTTY);
use Encode qw{encode from_to};
use Term::ANSIColor qw(:constants);
$Term::ANSIColor::AUTORESET = 1;
 
#параметры
my $usb_1='/dev/ttyUSB1';
my $usb_2='/dev/ttyUSB2';
my $timeout=20;# выход по таймауту
#######################
 
my $exit_="Время ожидания истекло, выход.";
my $error="ERROR\n";
####
my $name=$0;
$name=basename $name if $name =~m|^/usr|;
my $help=<<"HELP";
Использование:
$name команда
например USSD команды (проверка, пополнение счета и другие):
$name *111#, $name *код*код пополнения#, ... 
Другие опции:
info      различная информация о модеме
on        включить модем
off       выключить модем
modem     переключиться в режим модема (отключается встроенный кардридер и автозагрузка встроенного CD-ROM диск)
storage   отключить режим модема
cdstop    отключить автозапуск встроенного диска (на нем ПО для windows)
cdrun     включить автозапуск встроенного диска
sig       мониторинг уровеня сигнала сети,  для выхода Ctrl^C
search    выбор режима поиска сети GSM/3G(WCDMA)
network   сканировать сеть и вывести список доступных операторов связи
          + возможность ручного выбора оператора если есть доступны(й|е) для регистрации
pin       ввести PIN (PUK) код
sequrity  изменить, активировать/деактивировать  PIN код.
help      показать этот текст
HELP
####
my $par=shift @ARGV;
if($par){
	unless ($par=~m/^[*#][0-9]{3}[*0-9]*#$|^[0-9]$|^on$|^off$|^sig$|^info$|^cdstop$|^cdrun$|^-*help$|^network$|^search$|^storage$|^modem$|^pin$|^sequrity$/i){
		print BOLD, RED, "\nНЕВЕРНАЯ КОМАНДА!",RESET,"\n\n$help";
		exit;
	}
####
	if($par=~/help/i){
		print $help;
		exit;
	}
####
unless(-c $usb_1){print BOLD, RED, "модем не найден\n", RESET;exit;}
unless(-c $usb_2){print BOLD, RED, "модем не найден\n", RESET;exit;}
####
	if(sysopen(USB, $usb_2, O_RDWR | O_NOCTTY)){
		system("stty -iutf8 hupcl -icrnl -opost -onlcr -isig -icanon -echo -echoe -echok -F $usb_2");
	}else{
		print "$!, выход\n" and exit if $par=~m/^info$/i;
		print "$!, выход\n" and exit unless sysopen(USB, $usb_1, O_RDWR | O_NOCTTY);
		system("stty -iutf8 hupcl -icrnl -opost -onlcr -isig -icanon -echo -echoe -echok -F $usb_1");
	}
	select((select(USB), $| = 1)[0]);
#####
	my $sim_status=info('CPIN?', qr/(ERROR: SIM failure)/);#проверка SIM
	print BOLD, RED, "$sim_status\n", RESET if $sim_status;
#####
my $pin_info=info('CPIN?', qr/SIM (PIN.*|PUK.*)/);#проверка необходимости ввода PIN PUK кода
print BOLD, qq(ВНИМАНИЕ! Модем ожидает ввод $pin_info\n), RESET if $pin_info;
#####
	if($par=~/pin/i){
		pin_puk() unless info('CPIN?', qr/(READY)/);# ввод PIN PUK
	}elsif($par=~/sequrity/i){#изменить, активировать, деактивировать  PIN код
		if(info('CPIN?', qr/(READY)/)){
			while(1){
				if(info('clck="SC",2', qr/\+CLCK: ?(1)/)){
MARK1:{
					print "\n1  отключить проверку PIN кода\n";
					print "2  изменить PIN код\n";
					print "или\n0  выход\nваш выбор? введите цифру:";
					chomp(my $answer=<>);
					exit if $answer eq "0";
					print BOLD, RED, "введено некорректное значение!\n\n", RESET and redo MARK1 unless $answer=~m/^[12]$/;
					if($answer == 1){
						print BOLD, "ВНИМАНИЕ! При вводе PIN кода будте предельно внимательны.\nУ вас 3 или менее попыток.\n", RESET;
MARK2:{
						print UNDERLINE, "\n-> деактивация PIN кода <-\n", RESET;
						my $pin=check_pin('Введите PIN код:');
						my $pin_=info(qq(clck="SC",0,"$pin"), qr/(ERROR.*)/);
						if($pin_){
							if ($pin_=~m/PUK/i){
								print pin_puk() ? redo MARK1 : last;
							}
							print RED, BOLD, "$pin_\n", RESET and redo MARK2;
						}
							print BOLD, "OK\n",RESET;
}
					}
					if($answer == 2){
						print BOLD, "ВНИМАНИЕ! При вводе PIN кода будте предельно внимательны.\nУ вас 3 или менее попыток.\n", RESET;
MARK3:{
						print UNDERLINE, "\n-> изменение PIN кода <-\n", RESET;
						my $pin1=check_pin('Введите старый PIN код:');
						my $pin2=check_pin('Введите новый PIN код:');
						my $pin_=info(qq(cpwd="SC",$pin1,$pin2), qr/(ERROR.*)/);
						if($pin_){
							pin_puk() and last if $pin_=~m/PUK/i;
							print RED, BOLD, "$pin_\n", RESET and redo MARK3;
						}
						print BOLD, "OK\n", RESET;
} 
					}
}
					last;
				}else{
MARK4:{
					print "\n1  включить проверку PIN кода\n";
					print "или\n0  выход\nваш выбор? введите цифру:";
					chomp(my $answer=<>);
					exit if $answer eq "0";
					print RED, BOLD, "введено некорректное значение!\n\n", RESET and redo MARK4 unless $answer=~m/^1$/;
 
					print BOLD, "ВНИМАНИЕ! При вводе PIN кода будте предельно внимательны.\nУ вас 3 или менее попыток.\n", RESET;
MARK5:{
					print UNDERLINE, "\n-> активация PIN кода <-\n", RESET;
					my $pin=check_pin('Введите PIN код:');
						my $pin_=info(qq(clck="SC",1,"$pin"), qr/(ERROR.*)/);
						if($pin_){
							if ($pin_=~m/PUK/i){
								print pin_puk() ? redo MARK4 : last;
							}
							print RED, BOLD, "$pin_\n", RESET and redo MARK5;
						}
							print BOLD, "OK\n", RESET;
}
}
					last;
				}
			}
		}
	}elsif($par=~/off/i){
		print RED, $error, RESET unless switch('cfun=0', 'отключение модема');
	}elsif($par=~/on/i){
		print RED, $error, RESET unless switch('cfun=1', 'включение модема');
	}elsif($par=~/cdstop/i){
		my $stat=info('zcdrun=8', qr/(Close autorun state)/);
		$stat ? print "$stat\n" : print RED, $error, RESET;
	}elsif($par=~/cdrun/i){
		my $stat=info('zcdrun=9', qr/(Open autorun state)/);
		$stat ? print "$stat\n" : print RED, $error, RESET;
	}elsif($par=~/modem/i){
		my $stat=info('zcdrun=e', qr/(Enter download mode)/);
		$stat ? print "OK\n" : print RED, $error, RESET;
	}elsif($par=~/storage/i){
		my $stat=info('zcdrun=f', qr/(exit download mode)/);
		$stat ? print "OK\n" : print RED, $error, RESET;
	}elsif($par=~/sig/i){ #мониторинг уровеня сигнала сети
		print "Для выхода - Ctrl^C\n";
		my $sig="";
		my $s="";
		my $usb;
			while(1){
				$s=info('CSQ', qr/\+CSQ: (\d+?),\d{2}/);
				print BOLD, "Сигнал сети: ", RESET, sig($s)."\n" if  $s && $s ne $sig;
				$sig =$s if $s;
				sleep 1;
			}
	}elsif($par=~/info/i){ #различная информация 
		print "\n";
		my $producer=info('CGMI', qr/(^.{3,})/);
      print BOLD, "Производитель: ", RESET, "$producer\n" if $producer;
 
		my $model=info('CGMM', qr/(^.{3,})/);
		print BOLD, "Модель: ", RESET, "$model\n" if $model;
 
		my $prog=info('CGMR', qr/(^.{5,})/);
		print BOLD, "Версия ПО: ", RESET, "$prog\n" if $prog;
 
		my $imei=info('CGSN', qr/(^\d{15,})/);
		print BOLD, "IMEI: ", RESET, "$imei\n" if $imei;
 
		my $iems=info('CIMI', qr/(^\d{15,})/);
		print BOLD, "IEMS: ", RESET, "$iems\n" if $iems;
 
		my $number=info('CNUM', qr/\+CNUM:\D+,"(\+?\d{10,})",\d+/);
		if($number){
			$number=~s/(.+)/\+$1/ unless $number=~/^\+/;
			print BOLD, "Ваш номер: ", RESET, "$number\n";
		}
 
		print BOLD, "Сигнал сети: ", RESET, sig(info('CSQ', qr/\+CSQ: (\d+?),\d{2}/))."\n";#уровень сигнала сети
		my $operator=operator();
		print BOLD, "Оператор: ", RESET, "$operator\n" if $operator;#оператор сети
		my $mode_=mode();
		print BOLD, "Режим поиска сети: ", RESET, "$mode_" if $mode_;
		print "\n";
      exit;
	}elsif($par=~/search/i){# выбор режима поиска сети
		my $status=0;
		my $mode_=mode();
		print BOLD, "Текущие настройки: $mode_", RESET if $mode_;
			while(1){
				print <<SAY;
Выберите:
1 автоматически, GSM+WCDMA
2 автоматически, GSM+WCDMA, предпочтительно GSM
3 автоматически, GSM+WCDMA, предпочтительно WCDMA
4 автоматически, только GSM
5 автоматически, только WCDMA
0 выход
SAY
				print "ваш выбор? введите цифру:";
				chomp(my $answer=<>);
				print RED, BOLD, "\nВВЕДЕНО НЕВЕРНОЕ ЗНАЧЕНИЕ!\n\n", RESET and redo unless $answer=~m/^[0-5]$/;
				last if $answer == 0;
				$status=switch('zsnt=0,0,0') if $answer == 1;
				$status=switch('zsnt=0,0,1') if $answer == 2;
				$status=switch('zsnt=0,0,2') if $answer == 3;
				$status=switch('zsnt=1,0,0') if $answer == 4;
				$status=switch('zsnt=2,0,0') if $answer == 5;
				print RED, $error, RESET unless $status;
				print "OK\n" if $status == 1;
				last;
			}
	}elsif($par=~/network/i){# сканирование сети и возможность ручного выбора оператора если есть доступны(й|е)
		print "ждите...\n";
		my $operators=info('COPS=?', qr/^\+COPS: (.+$)/);
		my($i, @operators, @available);
		foreach (split /,?\((\d.+?\d)\),?/, $operators){$i++;push @operators, $_ if $i%2 == 0;}
			foreach (@operators){
				my( $stat, $operator)=$_=~m/(^\d),".+?","(.+?)","/;
				push @available, $operator if $stat eq '1';
				$stat="unknown" unless $stat;
				$stat="доступен" if $stat eq '1';
				$stat="текущий" if $stat eq '2';
				$stat="запрещен" if $stat eq '3';
				printf "%-20s%s\n", $operator, $stat;
			}
			if(@available){#если есть доступны(й|е) операторы
CYCLE_1:
				while(1){
					my $i=0;
					print BOLD, "доступные операторы:\n", RESET;
					map{print ++$i."  ".$_."\n"}(@available);
					print "или\n0  выход\nваш выбор? введите цифру:";
					chomp(my $answer=<>);
					print RED, BOLD, "Введено неверное значение!\n", RESET and redo if $answer=~m/^\D$/ && $answer > scalar @available;
					last if $answer == 0;
					system("clear");
						while(1){
							print << 'SAY';
Нажмите ENTER для продолжения, при этом при неудачной регистрации
модем "перескочит" на родную сеть
 
или для подключения в ручном режиме наберите:
SAY
							 print BOLD, "m\n", RESET;
							 print << 'SAY';
в дальнейшем при следующем подключении модема необходимо
будет явно выбрать сеть или изменить режим поиска сети.
 
или: 0  для выхода
 
SAY
							print "ваш выбор? ";
							chomp(my $answer2=<>);
							print RED, BOLD, "Введено неверное значение!\n", RESET and redo unless $answer2=~m/^m$|^0$|^$/i;
							last CYCLE_1 if $answer2 eq '0';
							my $mode=4;
							$mode=1 if $answer2;
							print RED, BOLD,"\nОШИБКА! НЕУДАЛОСЬ ПОДКЛЮЧИТЬСЯ\n\n", RESET and next CYCLE_1 unless switch("cops=$mode,1,$available[$answer-1]", 'ждите...');
							print RED, BOLD, "\nОШИБКА! НЕУДАЛОСЬ ПОДКЛЮЧИТЬСЯ, ПЕРЕКЛЮЧЕНИЕ НА РОДНУЮ СЕТЬ...\n\n", RESET and next CYCLE_1 unless info('cops?', qr/^\+COPS: [\d,]+"(.+?)"/) eq $available[$answer-1];
							print BOLD, "OK\n", RESET and last CYCLE_1;
						}
				}
			}
	}else{# ussd команда
		my $count;
		while(1){
		if(info('zoprt?', qr/^\+ZOPRT: (\d)/) eq '5' && !$sim_status){
			my $operator=operator();#оператор сети
			print RED, "сеть недоступна\n", RESET and exit unless $operator; #  проверка зарегистрирован ли модем в сети
			my $sig=info('CSQ', qr/\+CSQ: (\d+?),\d{2}/);
			print "$operator ".sig($sig)."\tзапрос отправлен, ждите...\n";#оператор+уровень сигнала сети
			my $answer=ussd($par);
   		print "$answer\n" if $answer;
			last;
		}else{
			last if $sim_status;
			print RED, "неудача, выход\n", RESET and last if $count;
			print RED, "неудача, повтор...\n", RESET;
			sleep 5;
			$count++;
		}
		}
	}
	close USB;
}else{
	print RED, BOLD, "не указана команда", RESET, "\n\n$help";
}
####
sub ussd{# USSD команды
my $st;
print USB "at+cusd=1,${_[0]},15\015\012";
{eval{
local $SIG{ALRM}=sub{ die "Timeout"; };#таймаут
alarm($timeout);
	while(<USB>){
#	   print;
		$st=$1 if m/^\+CUSD:.+?"(.+?)",[0-9]{2}/;
		last if m/CMTI/;
		$st="NO CARRIER\n" if m/NO CARRIER/;
		$st=$error if m/^ERROR/;
		last if $st;
	}
alarm(0);
}}
	if($st){
		if($st=~/^[0-9A-F]+$/i){
			$st = pack("H*", $st);
			encode("ucs-2",$st);
			from_to($st,"ucs-2","utf-8");
		}
		return $st;
	}elsif($@ =~ /Timeout/){
		return $exit_;
	}else{
		return;
	}
}
####
sub info{#
my $info;
my $pattern=$_[1];
print USB "at+${_[0]}\015\012";
{eval{
local $SIG{ALRM}=sub{ die "Timeout"; };#таймаут
${_[0]}=~m/COPS=/i ? alarm($timeout+40) : alarm($timeout);
	while(<USB>){
#print;
		last if m/^OK/;
		$info=$1 if m/$pattern/i;
		last if m/ERROR|^NO CARRIER/;
	}
alarm(0);
}}
print "$exit_\n" and return "" if $@ =~ /Timeout/;
$info ? return $info : return "";
}
####
sub switch{# изменение настроек модема
my $r="";
print "$_[1]\n" if $_[1];
print USB "at+${_[0]}\015\012";
{eval{
local $SIG{ALRM}=sub{ die "Timeout"; };#таймаут
${_[0]}=~m/COPS=/i ? alarm($timeout+40) : alarm($timeout);
	while(<USB>){
#print;
		$r=1 if m/^OK/;
		$r=0 if m/ERROR|^NO CARRIER/;
		last if $r || $r eq "0";
	}
alarm(0);
}}
print "$exit_\n" and return -1 if $@ =~ /Timeout/;
return $r;
}
####
sub sig{
return "неизвестно" unless $_[0];
$_[0] == 99 ? return "неизвестно" : return sprintf("%.0f", $_[0]*100/31)."%";
}
####
sub operator{#информация о сети
my $operator=info('cops?', qr/^\+COPS: [\d,]+"(.+?)"/);
$operator ? return "$operator ".info('zpas?', qr/^\+ZPAS: ("\w+?")/) : return "";
}
####
sub mode{#определение текущего режима поиска сети
my $mode=info('zsnt?', qr/^\+ZSNT: (\d,\d,\d)/);
	if($mode){
		return "автоматически, GSM+WCDMA\n" if $mode eq "0,0,0";
		return "автоматически, GSM+WCDMA, предпочтительно GSM\n" if $mode eq "0,0,1";
		return "автоматически, GSM+WCDMA, предпочтительно WCDMA\n" if $mode eq "0,0,2";
		return "автоматически, только GSM\n" if $mode eq "1,0,0";
		return "автоматически, только WCDMA\n" if $mode eq "2,0,0";
		return "вручную, GSM+WCDMA\n" if $mode eq "0,1,0";
		return "вручную. только GSM\n" if $mode eq "1,1,0";
		return "вручную, только WCDMA\n" if $mode eq "2,1,0";
	}
}
####
sub pin_puk{
system("clear");
my($m1, $m2, $m3, $pin_puk, $info);
	while($pin_puk=info('CPIN?', qr/(SIM PUK|SIM PIN)/)){
			if($pin_puk=~m/SIM PIN/i){
				print BOLD, "ВНИМАНИЕ! При вводе PIN кода будте предельно внимательны.\nУ вас 3 или менее попыток.", RESET, "\n0 - выход\n" unless $m1;
				$m1=1;
#				print "";
				my $answer=check_pin('Введите PIN код:');
				print RED, BOLD, "ERROR: incorrect password\n", RESET and next unless switch("cpin=$answer");
			}
			if($pin_puk=~m/SIM PUK/i){
				system("clear") unless $m3;$m3=1;
				print BOLD, "ВНИМАНИЕ! Вы исчерпали попытки ввода PIN кода.\nВведите восьмизначный PUK код, будте осторожны, количество попыток ограничено.", RESET, "\n0 - выход\n" unless $m2;
				$m2=1;
				print "Введите PUK код:";
				chomp(my $answer=<>);
				return if $answer eq "0";
				redo unless $answer;
				print RED, "введено некорректное значение! PUK код - восьмизначное число\n", RESET and redo unless $answer=~m/^\d{8}$/;
				my $answer2=check_pin('Введите новый PIN код:');
				print RED, BOLD, "ERROR: incorrect password\n", RESET and next unless switch("cpin=$answer,$answer2");
			}
	}
$info=info('CPIN?', qr/\+CPIN: ?(.+)$/);
$info=info('CPIN?', qr/\+CME ?(.+)$/) unless $info;
	if($info){
		print BOLD, "OK\n", RESET and return 1 if $info=~m/READY/i;
		print RED, "Unsupported, exit (PH-SIM PIN (SIM lock))\n", RESET and return if $info=~m/PH-SIM PIN/i;
		print RED, "Unsupported, exit (PH-NET PIN (Network personnalisation))\n", RESET and return if $info=~m/PH-NET PIN/i;
		print RED, "Unsupported, exit (SIM PIN2)\n", RESET and return if $info=~m/SIM PIN2/i;
		print RED, "Unsupported, exit (SIM PUK2)\n", RESET and return if $info=~m/SIM PUK2/i;
		print "$info\n" and return if $info=~m/ERROR/i;
	}else{
		print RED, BOLD, "ERROR\n", RESET and return;
	}
}
 
sub check_pin{
my $pin;
	while(1){
		print $_[0];
		chomp($pin=<>);
		exit if $pin eq "0";
		next unless $pin;
		print RED, "введено некорректное значение! PIN код: 4 - 8 цыфр\n", RESET and next unless $pin=~m/^\d{4,8}$/;
		last;
	}
return $pin;
}

Вскрываем

Изначально модем определяется как CD-ROM, на котором есть драйвера только под Windows (несмотря на заявленную поддержку MacOS X). После извлечения оного модем становится виден как 3 tty устройства, с которыми уже можно попробовать поработать, но разве нам нужны эти пляски?

Потому просто берем и отключаем cdrom командой:

./mf100.pl cdstop

Если скрипт ответил "OK", можно переткнуть модем и удостовериться, что больше нас ничто не беспокоит.

Звоним

Многие провайдеры используют коварный флаг, который разрывает соединение каждые 2-3 минуты и коварный билайн в их числе. Недуг лечится опять же, просто:

./mf100.pl modem

Кто как, а я для дозвона с незапамятных времен использую wvdial, ибо для ppp/pppd нужно писать много букав, а в kppp/gnome-ppp - много тыкать мышкой. Лень.

для wvdial используем такой конфиг (пользователь/пароль/точка доступа специфичны для вашего провайдера):

[Dialer beeline-3g]
Init1 = ATZ
Init2 = ATQ0 V1 E1 S0=0 &C1 &D2 +FCLASS=0
Init3 = AT+CGDCONT=1,"IP","home.beeline.ru"
Phone = *99#
ISDN = 0
Stupid Mode = 1
Auto Reconnect = 0
Idle Seconds = 0
Username = beeline
Password = beeline
Modem Type = USB Modem
Modem = /dev/ttyUSB2

теперь запускаем wvdial и радуемся интернету:

wvdial beeline-3g

Ссылки