BLE文件传输和BLE升级协议

BLE具有距离远,功耗低等优点,广泛应用于智能家居通信,测距,定位,升级管理等方面。

使用BLE对智能家居因件升级。

根据Bluetooth 4.0 BLE部分协议:

BLE中物理层physical layer的传输速率是1Mbps,除了这个外,数据传输速率与包大小、CPU处理时间相关。

包结构:

1.总体结构:

preamble(1 Byte)+Access Address(4 Bytes)+ PDU +CRC(3 Bytes)

preamble = 10101010 or 01010101

Access Address = 0x8e89bedd6

2. 广播包

PDU =  Header(2 Bytes)+ Payload (37 Bytes max.)

Header: 1)0000 - connected undirected advertising event 可连接非定向广播事件

   2)0001- connected directed advertising event 可连接定向广播事件

   3)0010 -non-connected undirected advertising event 不可连接非定向广播事件

4)0011 - response to scan requestform scanner扫描请求响应

     5)0101 - connect request by initiator连接请求

6)0110 -connected directed advertisingevent 可发现非定向广播事件

  • 非定向广播包(Undirected advertising packets)

Payload  = AdvA (6 Bytes) + AdvData (31 Bytes max.); 

// AdvA  contains advertiser‘s public addressif TxAdd = 1, or a random address if TxAdd = 0; 

// AdvData advertising data;

  • 定向广播包(Directed advertising packets)

Payload  = AdvA (6 Bytes) + InitA(6 Bytes) ; 

// AdvA  contains advertiser‘s public addressif TxAdd = 1, or a random address if TxAdd = 0; 

// InitA contains initiator's address if RxAdd = 1,or a random address if RxAdd = 0;

 

3. 扫描请求及扫描响应

PDU =  Header(2 Bytes)+ Payload (37 Bytes max.)

Header: 1)0011 - scan request for further informationfrom advertiser 扫描请求

   2)0100- response to scan request from scanner 扫描响应

  • 扫描请求

Payload  = ScanA (6 Bytes) + AdvA(6 Bytes) ;

// ScanA  contains Scanner's public address if TxAdd= 1, or a random address if TxAdd = 0; 

// AdvA  contains advertiser‘s public addressif TxAdd = 1, or a random address if TxAdd = 0; 

  • 扫描响应

Payload  =  AdvA(6 Bytes) + ScanRspData(0~31Bytes);

// ScanRspData data from advertiser’s host; 

// AdvA  contains advertiser‘s public addressif TxAdd = 1, or a random address if TxAdd = 0; 

 

3. 连接请求

PDU =  Header(2 Bytes)+ Payload (34 Bytes)

Header:0101 - connect request by initiator

Payload =InitA(6 Bytes)+ AdvA(6bytes)+LLData(22 Bytes)

LLData 包含连接信息,详细结构参考bluetooth 4.0 协议。

4. LL 数据通道及控制包

PDU =  Header(2 Bytes)+ Payload (27 Bytes max.)

Header:详细说明参考bluetooth 4.0 协议;

  • LL 数据通道

Payload  = 0~27 bytes

  • LL 控制包

Payload = 1~23 Bytes

 

若只考虑蓝牙设备连接之后,评估数据传输速率

  • 最大包长度:preamble(1 Byte)+ Access Address(4 Bytes)+ PDU(29 Bytes) + CRC(3 Bytes)= 41 Bytes
  • 射频PHY传输速率 1Mbps

则传输有效payload 27字节耗时:41*8/1 = 328us

一个27字节的传输周期 :328 + 150 + 80 + 150 = 708 us,若能持续传输,即传输速度:38KB/s,这样显然功耗不会低,也不符合BLE协议规范,真正的传输速度受连接事件间隔和间隔内能传输数据包数目相关。

如果连接时间间隔设置到7.5ms(协议规定最小值),一个连接事件传输3个包,则传输速率大约是10.8kB/s,一个连接事件能传输几个包与连 接设备密切相关,看到很多CC2540实测数据,时间间隔7.5ms和一个传输时间3个包以上很多都达不到,51内核是其重要原因之一,在 http://processors.wiki.ti.com/index.php/CC2540_Data_Throughput有5.9KB/s,也有在论坛看到有40ms间隔,2个包,即1.4KB/s成功的案例,以后有时间我再实测下速度。



传输txt和字符文本

/******************************************************************************
 *
 *  Copyright (C) 2013 Broadcom Corporation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *  The offset of the second SS. For EEPROM, SS can be at 0x00, 0x100, 0x200, 0x400, 0x800, 0x1000 and 0x2000. 
 *  SS1 is almost always 0x00. So SS2 can be any of the others. Default SS2 we will use is 0x100. You may use any of the others if you want the EEPROM layout to be different.
 *  We will 'allocate' 256 bytes for SS1 (even though it takes up only 40 bytes leaving a 216 byte hole) and set up 
 *  SS2 at 0x100. Since we know that SS2 will be 40 bytes long, we can set up VS at 0x2C0 and of length 0x200 bytes,
 *  DS1 at 0x4C0 and DS2 at 0x84C0 leaving 0x8000 bytes for DS1 and 0x7B40 (about 31.5KB) for DS2.
 *  So, the layout in EEPROM will be as follows with the following offsets:

 *   # 0-----0x100-------0x2C0-----------0x4C0----------------------------0x84C0----------------------X
 *   # |  SS1  |     SS2   |       VS      |              DS1                |          DS2           |
 *   # X----------------------------------------------------------------------------------------------X


 * These are more or less constants of the OTAFU protocol. If you changed these IDs in the FW, use the ones the FW uses.
 **********************************************************************************************************************/

package com.broadcom.app.ota_sample;


import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.Arrays;
import java.util.UUID;
import android.util.Log;
import android.bluetooth.BluetoothGattCharacteristic;


