Benjamin-Dobell / Heimdall

Heimdall is a cross-platform open-source tool suite used to flash firmware (aka ROMs) onto Samsung Galaxy devices.
MIT License
2.56k stars 584 forks source link

Can't use Samsung S6812B on linux #286

Open j-alonso opened 9 years ago

j-alonso commented 9 years ago

The current git version of Heimdall cannot access my device. The device in ODIN mode have VID:PID: 04E8:685D

Investigating this, I found: During the process of initialization, the device accepts configuration requests from the host computer. When this process finishes, the device enters a state waiting ONLY for handshake "ODIN" "LOKE". Any other request will fail and lock the device in a error state. diagram2

PROBLEMS:

1) ModemManager. The ModemManager send configuration requests to the USB causing the lock of the device.

To avoid this, the ModemManager can be deactivated before connecting the USB cable: su -c "systemctl stop ModemManager" Or the device can be blacklisted for the ModemManager.

2) Heimdall The call of _libusb_set_interface_altsetting send configuration requests causing the lock of the device. Suggestion: do not use _libusb_set_interface_altsetting. Since there is only one altsetting, I think is not necessary to call this function (need to investigate more!). The call of _libusb_get_string_descriptorascii send configuration requests causing the lock of the device. Suggestion: do not use.


Simple quick fix: (only comment out source)

