2012-08-08 17:02:31 +02:00
/*
0xFFFF - Open Free Fiasco Firmware Flasher
Copyright (C) 2012 Pali Rohár <pali.rohar@gmail.com>
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/>.
*/
2016-12-06 23:11:47 +01:00
/* Enable RTLD_DEFAULT for glibc */
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
2012-08-08 17:02:31 +02:00
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
2012-09-23 13:51:29 +02:00
#include <ctype.h>
2013-09-07 22:49:08 +02:00
#include <signal.h>
2016-12-06 23:11:47 +01:00
#include <dlfcn.h>
2012-08-08 17:02:31 +02:00
2016-12-06 22:39:44 +01:00
#include <usb.h>
#ifdef __linux__
#ifdef LIBUSB_HAS_DETACH_KERNEL_DRIVER_NP
#include <sys/ioctl.h>
#endif
#endif
2014-11-09 21:51:51 +01:00
2012-08-09 18:25:48 +02:00
#include "global.h"
2012-08-08 17:02:31 +02:00
#include "device.h"
2012-08-09 16:13:43 +02:00
#include "usb-device.h"
2012-08-10 12:03:30 +02:00
#include "printf-utils.h"
2012-09-23 14:03:28 +02:00
#include "nolo.h"
#include "cold-flash.h"
2012-11-15 15:23:45 +01:00
#include "mkii.h"
2012-08-09 18:25:48 +02:00
2012-08-08 17:02:31 +02:00
static struct usb_flash_device usb_devices [] = {
2016-03-09 00:15:25 +01:00
{ 0x0421 , 0x0096 , - 1 , - 1 , - 1 , FLASH_DISK , { DEVICE_RX_44 , 0 } },
2016-06-27 17:24:51 +02:00
{ 0x0421 , 0x0105 , 2 , 1 , - 1 , FLASH_NOLO , { DEVICE_SU_18 , DEVICE_RX_44 , DEVICE_RX_48 , DEVICE_RX_51 , DEVICE_RM_680 , 0 } },
{ 0x0421 , 0x0106 , 0 , - 1 , - 1 , FLASH_COLD , { DEVICE_RX_51 , DEVICE_RM_680 , 0 } },
2016-03-09 00:15:25 +01:00
{ 0x0421 , 0x0189 , - 1 , - 1 , - 1 , FLASH_DISK , { DEVICE_RX_48 , 0 } },
{ 0x0421 , 0x01c7 , - 1 , - 1 , - 1 , FLASH_DISK , { DEVICE_RX_51 , 0 } },
2016-06-27 17:24:51 +02:00
{ 0x0421 , 0x01c8 , 1 , 1 , - 1 , FLASH_MKII , { DEVICE_RX_51 , DEVICE_RM_680 , 0 } },
2016-03-09 00:15:25 +01:00
{ 0x0421 , 0x0431 , - 1 , - 1 , - 1 , FLASH_DISK , { DEVICE_SU_18 , DEVICE_RX_34 , 0 } },
{ 0x0421 , 0x04c3 , - 1 , - 1 , - 1 , FLASH_DISK , { DEVICE_RX_34 , 0 } },
2012-08-08 17:02:31 +02:00
{ 0x0421 , 0x3f00 , 2 , 1 , - 1 , FLASH_NOLO , { DEVICE_RX_34 , 0 } },
};
static const char * usb_flash_protocols [] = {
[ FLASH_NOLO ] = "NOLO" ,
[ FLASH_COLD ] = "Cold flashing" ,
2014-11-18 23:26:19 +01:00
[ FLASH_MKII ] = "Mk II protocol" ,
2014-11-18 23:31:28 +01:00
[ FLASH_DISK ] = "RAW disk" ,
2012-08-08 17:02:31 +02:00
};
const char * usb_flash_protocol_to_string ( enum usb_flash_protocol protocol ) {
2013-04-07 14:23:26 +02:00
if ( protocol >= sizeof ( usb_flash_protocols ) / sizeof ( usb_flash_protocols [ 0 ]) )
2012-08-08 17:02:31 +02:00
return NULL ;
return usb_flash_protocols [ protocol ];
}
2012-08-09 19:43:22 +02:00
static void usb_flash_device_info_print ( const struct usb_flash_device * dev ) {
2012-08-08 17:02:31 +02:00
int i ;
2012-08-11 14:40:56 +02:00
PRINTF_ADD ( "USB device: %s" , device_to_string ( dev -> devices [ 0 ]));
2012-08-08 17:02:31 +02:00
for ( i = 1 ; dev -> devices [ i ]; ++ i )
2012-08-09 18:25:48 +02:00
PRINTF_ADD ( "/%s" , device_to_string ( dev -> devices [ i ]));
2012-08-08 17:02:31 +02:00
2012-08-09 18:25:48 +02:00
PRINTF_ADD ( " (%#04x:%#04x) in %s mode" , dev -> vendor , dev -> product , usb_flash_protocol_to_string ( dev -> protocol ));
2012-08-08 17:02:31 +02:00
}
2016-12-06 22:39:44 +01:00
static void usb_reattach_kernel_driver ( usb_dev_handle * udev , int interface ) {
#ifdef __linux__
#ifdef LIBUSB_HAS_DETACH_KERNEL_DRIVER_NP
struct {
int ifno ;
int ioctl_code ;
void * data ;
} command = {
. ifno = interface ,
. ioctl_code = _IO ( 'U' , 23 ),
. data = NULL ,
};
2014-11-09 21:51:51 +01:00
2016-05-12 00:18:02 +02:00
if ( interface < 0 )
return ;
2016-12-06 22:39:44 +01:00
usb_release_interface ( udev , interface );
ioctl ( * (( int * ) udev ), _IOWR ( 'U' , 18 , command ), & command );
#endif
#endif
2014-11-09 21:51:51 +01:00
}
2016-12-06 22:39:44 +01:00
static void usb_descriptor_info_print ( usb_dev_handle * udev , struct usb_device * dev , char * product , size_t size ) {
2012-08-11 14:40:56 +02:00
char buf [ 1024 ];
char buf2 [ 1024 ];
unsigned int x ;
int ret ;
int i ;
2012-09-23 13:51:29 +02:00
memset ( buf , 0 , sizeof ( buf ));
2016-12-06 22:39:44 +01:00
usb_get_string_simple ( udev , dev -> descriptor . iProduct , buf , sizeof ( buf ));
2012-08-11 14:40:56 +02:00
PRINTF_LINE ( "USB device product string: %s" , buf [ 0 ] ? buf : "(not detected)" );
PRINTF_END ();
2012-09-23 14:01:20 +02:00
if ( product && buf [ 0 ] )
strncpy ( product , buf , size );
2012-09-23 13:51:29 +02:00
memset ( buf , 0 , sizeof ( buf ));
2012-09-22 23:20:01 +02:00
memset ( buf2 , 0 , sizeof ( buf2 ));
2016-12-06 22:39:44 +01:00
ret = usb_get_string_simple ( udev , dev -> descriptor . iSerialNumber , buf , sizeof ( buf ));
2012-09-23 13:51:29 +02:00
if ( ! isalnum ( buf [ 0 ]) )
buf [ 0 ] = 0 ;
2012-08-11 14:40:56 +02:00
for ( i = 0 ; i < ret ; i += 2 ) {
sscanf ( buf + i , "%2x" , & x );
if ( x > 32 && x < 128 )
buf2 [ i / 2 ] = x ;
else {
buf2 [ 0 ] = 0 ;
break ;
}
}
2012-09-23 13:51:29 +02:00
if ( ! isalnum ( buf2 [ 0 ]) )
buf2 [ 0 ] = 0 ;
2012-08-11 15:24:46 +02:00
PRINTF_LINE ( "USB device serial number string: %s" , buf2 [ 0 ] ? buf2 : ( buf [ 0 ] ? buf : "(not detected)" ));
2012-08-11 14:40:56 +02:00
PRINTF_END ();
}
2016-12-06 22:39:44 +01:00
static struct usb_device_info * usb_device_is_valid ( struct usb_device * dev ) {
2012-08-08 17:02:31 +02:00
2016-12-06 22:39:44 +01:00
int i ;
2012-09-23 14:01:20 +02:00
char product [ 1024 ];
2012-08-08 17:02:31 +02:00
struct usb_device_info * ret = NULL ;
for ( i = 0 ; usb_devices [ i ]. vendor ; ++ i ) {
2016-12-06 22:39:44 +01:00
if ( dev -> descriptor . idVendor == usb_devices [ i ]. vendor && dev -> descriptor . idProduct == usb_devices [ i ]. product ) {
2012-08-08 17:02:31 +02:00
2012-08-10 21:23:19 +02:00
printf ( " \b\b " );
2012-08-09 18:25:48 +02:00
PRINTF_END ();
PRINTF_ADD ( "Found " );
2012-08-09 19:43:22 +02:00
usb_flash_device_info_print ( & usb_devices [ i ]);
2012-08-09 18:25:48 +02:00
PRINTF_END ();
2012-08-08 17:02:31 +02:00
2012-08-09 18:25:48 +02:00
PRINTF_LINE ( "Opening USB..." );
2016-12-06 22:39:44 +01:00
usb_dev_handle * udev = usb_open ( dev );
if ( ! udev ) {
PRINTF_ERROR ( "usb_open failed" );
2012-11-21 14:16:10 +01:00
fprintf ( stderr , " \n " );
2012-08-08 17:02:31 +02:00
return NULL ;
}
2012-09-23 14:01:20 +02:00
usb_descriptor_info_print ( udev , dev , product , sizeof ( product ));
2012-08-11 14:40:56 +02:00
2014-11-18 23:51:04 +01:00
if ( usb_devices [ i ]. interface >= 0 ) {
2016-12-06 22:39:44 +01:00
#ifdef LIBUSB_HAS_DETACH_KERNEL_DRIVER_NP
2014-11-18 23:51:04 +01:00
PRINTF_LINE ( "Detaching kernel from USB interface..." );
2016-12-06 22:39:44 +01:00
usb_detach_kernel_driver_np ( udev , usb_devices [ i ]. interface );
#endif
2012-08-08 17:02:31 +02:00
2014-11-18 23:51:04 +01:00
PRINTF_LINE ( "Claiming USB interface..." );
2016-12-06 22:39:44 +01:00
if ( usb_claim_interface ( udev , usb_devices [ i ]. interface ) < 0 ) {
PRINTF_ERROR ( "usb_claim_interface failed" );
2014-11-18 23:51:04 +01:00
fprintf ( stderr , " \n " );
usb_reattach_kernel_driver ( udev , usb_devices [ i ]. interface );
2016-12-06 22:39:44 +01:00
usb_close ( udev );
2014-11-18 23:51:04 +01:00
return NULL ;
}
2012-08-08 17:02:31 +02:00
}
if ( usb_devices [ i ]. alternate >= 0 ) {
2012-08-09 18:25:48 +02:00
PRINTF_LINE ( "Setting alternate USB interface..." );
2016-12-06 22:39:44 +01:00
if ( usb_set_altinterface ( udev , usb_devices [ i ]. alternate ) < 0 ) {
PRINTF_ERROR ( "usb_claim_interface failed" );
2012-11-21 14:16:10 +01:00
fprintf ( stderr , " \n " );
2014-11-09 21:51:51 +01:00
usb_reattach_kernel_driver ( udev , usb_devices [ i ]. interface );
2016-12-06 22:39:44 +01:00
usb_close ( udev );
2012-08-08 17:02:31 +02:00
return NULL ;
}
}
if ( usb_devices [ i ]. configuration >= 0 ) {
2012-08-09 18:25:48 +02:00
PRINTF_LINE ( "Setting USB configuration..." );
2016-12-06 22:39:44 +01:00
if ( usb_set_configuration ( udev , usb_devices [ i ]. configuration ) < 0 ) {
PRINTF_ERROR ( "usb_set_configuration failed" );
2012-11-21 14:16:10 +01:00
fprintf ( stderr , " \n " );
2014-11-09 21:51:51 +01:00
usb_reattach_kernel_driver ( udev , usb_devices [ i ]. interface );
2016-12-06 22:39:44 +01:00
usb_close ( udev );
2012-08-08 17:02:31 +02:00
return NULL ;
}
}
2012-08-09 19:43:22 +02:00
ret = calloc ( 1 , sizeof ( struct usb_device_info ));
2012-08-08 17:02:31 +02:00
if ( ! ret ) {
2012-08-09 18:25:48 +02:00
ALLOC_ERROR ();
2014-11-09 21:51:51 +01:00
usb_reattach_kernel_driver ( udev , usb_devices [ i ]. interface );
2016-12-06 22:39:44 +01:00
usb_close ( udev );
2012-08-08 17:02:31 +02:00
return NULL ;
}
2016-03-09 00:15:59 +01:00
if ( strcmp ( product , "Nokia 770" ) == 0 || strcmp ( product , "Nokia 770 (Update mode)" ) == 0 )
2016-02-23 18:23:04 +01:00
ret -> device = DEVICE_SU_18 ;
2016-03-09 00:15:59 +01:00
else if ( strcmp ( product , "Nokia N800 Internet Tablet" ) == 0 || strcmp ( product , "Nokia N800 (Update mode)" ) == 0 )
ret -> device = DEVICE_RX_34 ;
else if ( strcmp ( product , "Nokia N810 Internet Tablet" ) == 0 || strcmp ( product , "Nokia N810 (Update mode)" ) == 0 )
ret -> device = DEVICE_RX_44 ;
else if ( strcmp ( product , "Nokia N810 Internet Tablet WiMAX Edition" ) == 0 || strcmp ( product , "Nokia-RX48 (Update mode)" ) == 0 )
ret -> device = DEVICE_RX_48 ;
else if ( strcmp ( product , "N900 (Storage Mode)" ) == 0 || strcmp ( product , "Nokia N900 (Update mode)" ) == 0 || strcmp ( product , "N900 (PC-Suite Mode)" ) == 0 )
2012-09-23 14:01:20 +02:00
ret -> device = DEVICE_RX_51 ;
2016-06-27 17:24:51 +02:00
else if ( strcmp ( product , "Sync Mode" ) == 0 )
ret -> device = DEVICE_RM_680 ;
2016-03-09 00:15:59 +01:00
else if ( strcmp ( product , "Nokia USB ROM" ) == 0 )
ret -> device = DEVICE_ANY ;
2012-09-23 14:01:20 +02:00
else
ret -> device = DEVICE_UNKNOWN ;
if ( device_to_string ( ret -> device ) )
PRINTF_LINE ( "Detected USB device: %s" , device_to_string ( ret -> device ));
else
PRINTF_LINE ( "Detected USB device: (not detected)" );
PRINTF_END ();
2016-03-09 00:15:59 +01:00
if ( ! noverify && ret -> device == DEVICE_UNKNOWN ) {
ERROR ( "Device detection failed" );
fprintf ( stderr , " \n " );
usb_reattach_kernel_driver ( udev , usb_devices [ i ]. interface );
2016-12-06 22:39:44 +01:00
usb_close ( udev );
2016-03-09 00:15:59 +01:00
free ( ret );
return NULL ;
}
if ( ! noverify && ret -> device != DEVICE_ANY ) {
2012-09-23 14:01:20 +02:00
enum device * device ;
for ( device = usb_devices [ i ]. devices ; * device ; ++ device )
if ( * device == ret -> device )
break ;
if ( ! * device ) {
ERROR ( "Device mishmash" );
2012-11-21 14:16:10 +01:00
fprintf ( stderr , " \n " );
2014-11-09 21:51:51 +01:00
usb_reattach_kernel_driver ( udev , usb_devices [ i ]. interface );
2016-12-06 22:39:44 +01:00
usb_close ( udev );
2014-09-29 00:44:19 +02:00
free ( ret );
2012-09-23 14:01:20 +02:00
return NULL ;
}
}
ret -> hwrev = - 1 ;
2012-08-09 19:43:22 +02:00
ret -> flash_device = & usb_devices [ i ];
ret -> udev = udev ;
2012-08-08 17:02:31 +02:00
break ;
}
}
return ret ;
}
2016-12-06 22:39:44 +01:00
static struct usb_device_info * usb_search_device ( struct usb_device * dev , int level ) {
int i ;
struct usb_device_info * ret = NULL ;
if ( ! dev )
return NULL ;
ret = usb_device_is_valid ( dev );
if ( ret )
return ret ;
for ( i = 0 ; i < dev -> num_children ; i ++ ) {
ret = usb_search_device ( dev -> children [ i ], level + 1 );
if ( ret )
break ;
}
return ret ;
}
2013-09-07 22:49:08 +02:00
static volatile sig_atomic_t signal_quit ;
static void signal_handler ( int signum ) {
signal_quit = 1 ;
( void ) signum ;
}
2012-08-09 18:29:13 +02:00
struct usb_device_info * usb_open_and_wait_for_device ( void ) {
2012-08-08 17:02:31 +02:00
2016-12-06 22:39:44 +01:00
struct usb_bus * bus ;
2012-08-08 17:02:31 +02:00
struct usb_device_info * ret = NULL ;
int i = 0 ;
2013-09-20 18:08:31 +02:00
void ( * prev )( int );
2012-08-08 17:02:31 +02:00
static char progress [] = { '/' , '-' , '\\' , '|' };
2016-12-06 23:11:47 +01:00
if ( dlsym ( RTLD_DEFAULT , "libusb_init" ) )
ERROR_RETURN ( "You are trying to use broken libusb-1.0 library (either directly or via wrapper) which has slow listing of usb devices. It cannot be used for flashing or cold-flashing. Please use libusb 0.1." , NULL );
2016-12-06 22:39:44 +01:00
usb_init ();
usb_find_busses ();
2012-08-08 17:02:31 +02:00
2012-08-11 14:40:56 +02:00
PRINTF_BACK ();
printf ( " \n " );
2013-09-07 22:49:08 +02:00
signal_quit = 0 ;
prev = signal ( SIGINT , signal_handler );
while ( ! signal_quit ) {
2012-08-08 17:02:31 +02:00
2012-08-09 18:25:48 +02:00
PRINTF_LINE ( "Waiting for USB device... %c" , progress [ ++ i % sizeof ( progress )]);
2012-08-08 17:02:31 +02:00
2016-12-06 22:39:44 +01:00
usb_find_devices ();
for ( bus = usb_get_busses (); bus ; bus = bus -> next ) {
if ( bus -> root_dev )
ret = usb_search_device ( bus -> root_dev , 0 );
else {
struct usb_device * dev ;
for ( dev = bus -> devices ; dev ; dev = dev -> next ) {
ret = usb_search_device ( dev , 0 );
if ( ret )
break ;
}
}
2012-08-08 17:02:31 +02:00
if ( ret )
break ;
2016-12-06 22:39:44 +01:00
}
2016-01-23 16:38:17 +01:00
2012-08-08 17:02:31 +02:00
if ( ret )
break ;
2014-11-26 15:46:03 +01:00
SLEEP ( 0xc350 ); // 0.5s
2012-08-08 17:02:31 +02:00
}
2013-09-07 22:49:08 +02:00
if ( prev != SIG_ERR )
signal ( SIGINT , prev );
2012-08-09 18:25:48 +02:00
PRINTF_BACK ();
2012-08-11 14:40:56 +02:00
printf ( " \n " );
2012-08-08 17:02:31 +02:00
if ( ! ret )
return NULL ;
return ret ;
}
void usb_close_device ( struct usb_device_info * dev ) {
2016-12-07 11:56:37 +01:00
if ( dev -> flash_device -> protocol != FLASH_COLD )
usb_reattach_kernel_driver ( dev -> udev , dev -> flash_device -> interface );
2016-12-06 22:39:44 +01:00
usb_close ( dev -> udev );
2012-08-08 17:02:31 +02:00
free ( dev );
}
2012-09-23 14:03:28 +02:00
void usb_switch_to_nolo ( struct usb_device_info * dev ) {
printf ( " \n Switching to NOLO mode... \n " );
if ( dev -> flash_device -> protocol == FLASH_COLD )
leave_cold_flash ( dev );
else if ( dev -> flash_device -> protocol == FLASH_MKII )
2016-03-12 12:47:41 +01:00
mkii_reboot_device ( dev , 0 );
2012-09-23 14:03:28 +02:00
else if ( dev -> flash_device -> protocol == FLASH_DISK )
printf_and_wait ( "Unplug USB cable, turn device off, press ENTER and plug USB cable again" );
}
void usb_switch_to_cold ( struct usb_device_info * dev ) {
printf ( " \n Switching to Cold Flash mode... \n " );
2012-11-20 21:52:03 +01:00
2012-11-21 14:16:10 +01:00
if ( dev -> flash_device -> protocol == FLASH_NOLO )
nolo_reboot_device ( dev );
else if ( dev -> flash_device -> protocol == FLASH_MKII )
2016-03-12 12:47:41 +01:00
mkii_reboot_device ( dev , 0 );
2012-11-21 14:16:10 +01:00
else if ( dev -> flash_device -> protocol == FLASH_DISK )
2012-11-20 21:52:03 +01:00
printf_and_wait ( "Unplug USB cable, turn device off, press ENTER and plug USB cable again" );
2012-09-23 14:03:28 +02:00
}
2014-11-18 23:26:50 +01:00
void usb_switch_to_update ( struct usb_device_info * dev ) {
2012-09-23 14:03:28 +02:00
printf ( " \n Switching to Update mode... \n " );
if ( dev -> flash_device -> protocol == FLASH_COLD )
leave_cold_flash ( dev );
else if ( dev -> flash_device -> protocol == FLASH_NOLO )
nolo_boot_device ( dev , "update" );
2016-03-12 12:01:27 +01:00
else if ( dev -> flash_device -> protocol == FLASH_MKII && ! ( dev -> data & MKII_UPDATE_MODE ) )
2016-03-12 12:47:41 +01:00
mkii_reboot_device ( dev , 1 );
2012-09-23 14:03:28 +02:00
else if ( dev -> flash_device -> protocol == FLASH_DISK )
printf_and_wait ( "Unplug USB cable, turn device off, press ENTER and plug USB cable again" );
}
void usb_switch_to_disk ( struct usb_device_info * dev ) {
printf ( " \n Switching to RAW disk mode... \n " );
2012-11-15 15:23:45 +01:00
if ( dev -> flash_device -> protocol == FLASH_COLD )
leave_cold_flash ( dev );
else if ( dev -> flash_device -> protocol == FLASH_NOLO ) {
nolo_boot_device ( dev , NULL );
printf_and_wait ( "Wait until device start, choose USB Mass Storage Mode and press ENTER" );
2014-11-21 17:16:10 +01:00
} else if ( dev -> flash_device -> protocol == FLASH_MKII ) {
2016-03-12 12:01:27 +01:00
if ( dev -> data & MKII_UPDATE_MODE )
2016-03-12 12:47:41 +01:00
mkii_reboot_device ( dev , 0 );
2014-11-21 17:16:10 +01:00
else
printf_and_wait ( "Unplug USB cable, plug again, choose USB Mass Storage Mode and press ENTER" );
}
2012-09-23 14:03:28 +02:00
}