public class B20732_OtaService {
	private static final String TAG = "OTA_UPdate";

	private static final int maxOtaDataSize = 16;               // That is the max size of data in the OTA PDU. 
	private short maxSSLen = 0x28;                              // The static section is always 40 bytes long.
	private short secondSSOffset = 0x100;   
	private short firstSSDsOffset = 0x4C0;

	
	private static final int eeprom_address = 0xFF0004C0;
	private int offset = 0, pack_id = 0, patch_size = 0;
	
	private String burnimg_path;   // The path of the burn image that will be generated when updating.
	
	private GattAppService mGattService = null;
    private BluetoothGattCharacteristic mUpdateValueCharact = null;
    private BluetoothGattCharacteristic mUpdateResultCharact = null;
	
	private BufferedReader  image_file_reader = null;
	private short last_length = 0;

	private static final byte ID_ENABLE_OTAFU = 0x70;
	private static final byte ID_SETUP_READ   = 0x71;
	private static final byte ID_READ         = 0x72;
	private static final byte ID_ERASE        = 0x73;
	private static final byte ID_WRITE        = 0x74;
	private static final byte ID_LAUNCH       = 0x75;
	
	public static final short UPDATE_STATUS_CONTINUE = 0x00;
	public static final short UPDATE_STATUS_COMPLETE_SUCCESS  = 0x01;
	public static final short UPDATE_STATUS_ENABLE_FAIL = 0x02;
	public static final short UPDATE_STATUS_WRITE_FAIL = 0x03;
	public static final short UPDATE_STATUS_LAUNCH_FAIL = 0x04;

	
    public static final UUID OTA_SERVICE_UUID          = UUID.fromString("0000E010-0000-1000-8000-00805f9b34fb");
    public static final UUID OTA_WRITE_CHARACTER_UUID  = UUID.fromString("0000E011-0000-1000-8000-00805f9b34fb");
    public static final UUID OTA_RESULT_CHARACTER_UUID = UUID.fromString("0000E012-0000-1000-8000-00805f9b34fb");
    
    private boolean data_send_error = false;
    private short fail_packet_num = 0;
    private short[] array_fail_packet;
	
	
	
	public B20732_OtaService( GattAppService Service, BluetoothGattCharacteristic Characteristic )
	{
		mGattService = Service;
		mUpdateValueCharact = Characteristic;

		if( mGattService != null  )
		{
            mUpdateResultCharact = mGattService.getCharacteristic( OTA_SERVICE_UUID, OTA_RESULT_CHARACTER_UUID );
        }
		
		array_fail_packet = new short[2000];
		for (short i=0;i<2000;i++)
			array_fail_packet[i] = (short)-1;
		
	}
	
	public Boolean Set_PatchFilePath( String path )
	{
	    Boolean result =false;

	    if( path != null )
	    {
    		burnimg_path = new String(path);

            if( burnimg_path != null )
            {
        		if( OpenImageFile( burnimg_path ))
        		{
        		    result = true;
        		}
    		}
		}
		
	    return result;
	}
	
	class OtaReport_T {
		byte operation_id;
		byte operation_result;
		short len;
		byte[] data;
		byte  checksum;
		
		public OtaReport_T( short length )
		{
		    if( length > 0 )
		    	data = new byte[length];
		    
		    operation_id = 0;
		    operation_result = 0;
		    len = length;
		    checksum = 0;
		}
		
		private String ByteArrayToDataString( byte[] data )
		{
			String out = null;
			
			if( data != null )
		        out = Arrays.toString( data );
			
			return out;
		}

		public byte Checksum()
		{
		    int tst_sum = 0;
		    int i = 0;

		    tst_sum = operation_id + operation_result + (len&0xFF) + (( len>>8)&0xFF);

		    if( len > 0 )
		    {
		        for( i=0;i<len;i++)
		        {
		            tst_sum += data[i];
		        }
		    }

		    return (byte) ((~tst_sum+1)&0xFF); 
		}
		
		/*##############################################################
		# Print a report (the hash returned by RxReport/GetReport)
		##############################################################*/
		public void  PrintReport( )
		{
		    Log.v(TAG, "Device OTAFU Update Result: Operation = " + "Result = " + operation_result + operation_id + ", len = " + len + ",data = "+ ByteArrayToDataString(data) +", checksum = " + checksum + "\n");
		}
	}
	

	/*##############################################################
	# Receive an OTAFU report, decode it into returned hash
	##############################################################*/
	private OtaReport_T RxUpdateReport( byte[] data )
	{
		OtaReport_T report;
		short i = 0;
		
		short length = (short)(((0x000000FF & ((int)data[3]))<<8) | (0x000000FF & ((int)data[2])));
		
		report = new OtaReport_T(length);
		
	    report.operation_id = data[0];
	    report.operation_result = data[1];
	    report.len  = length;
	    
	    if( length > 0 )
	    {
	    	for( i=0;i<length;i++)
	    	{
	    		report.data[i]  = data[4+i];
	    	}

	    	// And ensure that the received checksum == computed checksum
	    	report.checksum  = data[length+4];
    	    if ( report.Checksum() !=  data[length+4] ) 
    	    {
    	        Log.v( TAG, "Report is inconflict \n");
    	    }
	    }
	   	     
	    report.PrintReport();

	    return report;
	}
	
	private Boolean SendACLData( byte[] data )
	{
		Boolean result = false;
		
		if( (mGattService != null) && (mUpdateValueCharact != null))
		{
			
			if ( mUpdateValueCharact.setValue(data) ) 
			{
				//mUpdateValueCharact.setWriteType( BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE );
				result = mGattService.writeCharacteristic(mUpdateValueCharact);
			}
		}
		
		return result;
	}
	
	
	private Boolean OpenImageFile( String FilePath )
	{
	    Boolean ret = false;
	    
		try {
			File patch_file = new File(FilePath );
			if( patch_file != null  )
			{
			    image_file_reader = new BufferedReader(new FileReader( patch_file ));
			    if( null != image_file_reader )
			    {
	                ret = true;
	                patch_size = (int) patch_file.length();
			    }
		         
			}
		}  
		catch (Exception e) {
            e.printStackTrace();

            return false;
        }

        return ret;
	}
	