diff --git a/heimdall/source/BridgeManager.cpp b/heimdall/source/BridgeManager.cpp
index b7bff3d..4bd4042 100644
--- a/heimdall/source/BridgeManager.cpp
+++ b/heimdall/source/BridgeManager.cpp
@@ -128,23 +128,23 @@ int BridgeManager::FindDeviceInterface(void)
    {
        unsigned char stringBuffer[128];

-       if (libusb_get_string_descriptor_ascii(deviceHandle, deviceDescriptor.iManufacturer,
-           stringBuffer, 128) >= 0)
-       {
-           Interface::Print("      Manufacturer: \"%s\"\n", stringBuffer);
-       }
-
-       if (libusb_get_string_descriptor_ascii(deviceHandle, deviceDescriptor.iProduct,
-           stringBuffer, 128) >= 0)
-       {
-           Interface::Print("           Product: \"%s\"\n", stringBuffer);
-       }
-
-       if (libusb_get_string_descriptor_ascii(deviceHandle, deviceDescriptor.iSerialNumber,
-           stringBuffer, 128) >= 0)
-       {
-           Interface::Print("         Serial No: \"%s\"\n", stringBuffer);
-       }
+//     if (libusb_get_string_descriptor_ascii(deviceHandle, deviceDescriptor.iManufacturer,
+//         stringBuffer, 128) >= 0)
+//     {
+//         Interface::Print("      Manufacturer: \"%s\"\n", stringBuffer);
+//     }
+//
+//     if (libusb_get_string_descriptor_ascii(deviceHandle, deviceDescriptor.iProduct,
+//         stringBuffer, 128) >= 0)
+//     {
+//         Interface::Print("           Product: \"%s\"\n", stringBuffer);
+//     }
+//
+//     if (libusb_get_string_descriptor_ascii(deviceHandle, deviceDescriptor.iSerialNumber,
+//         stringBuffer, 128) >= 0)
+//     {
+//         Interface::Print("         Serial No: \"%s\"\n", stringBuffer);
+//     }

        Interface::Print("\n            length: %d\n", deviceDescriptor.bLength);
        Interface::Print("      device class: %d\n", deviceDescriptor.bDeviceClass);
@@ -261,13 +261,13 @@ bool BridgeManager::SetupDeviceInterface(void)
 {
    Interface::Print("Setting up interface...\n");

-   int result = libusb_set_interface_alt_setting(deviceHandle, interfaceIndex, altSettingIndex);
+// int result = libusb_set_interface_alt_setting(deviceHandle, interfaceIndex, altSettingIndex);

-   if (result != LIBUSB_SUCCESS)
-   {
-       Interface::PrintError("Setting up interface failed!\n");
-       return (false);
-   }
+// if (result != LIBUSB_SUCCESS)
+// {
+//     Interface::PrintError("Setting up interface failed!\n");
+//     return (false);
+// }

    Interface::Print("\n");
    return (true);
j-alonso commented 9 years ago

Complementing the report, the test sequence used was:

When the device enters "ERROR STATE" it responds with: URB status: No such file or directory (-ENOENT) -2 The device must be powered off to be used again.

fedora 22 kernel-4.1.4-200.fc22.x86_64 libusbx-1.0.19-2.fc22.x86_64 libusbx-devel-1.0.19-2.fc22.x86_64

villavic commented 8 years ago

This is the correct RCA of all the reported issues on error 110 and similar for many devices. Running off master it can be easily tested by only commenting out the set_interface_alt_setting call and running without --verbose (as get_string_descriptor_ascii locks the device and now it's only called when running with --verbose). As an alternative to just removing that piece of code, I just put an

    if (altSettingIndex == 0)
        return (true);

before set_interface_alt_setting, instead. Tested on Gentoo running kernel 4.3.3 and libusb-1.0.20 against a Samsung GT-S5367.

jkkoski commented 8 years ago

I can confirm that this works with Samsung Galaxy Trend Plus (GT-S7580).

j-alonso commented 8 years ago

Investigating at source level (libusb and kernel usb), I found:

Linux kernel have a module called "cdc_acm" that is automatically called when the Samsung Mobile Phone in ODIN mode is plugged. This module is used for USB modems and ISDN adapters. It creates a character device /dev/ttyACM* and sends "Set Line Coding" to the device. Then a system service called ModemManager run on /dev/ttyACM* to provide unified high level API for communicating with the modem.

I suppose that all Samsung Mobile Phones in ODIN mode are recognized as "CDC ACM" and the module "cdc_acm" is always called. To verify this for your phone check the system log for a line with: "cdc_acm 2-1.2:1.0: ttyACM0: USB ACM device"

Note: "Set Line Coding" is not sent when the module "cdc_acm" is not called and Heimdall uses "libusb".

Problem: All phones in ODIN mode receive the command "Set Line Coding", but only a subset (GT-S6812B, GT-S5367, GT-S7580 ...) locks if some "libusb" calls are called like: libusb_set_interface_alt_setting and libusb_get_string_descriptor_ascii. These are described in more detail at the start of the thread.

The devices not affected by "Set Line Coding" can have problems with ModemManager. Depending of timing, ModemManager can run simultaneously with Heimdall. See @kynan comment in issue #228. He propose a "blacklist" for ModemManager.

I think that a solution would be:

~~Blacklist the Samsung devices in ODIN mode in the module "cdc_acm". I send patches to the maintainer of cdc_acm to blacklist the 3 devices used:~~ http://marc.info/?l=linux-usb&m=145185842102902&w=2 @Benjamin-Dobell ~~There is a problem with 2 devices. Do you have more information about these devices?~~ ~~This solution works for new kernels. Since there is no ttyACM device, there is no problem with ModemManager.~~

~~For old kernels, the user can temporarily blacklist the module "cdc_acm" using this instructions:~~

I think that a solution would be:

The user can temporarily blacklist the module "cdc_acm" using this instructions:

  echo "blacklist cdc_acm" >/etc/modprobe.d/cdc_acm-blacklist.conf
  rmmod cdc_acm  # ignore ERROR is not currently loaded

Note: Since there is no ttyACM device, there is no problem with ModemManager.

villavic commented 8 years ago

There is no need to mess around with the cdc_acm module and ModemManager. There's a proper method to make ModemManager ignore an interface and that is setting:

ENV{ID_MM_DEVICE_IGNORE}="1"

in the udev rule for the devices. Heimdall already installs such an udev rule to set the permissions of the device's character files in /dev , it's just a matter of adding this, such as:

SUBSYSTEM=="usb", ATTR{idVendor}=="04e8", ATTR{idProduct}=="685d", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1"

At least on my testing, with the previously mentioned changes (skipping set_interface_alt_setting and running without --verbose) having both ModemManager running and cdc_acm loaded doesn't affect Heimdall flashing at all.