ZTE MF180
Сабж
Билайновский модем, куплен в августе 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
Ссылки
- источник вдохновения и скрипта - там все издевательства производятся над MF100; домен в данный момент на парковке