	private void CloseImageFile( )
	{
		try   {
			if( image_file_reader != null)
			{
		        image_file_reader.close();
		        image_file_reader = null;
			}
		}  
		catch (Exception e) {
            e.printStackTrace();
        }
	}
	
	private int ReadImageData( byte[] write_buf, int length )
	{
		int read_len = 0;
		String read_str;
		byte[] tmp_buf = new byte[64];
		int i,j;
		
		if ((image_file_reader != null) && ( write_buf != null ))
		{
			try  {
				 read_str = image_file_reader.readLine();
	    	 
	             if(( read_str == null ))
	             {
	        	     CloseImageFile();
	             }
	             else
	             {
	            	 Log.v(TAG, "read patch: " + read_str + "\n");
	                 if( read_str.getBytes().length < 64 )
	                 {
	                     System.arraycopy(read_str.toLowerCase().getBytes(), 0, tmp_buf, 0,  read_str.getBytes().length );
	                     read_len = read_str.getBytes().length;
	                     
	                     for( i=1,j=0;i < read_len;i+=2, j++)
	                     {
	                    	 if( tmp_buf[i] > '9')
	                    	 {
	                    	      write_buf[j] = (byte) ((tmp_buf[i] - 'a' + 0xA)<<4);
	                    	      
	                    	 }
	                    	 else
	                    	 {
	                    		 write_buf[j] = (byte) ((tmp_buf[i] - '0')<<4);
	                    	 }
	                    	 
	                    	 if( tmp_buf[i+1] > '9')
	                    	 {
	                    	      write_buf[j] += tmp_buf[i+1] - 'a' + 0xA;
	                    	      
	                    	 }
	                    	 else
	                    		 write_buf[j] += tmp_buf[i+1] - '0';
	                     }
	                     
	                     read_len = j;
	                 }
	                 offset += read_str.getBytes().length;
	             }
			}  
			catch (Exception e) {
                e.printStackTrace();
            }
	         
		}
		
		return read_len;
	}
	
	/*##############################################################
	# Compute and return a checksum
	##############################################################*/
	private byte Checksum( short id, int addr, short len, byte[] data)
	{
	    int sum = (int)id;
	    short i = 0;
	    
	    sum += addr & 0xFF;
	    sum += (addr>>8) & 0xFF;
	    sum += (addr>>16) & 0xFF;
	    sum += (addr>>24) & 0xFF;

	    sum += len & 0xFF;
	    sum += (len>>8) & 0xFF;

	    if( (len > 0 ) && (data != null))
	    {
		    for(i=0; i < len; i++)
		    {
		        sum += data[i];
		    }
	    }
		
		//1-byte 2's complement of all the bytes
	    return (byte) ((~sum+1)&0xFF); 
	}
	
	
	/*##############################################################
	# Write memory (addr, bytes)
	##############################################################*/
	private Boolean WriteMem( int addr, byte[] data )
	{
		Boolean result =false;
		
	    if( data == null )  return result;
	    
	    Log.v( TAG, "WriteMem: address :" + addr + "data length :" + data.length);
	    
	    result = SetReport( ID_WRITE, addr,  (short) data.length, data);
		
		return result;
	}

	

	/*##############################################################
	# Enable OTAFU (send the magic 0x70 report)
	##############################################################*/
	public Boolean EnableOtafu()
	{
		Boolean result =false;
	
		result = SetReport( ID_ENABLE_OTAFU, 0xFF00, (short) 0, null); // A launch to 0x00 will never return...

        Log.v( TAG, "Enabled OTA result: " + result );
		
		return result;
	}



	/*##############################################################
	# Send a set report
	# The general format of the BRCM OTA Set-report message is:
	# X-----------------------X-----------------------------X---------------------X-------------------X--------------------------X----------X------------------X
	# PACKET_LENGTH(2 bytes)  |  L2CAP_CHANNEL_ID(2 bytes)  |  REPORT_ID(1 byte)  | ADDRESS(4 bytes)  |  REPORT_LENGTH(2 bytes)  |  DATA()  | CHECK_SUM(1 byte)|
	# X-----------------------X-----------------------------X---------------------X-------------------X--------------------------X----------X------------------X
	##############################################################*/
	private Boolean SetReport( byte repid, int addr, short len, byte[] data )
	{
	    byte checksum_value = Checksum(repid, addr, len, data);
	   
		byte[] sreportbytes = new byte[len + 8];
		
		Boolean result = false;
		
		sreportbytes[0] = repid;
		
		sreportbytes[1] = (byte) (addr&0xFF);
		sreportbytes[2] = (byte) ((addr>>8)&0xFF);
		sreportbytes[3] = (byte) ((addr>>16)&0xFF);
		sreportbytes[4] = (byte) ((addr>>24)&0xFF);
		
		sreportbytes[5] = (byte) (len&0xFF);
		sreportbytes[6] = (byte) (len>>8);
		
		// Put the report bytes together
		if( (len > 0 ) && ( data != null ) )
		{
			short i = 0;
			for( i=0;i<len;i++)
			{
				sreportbytes[7+i] = data[i];
			}
		}
		
		sreportbytes[len+7] = checksum_value;
		

		result = SendACLData( sreportbytes );
		
	    return result;
	}


