diff options
Diffstat (limited to 'lib/network/connection/cellular_card.pm')
-rw-r--r-- | lib/network/connection/cellular_card.pm | 278 |
1 files changed, 278 insertions, 0 deletions
diff --git a/lib/network/connection/cellular_card.pm b/lib/network/connection/cellular_card.pm new file mode 100644 index 0000000..0fc3fbf --- /dev/null +++ b/lib/network/connection/cellular_card.pm @@ -0,0 +1,278 @@ +package network::connection::cellular_card; + +use base qw(network::connection::cellular); + +use strict; +use common; + +my $wrong_pin_error = N_("Wrong PIN number format: it should be 4 digits."); + +sub get_type_name() { N("GPRS/Edge/3G") } +sub _get_type_icon() { 'cellular' } +sub get_devices() { + require detect_devices; + my @maybe_usbserial_modules = ('usbserial_generic', 'unknown'); + my @serial = grep { $_->{description} =~ /GPRS|EDGE|3G|UMTS|H.DPA|CDMA/i } detect_devices::matching_driver('serial_cs', 'usbserial', @maybe_usbserial_modules); + member($_->{driver}, @maybe_usbserial_modules) and $_->{driver} = 'usbserial' foreach @serial; + #- cdc_acm can not be listed directly in network/cellular, it is already in network/isdn + @serial, detect_devices::probe_category('network/cellular'), detect_devices::matching_driver('cdc_acm'); +} +sub get_metric { 40 } + +sub get_packages { 'comgt', 'ppp' } + +sub handles_ifcfg { + my ($_class, $ifcfg) = @_; + exists $ifcfg->{CELLULAR_CID}; +} + +my @thirdparty_settings = ( + { + name => 'nozomi', + description => 'Option GlobeTrotter 3G/EDGE and FUSION+', + url => 'http://www.pharscape.org/', + kernel_module => 1, + }, + { + name => 'hso', + description => 'Option High Speed Mobile Devices', + url => 'http://www.pharscape.org/', + kernel_module => 1, + tools => { + package => 'hso-rezero', + test_file => '/usr/sbin/rezero', + }, + }, +); + +sub get_thirdparty_settings() { + \@thirdparty_settings; +} + +sub guess_hardware_settings { + my ($self) = @_; + $self->{hardware}{pin} ||= chomp_(cat_("/etc/sysconfig/network-scripts/pin-" . $self->get_interface)); +} + +sub get_interface { + my ($self) = @_; + $self->get_driver eq "hso" ? + "hso0" : + "ppp0"; +} + +sub get_tty_device { + my ($self) = @_; + + my $tty_interface = 0; + my %udev_env = map { if_(/^([^=]*)=(.*)$/, $1 => $2) } chomp_(run_program::get_stdout("udevadm info --query=property --path=$self->{device}{sysfs_device}")); + + if ($udev_env{USB_MODEM_INTERFACE}) { + my $dev_sys_path = $self->{device}->{sysfs_device}; + my $cfg = chomp_(cat_($dev_sys_path . "/bConfigurationValue")); + my $tty_usb = basename(glob_("$dev_sys_path/" . + $self->{device}->{pci_bus} . "-" . + ($self->{device}->{usb_port} + 1) . ":$cfg." . + $udev_env{USB_MODEM_INTERFACE} . "/ttyUSB*")); + if ($tty_usb) { + $tty_usb =~ s/ttyUSB//; + $tty_interface = $tty_usb; + } + } else { + # if no usb interface for tty port given, use first one + my @intfs = glob_("$self->{device}->{sysfs_device}/" . + $self->{device}->{pci_bus} . "-" . + ($self->{device}->{usb_port} + 1) . ":*"); + foreach (@intfs) { + my $tty_usb = basename(glob_("$_/tty*")); + if ($tty_usb) { + $tty_usb =~ s/tty[a-zA-Z]*//; + $tty_interface = $tty_usb; + last; + } + } + } + + $self->{device}{device} ? + "/dev/" . $self->{device}{device} : + $self->get_driver eq "nozomi" ? + "/dev/noz" . $tty_interface : + $self->get_driver eq "cdc_acm" ? + "/dev/ttyACM" . $tty_interface : + $self->get_driver eq "hso" ? + "/dev/ttyHS" . $tty_interface : + "/dev/ttyUSB" . $tty_interface; +} + +sub get_control_device { + my ($self) = @_; + my $tty_device = $self->get_tty_device; + if ($tty_device eq "/dev/ttyUSB0") { + for my $id (2, 1) { + my $usb_control_device = "/dev/ttyUSB" . $id; + return $usb_control_device if -e $usb_control_device; + } + } + $tty_device; +} + +sub network_scan_is_slow() { 1 } +sub check_hardware_is_slow() { 1 } +sub has_unique_network() { 1 } + +sub get_networks { + my ($self) = @_; + my $cmd = 'comgt -d ' . $self->get_control_device; + my ($network, $state) = `$cmd reg` =~ /^Registered on \w+ network: "(.*)",(\d+)$/m; + my ($strength) = `$cmd sig` =~ /^Signal Quality:\s+(\d+),\d+$/; + $self->probed_networks; + $self->{networks} = $network && { + $network => { + name => $network, + signal_strength => $strength * 5, + current => $state == 2, + } + }; +} + +sub get_hardware_settings { + my ($self) = @_; + [ { label => N("PIN number (4 digits). Leave empty if PIN is not required."), val => \$self->{hardware}{pin}, hidden => 1 } ]; +} + +sub check_hardware_settings { + my ($self) = @_; + if ($self->{hardware}{pin} !~ /(^$|^[0-9]{4}$)/) { + $self->{hardware}{error} = translate($wrong_pin_error); + return 0; + } + 1; +} + +sub get_peer_default_options { + my ($self) = @_; + $self->SUPER::get_peer_default_options, + "noccp", # disable CCP to avoid warning messages + "debug"; +} + +sub build_peer { + my ($self) = @_; + $self->SUPER::build_peer; + #- don't run comgt for now, it hangs on ttyUSB0 devices when run from pppd + #- $self->{access}{peer}->{init} = "comgt -d $dev < $pin_file" +} + +sub set_ppp_settings { + my ($self) = @_; + $self->{access}{no_dial} = $self->get_driver eq "hso"; + $self->{access}{cid} = 3; + + $self->{access}{at_commands} = [ + "AT+CPIN?", + # Set +CGEE to 2 + "AT+CMEE=2", + qq(AT+CGDCONT=$self->{access}{cid},"IP","$self->{access}{apn}"), + # Setup +CGEQREG (QoS, don't set it for now) + # qq(AT+CGEQREQ=3,3,64,384,0,0,2,0,"0E0","0E0",3,0,0), + # Attached to network, will return 1 + "AT+CGATT?", + if_($self->get_driver eq "hso", ""), + ]; + + $self->SUPER::set_ppp_settings; +} + +sub write_settings { + my ($self) = @_; + + my $interface = $self->get_interface; + my $pin_file = "/etc/sysconfig/network-scripts/pin-$interface"; + output_with_perm($pin_file, 0600, $self->{hardware}{pin} . "\n"); + + $self->SUPER::write_settings; +} + +sub prepare_device { + my ($self) = @_; + + my $driver = $self->get_driver; + require modules; + my $modules_conf = ref($::o) && $::o->{modules_conf} || modules::any_conf->read; + modules::load_and_configure($modules_conf, $driver, + if_($driver eq 'usbserial', join( + ' ', + "vendor=0x" . sprintf("%04x", $self->{device}{vendor}), + "product=0x" . sprintf("%04x", $self->{device}{id})))); + $modules_conf->write if !ref $::o; + sleep 2 if $driver eq 'usbserial'; +} + +sub check_device { + my ($self) = @_; + + my $dev = $self->get_tty_device; + if (! -e $dev) { + $self->{device}{error} = N("Unable to open device %s", $dev); + return; + } + + 1; +} + +sub check_hardware { + my ($self) = @_; + to_bool(run_program::rooted($::prefix, 'comgt', '>', '/dev/null', '-d', $self->get_control_device, 'PIN')); +} + +sub configure_hardware { + my ($self) = @_; + + my $device_ready = 0; + + require IPC::Open2; + require IO::Select; + require c; + use POSIX qw(:errno_h); + + my $pid = IPC::Open2::open2(my $cmd_out, my $cmd_in, 'comgt', '-d', $self->get_control_device); + common::nonblock($cmd_out); + my $selector = IO::Select->new($cmd_out); + my $already_entered_pin; + + while ($selector->can_read) { + local $_; + my $rv = sysread($cmd_out, $_, 512); + $rv == 0 and $selector->remove($cmd_out); + if (/^\s*\*\*\*SIM ERROR\*\*\*/m) { + $self->{hardware}{error} = N("Please check that your SIM card is inserted."); + last; + } elsif (/^Enter PIN number:/m) { + $self->{hardware}{pin} or last; + if ($already_entered_pin) { + $self->{hardware}{error} = translate($wrong_pin_error); + last; + } + $already_entered_pin = 1; + print $cmd_in $self->{hardware}{pin} . "\n"; + } elsif (/^ERROR entering PIN/m) { + $self->{hardware}{error} = N("You entered a wrong PIN code. +Entering the wrong PIN code multiple times may lock your SIM card!"); + last; + } elsif (/^Waiting for Registration/m) { + #- the card seems to be reset if comgt is killed right here, wait a bit + sleep 1; + #- don't wait the full scan + $device_ready = 1; + last; + } + } + kill 'TERM', $pid; + close($cmd_out); + close($cmd_in); + waitpid($pid, 0); + + $device_ready; +} + +1; |