	/*##############################################################
	# Launch
	##############################################################*/
	private Boolean Launch( int addr )
	{
		Boolean result =false;
		
		Log.v( TAG,  "Launch: " + addr );
		CloseImageFile( );
		result = SetReport( ID_LAUNCH, addr, (short) 0, null); // A launch to 0x00 will never return...
		
		return result;
	}
	
	
	public Boolean WriteImage( Boolean retry )
	{
		Boolean complete = false;
		byte[] data = null;
		byte[] tmp_buffer = new byte[64];
		int len = 0;
		byte pdu_len = 0;
		short pdu_offset = 0;
		byte rec_type = 0;
		Boolean pdu_found = false;
			
		if( !retry )
		{
			do   
			{
				len = ReadImageData( tmp_buffer, 64 );
				if( len > 4 )
				{
				    pdu_len = tmp_buffer[0];
				    pdu_offset = (short)(((0x000000FF & ((int)tmp_buffer[1]))<<8) | (0x000000FF & ((int)tmp_buffer[2])));
				    rec_type = tmp_buffer[3];
				    
				    if (rec_type == 0x01)     // End-of-ffile record
			        {
				    	complete = true;//last LINE;
				    	offset = patch_size;
				    	break;
			        }
				    else if (rec_type == 0x04)  // Extended linear address record
			        {
				    	Log.v(TAG, "Read Image file header\n");
			        }
			        else if (rec_type == 0x00)  // Data record
			        {
			        	if( pdu_offset >= 0x04C0 )
			        	    pdu_found = true;
			        } 
			        
				}
				else
					break;
			}
			while( !pdu_found );
		}
		
		if( !complete )
		{
			if( pdu_found )
			{
				 int sum = 0;
				 if( pdu_len > maxOtaDataSize  ) pdu_len = 16;
			     data = new byte[pdu_len+4];
			     //System.arraycopy(tmp_buffer.,  0,  data,  0,  pdu_len );
			     //data = Arrays.copyOfRange( tmp_buffer,  4, pdu_len + 4);
			     //WriteMem( eeprom_address + offset, data );
			     //offset += pdu_len;
			     data[0] = ID_WRITE;
			     data[1] = pdu_len;
			     data[2] = (byte)pack_id;
			     for ( int i=0;i<pdu_len;i++)
			     {
			         data[i+3] = tmp_buffer[i+4];
			     }
			     for ( int j=0;j<pdu_len+3; j++)
			    	 sum += data[j];
			    		 
			     data[pdu_len+3] = (byte)(~sum+1);
			     SendACLData(data);
			     pack_id++;
			}
		}
		
		return complete;
	}


	public Boolean GetLastUpdateResult( )
	{
	    Boolean result =false;
	    
        if ( mUpdateResultCharact != null ) 
        {
        	result = mGattService.readCharacteristic( mUpdateResultCharact );
        }

        return result;
	}
	
	
	public short OtaHandleWriteResult( byte[] value, int status )
	{
	    short result = UPDATE_STATUS_CONTINUE;
	    Boolean write_failed = false;

	   // RxUpdateReport( value );
	    
	    switch( value[0])
    	{
    	case ID_ENABLE_OTAFU:
    		if( 0 != status )
    		{
    			result = UPDATE_STATUS_ENABLE_FAIL;
    		}
    		else
    		{
    			//GetLastUpdateResult( );
    		    WriteImage(false);
    		}
    		
    		break;
    		
    	case ID_WRITE:
    		if( 0 != status )
    		{
    			write_failed = true;
    			data_send_error = true;
    		}
    		
    		if( !data_send_error) 
    		{
    		
    		    if( WriteImage( write_failed ))
    		    {
    		       if( Launch(0) )
    		       //if( GetLastUpdateResult())
    		       {
    		    	   result = UPDATE_STATUS_CONTINUE;
    		    	   //GetLastUpdateResult();
    		       }
    		    }
    		}
    		else
    			result = UPDATE_STATUS_LAUNCH_FAIL;
    		
    		break;
    		
    	case ID_LAUNCH:
    		if( 0 != status)
    		{
    			result = UPDATE_STATUS_LAUNCH_FAIL;
    		}
    		else
    		{
    		    result = UPDATE_STATUS_CONTINUE;
    		}
    		break;
    		
    	
    	default:
    		break;
    	}
	    
	    return result;
	}
	
	
	public short OtaHandleUpdateResult( byte[] value ) 
	{
	    short result = UPDATE_STATUS_CONTINUE;
	    Boolean write_failed = false;
	    short length = 0;

	    RxUpdateReport( value );
	    
	    switch( value[0])
    	{
    	case ID_ENABLE_OTAFU:
    		if( 0 == value[1] )
    		{
    			result = UPDATE_STATUS_ENABLE_FAIL;
    		}
    		//else
    			//WriteImage(false);
    		
    		break;
    		
    	case ID_WRITE:
    		if( 0 == value[1] )
    		{
    			write_failed = true;
    		}
    		else
    		{
    			
    		}
    		
    		length = (short)(((0x000000FF & ((int)value[6]))<<8) | (0x000000FF & ((int)value[5])));
    		Log.v( TAG, "Write length: " + length );
    		
    		if(( length == last_length)  && ( length != 0 ))
    		{
    			Launch(0);
    		}
    		else
    		{
	    		try {
					last_length = length;
					Thread.sleep(2000);
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
	    		
	    		GetLastUpdateResult();
    		}
    		break;
    		
    	case ID_LAUNCH:
    		if( 0 == value[1])
    		{
    			result = UPDATE_STATUS_LAUNCH_FAIL;
    		}
    		else
    		{
    		    result = UPDATE_STATUS_COMPLETE_SUCCESS;
    		}
    		break;
    		
    	
    	default:
    		break;
    	}
	    
	    return result;
	}
	
	public byte OtaGetUpdateProgress()
	{
		byte percent = 0;
		
	    if( ( patch_size > 0 ) && ( offset > 0 ) && ( patch_size >= offset ) )
	    {
	    	percent = (byte)((float)offset*100/patch_size);
	    }
	    
	    Log.v( TAG, "Update Progress: " + percent );
	    return percent;
	}

}

传输BIN和二进制文件

/******************************************************************************
 *
 *  Copyright (C) 2013 Broadcom Corporation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *  The offset of the second SS. For EEPROM, SS can be at 0x00, 0x100, 0x200, 0x400, 0x800, 0x1000 and 0x2000. 
 *  SS1 is almost always 0x00. So SS2 can be any of the others. Default SS2 we will use is 0x100. You may use any of the others if you want the EEPROM layout to be different.
 *  We will 'allocate' 256 bytes for SS1 (even though it takes up only 40 bytes leaving a 216 byte hole) and set up 
 *  SS2 at 0x100. Since we know that SS2 will be 40 bytes long, we can set up VS at 0x2C0 and of length 0x200 bytes,
 *  DS1 at 0x4C0 and DS2 at 0x84C0 leaving 0x8000 bytes for DS1 and 0x7B40 (about 31.5KB) for DS2.
 *  So, the layout in EEPROM will be as follows with the following offsets:

 *   # 0-----0x100-------0x2C0-----------0x4C0----------------------------0x84C0----------------------X
 *   # |  SS1  |     SS2   |       VS      |              DS1                |          DS2           |
 *   # X----------------------------------------------------------------------------------------------X


 * These are more or less constants of the OTAFU protocol. If you changed these IDs in the FW, use the ones the FW uses.
 **********************************************************************************************************************/

package com.broadcom.app.ota_sample;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.util.Arrays;
import java.util.UUID;

import android.bluetooth.BluetoothGattCharacteristic;
import android.os.Environment;
import android.util.Log;

public class B20732_OtaServiceBin {
	private static final String TAG = "OTA_UPdate";

	private static final int maxOtaDataSize = 16; // That is the max size of
													// data in the OTA PDU.
	private short maxSSLen = 0x28; // The static section is always 40 bytes
									// long.
	private short secondSSOffset = 0x100;
	private short firstSSDsOffset = 0x4C0;

	private static final int eeprom_address = 0xFF0004C0;
	// private int offset = 0, pack_id = 0, patch_size = 0;

	private String burnimg_path; // The path of the burn image that will be
									// generated when updating.

	private GattAppService mGattService = null;
	private BluetoothGattCharacteristic mUpdateValueCharact = null;
	private BluetoothGattCharacteristic mUpdateResultCharact = null;

	private BufferedReader image_file_reader = null;
	private short last_length = 0;

	private static final byte ID_ENABLE_OTAFU = 0x70;
	private static final byte ID_SETUP_READ = 0x71;
	private static final byte ID_READ = 0x72;
	private static final byte ID_ERASE = 0x73;
	private static final byte ID_WRITE = 0x74;
	private static final byte ID_LAUNCH = 0x75;

	public static final short UPDATE_STATUS_CONTINUE = 0x00;
	public static final short UPDATE_STATUS_COMPLETE_SUCCESS = 0x01;
	public static final short UPDATE_STATUS_ENABLE_FAIL = 0x02;
	public static final short UPDATE_STATUS_WRITE_FAIL = 0x03;
	public static final short UPDATE_STATUS_LAUNCH_FAIL = 0x04;

	public static final UUID OTA_SERVICE_UUID = UUID
			.fromString("0000E010-0000-1000-8000-00805f9b34fb");
	public static final UUID OTA_WRITE_CHARACTER_UUID = UUID
			.fromString("0000E011-0000-1000-8000-00805f9b34fb");
	public static final UUID OTA_RESULT_CHARACTER_UUID = UUID
			.fromString("0000E012-0000-1000-8000-00805f9b34fb");

	private boolean data_send_error = false;
	private short fail_packet_num = 0;
	private short[] array_fail_packet;

	// jia add
	private DataInputStream dis = null;
	private FileInputStream fin = null;
	private DataOutputStream dos = null;
	private long offset = 0, pack_id = 0, patch_size = 0;
	private byte read_max_buf = maxOtaDataSize;

	// private long new_offset = 0, new_pack_id = 0, new_patch_size = 0;

	public B20732_OtaServiceBin(GattAppService Service,
			BluetoothGattCharacteristic Characteristic) {
		mGattService = Service;
		mUpdateValueCharact = Characteristic;

		if (mGattService != null) {
			mUpdateResultCharact = mGattService.getCharacteristic(
					OTA_SERVICE_UUID, OTA_RESULT_CHARACTER_UUID);
		}

		array_fail_packet = new short[2000];
		for (short i = 0; i < 2000; i++)
			array_fail_packet[i] = (short) -1;

	}

	/*
	 * public Boolean Set_PatchFilePath( String path ) { Boolean result =false;
	 * 
	 * if( path != null ) { burnimg_path = new String( path );
	 * 
	 * if( burnimg_path != null ) { if( OpenImageFile( burnimg_path )) { result
	 * = true; } } }
	 * 
	 * return result; }
	 */
	public Boolean Set_PatchBinFilePath(String path) {
		Boolean result = false;

		if (path != null) {
			//burnimg_path = new String(path);
			String SDPATH = Environment.getExternalStorageDirectory().getPath();
			burnimg_path = new String(SDPATH+"//a.bin");
			File file = new File(burnimg_path); 

	        if (file.exists()) { 

	       
			if (burnimg_path != null) {
				if (OpenBinFile(burnimg_path)) {
					result = true;
				}
			
	        }
	        }
		}

		return result;
	}

	class OtaReport_T {
		byte operation_id;
		byte operation_result;
		short len;
		byte[] data;
		byte checksum;

		public OtaReport_T(short length) {
			if (length > 0)
				data = new byte[length];

			operation_id = 0;
			operation_result = 0;
			len = length;
			checksum = 0;
		}

		private String ByteArrayToDataString(byte[] data) {
			String out = null;

			if (data != null)
				out = Arrays.toString(data);

			return out;
		}

		public byte Checksum() {
			int tst_sum = 0;
			int i = 0;

			tst_sum = operation_id + operation_result + (len & 0xFF)
					+ ((len >> 8) & 0xFF);

			if (len > 0) {
				for (i = 0; i < len; i++) {
					tst_sum += data[i];
				}
			}

			return (byte) ((~tst_sum + 1) & 0xFF);
		}

		/*
		 * ############################################################## #
		 * Print a report (the hash returned by RxReport/GetReport)
		 * ##############################################################
		 */
		public void PrintReport() {
			Log.v(TAG, "Device OTAFU Update Result: Operation = " + "Result = "
					+ operation_result + operation_id + ", len = " + len
					+ ",data = " + ByteArrayToDataString(data)
					+ ", checksum = " + checksum + "\n");
		}
	}
 
	/*
	 * ############################################################## # Receive
	 * an OTAFU report, decode it into returned hash
	 * ##############################################################
	 */
	private OtaReport_T RxUpdateReport(byte[] data) {
		OtaReport_T report;
		short i = 0;

		short length = (short) (((0x000000FF & ((int) data[3])) << 8) | (0x000000FF & ((int) data[2])));

		report = new OtaReport_T(length);

		report.operation_id = data[0];
		report.operation_result = data[1];
		report.len = length;

		if (length > 0) {
			for (i = 0; i < length; i++) {
				report.data[i] = data[4 + i];
			}

			// And ensure that the received checksum == computed checksum
			report.checksum = data[length + 4];
			if (report.Checksum() != data[length + 4]) {
				Log.v(TAG, "Report is inconflict \n");
			}
		}

		report.PrintReport();

		return report;
	}

	private Boolean SendACLData(byte[] data) {
		Boolean result = false;

		if ((mGattService != null) && (mUpdateValueCharact != null)) {

			if (mUpdateValueCharact.setValue(data)) {
				// mUpdateValueCharact.setWriteType(
				// BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE );
				result = mGattService.writeCharacteristic(mUpdateValueCharact);
			}
		}

		return result;
	}

	/*
	 * private Boolean OpenImageFile( String FilePath ) { Boolean ret = false;
	 * 
	 * try { File patch_file = new File(FilePath ); if( patch_file != null ) {
	 * image_file_reader = new BufferedReader(new FileReader( patch_file )); if(
	 * null != image_file_reader ) { ret = true; patch_size = (int)
	 * patch_file.length(); }
	 * 
	 * } } catch (Exception e) { e.printStackTrace();
	 * 
	 * return false; }
	 * 
	 * return ret; }
	 */
	private Boolean OpenBinFile(String FilePath) {
		Boolean ret = false;

		try {
			// File patch_file = new File(FilePath );
			Log.i("AAAAA", "OpenBinFile FilePath = "+FilePath);
			fin = new FileInputStream(FilePath);
			dis = new DataInputStream(fin);
			if (fin != null) {
				if (dis != null) {
					ret = true;
					patch_size = fin.getChannel().size();
					Log.i("AAAAA", "OpenBinFile patch_size = "+patch_size);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();

			return false;
		}

		return ret;
	}

	/*
	 * private void CloseImageFile( ) { try { if( image_file_reader != null) {
	 * image_file_reader.close(); image_file_reader = null; } } catch (Exception
	 * e) { e.printStackTrace(); } }
	 */
	private void CloseBinFile() {
		try {
			if (dis != null) {
				dis.close();
				fin.close();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/*
	 * private int ReadImageData( byte[] write_buf, int length ) { int read_len
	 * = 0; String read_str; byte[] tmp_buf = new byte[64]; int i,j;
	 * 
	 * if ((image_file_reader != null) && ( write_buf != null )) { try {
	 * read_str = image_file_reader.readLine();
	 * 
	 * if(( read_str == null )) { CloseImageFile(); } else { Log.v(TAG,
	 * "read patch: " + read_str + "\n"); if( read_str.getBytes().length < 64 )
	 * { System.arraycopy(read_str.toLowerCase().getBytes(), 0, tmp_buf, 0,
	 * read_str.getBytes().length ); read_len = read_str.getBytes().length;
	 * 
	 * for( i=1,j=0;i < read_len;i+=2, j++) { if( tmp_buf[i] > '9') {
	 * write_buf[j] = (byte) ((tmp_buf[i] - 'a' + 0xA)<<4);
	 * 
	 * } else { write_buf[j] = (byte) ((tmp_buf[i] - '0')<<4); }
	 * 
	 * if( tmp_buf[i+1] > '9') { write_buf[j] += tmp_buf[i+1] - 'a' + 0xA;
	 * 
	 * } else write_buf[j] += tmp_buf[i+1] - '0'; }
	 * 
	 * read_len = j; } offset += read_str.getBytes().length; } } catch
	 * (Exception e) { e.printStackTrace(); }
	 * 
	 * }
	 * 
	 * return read_len; }
	 */
	private int ReadBinData(byte[] write_buf) {
		int read_len = 0;
		byte[] tmp_buf = new byte[this.read_max_buf + 4];
		boolean isFinish = false;

		if ((dis != null) && (write_buf != null)) {
			try {
				if ((patch_size - offset) >= this.read_max_buf) {
					read_len = this.read_max_buf;
					tmp_buf[3] = (byte) 0x00;
				} else if ((patch_size - offset) > 0) {
					read_len = (int) (patch_size - offset);
					tmp_buf[3] = (byte) 0x00;
					isFinish = true;
				} else {
					tmp_buf[3] = (byte) 0x01;
				}
				tmp_buf[0] = (byte) read_len;
				tmp_buf[1] = (byte) 0xff;
				tmp_buf[2] = (byte) 0xff;

				for (int i = 0; i < read_len; i++) {
					tmp_buf[i + 4] = dis.readByte();
					
				}
				if (isFinish) {
					System.arraycopy(tmp_buf, 0, write_buf, 0, read_len);
					CloseBinFile();
				} else {
					System.arraycopy(tmp_buf, 0, write_buf, 0,
							this.read_max_buf);
				}
				offset += read_len;
				Log.i("AAAAA", "ReadBinData offset="+offset);
			} catch (Exception e) {
				e.printStackTrace();
			}

		}

		return read_len + 4;
	}

	/*
	 * ############################################################## # Compute
	 * and return a checksum
	 * ##############################################################
	 */
	private byte Checksum(short id, int addr, short len, byte[] data) {
		int sum = (int) id;
		short i = 0;

		sum += addr & 0xFF;
		sum += (addr >> 8) & 0xFF;
		sum += (addr >> 16) & 0xFF;
		sum += (addr >> 24) & 0xFF;

		sum += len & 0xFF;
		sum += (len >> 8) & 0xFF;

		if ((len > 0) && (data != null)) {
			for (i = 0; i < len; i++) {
				sum += data[i];
			}
		}

		// 1-byte 2's complement of all the bytes
		return (byte) ((~sum + 1) & 0xFF);
	}

	/*
	 * ############################################################## # Write
	 * memory (addr, bytes)
	 * ##############################################################
	 */
	private Boolean WriteMem(int addr, byte[] data) {
		Boolean result = false;

		if (data == null)
			return result;

		Log.v(TAG, "WriteMem: address :" + addr + "data length :" + data.length);

		result = SetReport(ID_WRITE, addr, (short) data.length, data);

		return result;
	}

	/*
	 * ############################################################## # Enable
	 * OTAFU (send the magic 0x70 report)
	 * ##############################################################
	 */
	public Boolean EnableOtafu() {
		Boolean result = false;

		result = SetReport(ID_ENABLE_OTAFU, 0xFF00, (short) 0, null); // A
																		// launch
																		// to
																		// 0x00
																		// will
																		// never
																		// return...

		Log.v(TAG, "Enabled OTA result: " + result);

		return result;
	}

	/*
	 * ############################################################## # Send a
	 * set report # The general format of the BRCM OTA Set-report message is: #
	 * X
	 * -----------------------X-----------------------------X--------------------
	 * -
	 * X-------------------X--------------------------X----------X--------------
	 * ----X # PACKET_LENGTH(2 bytes) | L2CAP_CHANNEL_ID(2 bytes) | REPORT_ID(1
	 * byte) | ADDRESS(4 bytes) | REPORT_LENGTH(2 bytes) | DATA() | CHECK_SUM(1
	 * byte)| #
	 * X-----------------------X-----------------------------X----------
	 * ----------
	 * -X-------------------X--------------------------X----------X----
	 * --------------X
	 * ##############################################################
	 */
	private Boolean SetReport(byte repid, int addr, short len, byte[] data) {
		byte checksum_value = Checksum(repid, addr, len, data);

		byte[] sreportbytes = new byte[len + 8];

		Boolean result = false;

		sreportbytes[0] = repid;

		sreportbytes[1] = (byte) (addr & 0xFF);
		sreportbytes[2] = (byte) ((addr >> 8) & 0xFF);
		sreportbytes[3] = (byte) ((addr >> 16) & 0xFF);
		sreportbytes[4] = (byte) ((addr >> 24) & 0xFF);

		sreportbytes[5] = (byte) (len & 0xFF);
		sreportbytes[6] = (byte) (len >> 8);

		// Put the report bytes together
		if ((len > 0) && (data != null)) {
			short i = 0;
			for (i = 0; i < len; i++) {
				sreportbytes[7 + i] = data[i];
			}
		}

		sreportbytes[len + 7] = checksum_value;

		result = SendACLData(sreportbytes);

		return result;
	}

	/*
	 * ############################################################## # Launch
	 * ##############################################################
	 */
	/*
	 * private Boolean Launch( int addr ) { Boolean result =false;
	 * 
	 * Log.v( TAG, "Launch: " + addr ); CloseImageFile( ); result = SetReport(
	 * ID_LAUNCH, addr, (short) 0, null); // A launch to 0x00 will never
	 * return...
	 * 
	 * return result; }
	 */
	private Boolean LaunchBin(int addr) {
		Boolean result = false;

		Log.v(TAG, "Launch: " + addr);
		CloseBinFile();
		result = SetReport(ID_LAUNCH, addr, (short) 0, null); // A launch to
																// 0x00 will
																// never
																// return...

		return result;
	}

	/*
	 * public Boolean WriteImage( Boolean retry ) { Boolean complete = false;
	 * byte[] data = null; byte[] tmp_buffer = new byte[64]; int len = 0; byte
	 * pdu_len = 0; short pdu_offset = 0; byte rec_type = 0; Boolean pdu_found =
	 * false;
	 * 
	 * if( !retry ) { do { len = ReadImageData( tmp_buffer, 64 ); if( len > 4 )
	 * { pdu_len = tmp_buffer[0]; pdu_offset = (short)(((0x000000FF &
	 * ((int)tmp_buffer[1]))<<8) | (0x000000FF & ((int)tmp_buffer[2])));
	 * rec_type = tmp_buffer[3];
	 * 
	 * if (rec_type == 0x01) // End-of-ffile record { complete = true;//last
	 * LINE; offset = patch_size; break; } else if (rec_type == 0x04) //
	 * Extended linear address record { Log.v(TAG, "Read Image file header\n");
	 * } else if (rec_type == 0x00) // Data record { if( pdu_offset >= 0x04C0 )
	 * pdu_found = true; }
	 * 
	 * } else break; } while( !pdu_found ); }
	 * 
	 * if( !complete ) { if( pdu_found ) { int sum = 0; if( pdu_len >
	 * maxOtaDataSize ) { pdu_len = maxOtaDataSize; } data = new
	 * byte[pdu_len+4]; //System.arraycopy(tmp_buffer., 0, data, 0, pdu_len );
	 * //data = Arrays.copyOfRange( tmp_buffer, 4, pdu_len + 4); //WriteMem(
	 * eeprom_address + offset, data ); //offset += pdu_len; data[0] = ID_WRITE;
	 * data[1] = pdu_len; data[2] = (byte)pack_id; for ( int i=0;i<pdu_len;i++)
	 * { data[i+3] = tmp_buffer[i+4]; } for ( int j=0;j<pdu_len+3; j++) sum +=
	 * data[j];
	 * 
	 * data[pdu_len+3] = (byte)(~sum+1); SendACLData(data); pack_id++; } }
	 * 
	 * return complete; }
	 */
	public Boolean WriteBin(Boolean retry) {
		Boolean complete = false;
		byte[] data = null;
		byte[] tmp_buffer = new byte[this.read_max_buf + 4];
		int len = 0;
		byte pdu_len = 0;
		short pdu_offset = 0;
		byte rec_type = 0;
		Boolean pdu_found = false;

		if (!retry) {
			do {
				len = ReadBinData(tmp_buffer);
				if (len >= 4) {
					pdu_len = tmp_buffer[0];
					pdu_offset = (short) (((0x000000FF & ((int) tmp_buffer[1])) << 8) | (0x000000FF & ((int) tmp_buffer[2])));
					rec_type = tmp_buffer[3];

					if (rec_type == 0x01) // End-of-ffile record
					{
						complete = true;// last LINE;
						offset = patch_size;
						break;
					} else if (rec_type == 0x00) // Data record
					{
						pdu_found = true;
					}

				} else {
					break;
				}
			} while (!pdu_found);
		}

		if (!complete) {
			if (pdu_found) {
				int sum = 0;
				if (pdu_len > read_max_buf) {
					pdu_len = read_max_buf;
				}
				data = new byte[pdu_len + 4];

				// offset += pdu_len;
				data[0] = ID_WRITE;
				data[1] = pdu_len;
				data[2] = (byte) pack_id;
				for (int i = 0; i < pdu_len; i++) {
					data[i + 3] = tmp_buffer[i + 4];
				}
				for (int j = 0; j < pdu_len + 3; j++) {
					sum += data[j];
				}

				data[pdu_len + 3] = (byte) (~sum + 1);
				SendACLData(data);
				pack_id++;
				Log.i("AAAAA", "ReadBinData pack_id="+pack_id);
			}
		}
		return complete;
	}

	public Boolean GetLastUpdateResult() {
		Boolean result = false;

		if (mUpdateResultCharact != null) {
			result = mGattService.readCharacteristic(mUpdateResultCharact);
		}

		return result;
	}

	public short OtaHandleWriteResult(byte[] value, int status) {
		short result = UPDATE_STATUS_CONTINUE;
		Boolean write_failed = false;

		// RxUpdateReport( value );

		switch (value[0]) {
		case ID_ENABLE_OTAFU:
			if (0 != status) {
				result = UPDATE_STATUS_ENABLE_FAIL;
			} else {
				// GetLastUpdateResult( );
				// WriteImage(false);
				// jia add
				WriteBin(false);
			}

			break;

		case ID_WRITE:
			if (0 != status) {
				write_failed = true;
				data_send_error = true;
			}

			if (!data_send_error) {

				// if( WriteImage( write_failed ))
				// jia add
				if (WriteBin(write_failed)) {
					// if( Launch(0) )
					// if( GetLastUpdateResult())
					// jia add
					if (LaunchBin(0)) {
						result = UPDATE_STATUS_CONTINUE;
						// GetLastUpdateResult();
					}
				}
			} else
				result = UPDATE_STATUS_LAUNCH_FAIL;

			break;

		case ID_LAUNCH:
			if (0 != status) {
				result = UPDATE_STATUS_LAUNCH_FAIL;
			} else {
				result = UPDATE_STATUS_CONTINUE;
			}
			break;

		default:
			break;
		}

		return result;
	}

	public short OtaHandleUpdateResult(byte[] value) {
		short result = UPDATE_STATUS_CONTINUE;
		Boolean write_failed = false;
		short length = 0;

		RxUpdateReport(value);

		switch (value[0]) {
		case ID_ENABLE_OTAFU:
			if (0 == value[1]) {
				result = UPDATE_STATUS_ENABLE_FAIL;
			}
			// else
			// WriteImage(false);

			break;

		case ID_WRITE:
			if (0 == value[1]) {
				write_failed = true;
			} else {

			}

			length = (short) (((0x000000FF & ((int) value[6])) << 8) | (0x000000FF & ((int) value[5])));
			Log.v(TAG, "Write length: " + length);

			if ((length == last_length) && (length != 0)) {
				// Launch(0);
				// jia add
				LaunchBin(0);
			} else {
				try {
					last_length = length;
					Thread.sleep(2000);
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}

				GetLastUpdateResult();
			}
			break;

		case ID_LAUNCH:
			if (0 == value[1]) {
				result = UPDATE_STATUS_LAUNCH_FAIL;
			} else {
				result = UPDATE_STATUS_COMPLETE_SUCCESS;
			}
			break;

		default:
			break;
		}

		return result;
	}

	public byte OtaGetUpdateProgress() {
		byte percent = 0;

		if ((patch_size > 0) && (offset > 0) && (patch_size >= offset)) {
			percent = (byte) ((float) offset * 100 / patch_size);
		}

		Log.v(TAG, "Update Progress: " + percent);
		return percent;
	}

}


  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值