2 /// \file controller.cc
3 /// High level Barry API class
7 Copyright (C) 2005-2006, Net Direct Inc. (http://www.netdirect.ca/)
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 See the GNU General Public License in the COPYING file at the
19 root directory of this project for more details.
22 #include "controller.h"
25 #include "protostructs.h"
31 #define __DEBUG_MODE__
41 // Controller constructor
43 /// Constructor for the Controller class. Requires a valid ProbeResult
44 /// object to find the USB device to talk to.
46 /// \param[in] device One of the ProbeResult objects from the
49 Controller::Controller(const ProbeResult
&device
)
50 : m_dev(device
.m_dev
),
52 m_socket(m_dev
, device
.m_ep
.write
, device
.m_ep
.read
),
55 if( !m_dev
.SetConfiguration(BLACKBERRY_CONFIGURATION
) )
56 throw BError(m_dev
.GetLastError(),
57 "Controller: SetConfiguration failed");
59 m_iface
= new Usb::Interface(m_dev
, BLACKBERRY_INTERFACE
);
62 Controller::~Controller()
67 ///////////////////////////////////////////////////////////////////////////////
70 void Controller::SelectMode(ModeType mode
, uint16_t &socket
, uint8_t &flag
)
75 packet
.size
= SB_MODE_PACKET_COMMAND_SIZE
;
76 packet
.command
= SB_COMMAND_SELECT_MODE
;
77 packet
.u
.mode
.socket
= SB_MODE_REQUEST_SOCKET
;
78 packet
.u
.mode
.flag
= 0x05; // FIXME
79 memset(packet
.u
.mode
.modeName
, 0, sizeof(packet
.u
.mode
.modeName
));
81 char *modeName
= (char *) packet
.u
.mode
.modeName
;
85 strcpy(modeName
, "RIM Bypass");
89 strcpy(modeName
, "RIM Desktop");
93 strcpy(modeName
, "RIM_JavaLoader");
97 throw std::logic_error("Controller: Invalid mode in SelectMode");
101 // send mode command before we open, as a default socket is socket 0
102 Data
command(&packet
, packet
.size
);
104 if( !m_socket
.Send(command
, response
) ) {
105 eeout(command
, response
);
106 throw BError(m_socket
.GetLastStatus(),
107 "Controller: error setting desktop mode");
110 // get the data socket number
111 // indicates the socket number that
112 // should be used below in the Open() call
113 CheckSize(response
, SB_MODE_PACKET_RESPONSE_SIZE
);
114 MAKE_PACKET(modepack
, response
);
115 if( modepack
->command
!= SB_COMMAND_MODE_SELECTED
) {
116 eeout(command
, response
);
117 throw BError("Controller: mode not selected");
120 // return the socket and flag that the device is expecting us to use
121 socket
= modepack
->u
.mode
.socket
;
122 flag
= modepack
->u
.mode
.flag
+ 1;
125 unsigned int Controller::GetCommand(CommandType ct
)
127 unsigned int cmd
= 0;
128 char *cmdName
= "Unknown";
133 cmdName
= "Database Access";
134 cmd
= m_commandTable
.GetCommand(cmdName
);
137 throw std::logic_error("Controller: unknown command type");
141 std::ostringstream oss
;
142 oss
<< "Controller: unable to get command code: " << cmdName
;
143 throw BError(oss
.str());
149 void Controller::LoadCommandTable()
151 assert( m_mode
== Desktop
);
153 char rawCommand
[] = { 6, 0, 0x0a, 0, 0x40, 0, 0, 1, 0, 0 };
154 *((uint16_t*) rawCommand
) = m_socket
.GetSocket();
156 Data
command(rawCommand
, sizeof(rawCommand
));
158 if( !m_socket
.Packet(command
, response
) ) {
159 eeout(command
, response
);
160 throw BError(m_socket
.GetLastStatus(),
161 "Controller: error getting command table");
164 MAKE_PACKET(rpack
, response
);
165 while( rpack
->command
!= SB_COMMAND_DB_DONE
) {
166 if( !m_socket
.NextRecord(response
) ) {
167 eout("Response packet:\n" << response
);
168 throw BError(m_socket
.GetLastStatus(),
169 "Controller: error getting command table(next)");
172 rpack
= (const Packet
*) response
.GetData();
173 if( rpack
->command
== SB_COMMAND_DB_DATA
&& rpack
->size
> 10 ) {
174 // second packet is generally large, and contains
176 m_commandTable
.Clear();
177 m_commandTable
.Parse(response
, 6);
181 ddout(m_commandTable
);
184 void Controller::LoadDBDB()
186 assert( m_mode
== Desktop
);
189 packet
.socket
= m_socket
.GetSocket();
191 packet
.command
= SB_COMMAND_DB_DATA
;
192 packet
.u
.db
.tableCmd
= GetCommand(DatabaseAccess
);
193 // packet.u.db.u.command.operation = SB_DBOP_GET_DBDB;
194 packet
.u
.db
.u
.command
.operation
= SB_DBOP_OLD_GET_DBDB
;
196 Data
command(&packet
, packet
.size
);
199 if( !m_socket
.Packet(command
, response
) ) {
200 eeout(command
, response
);
201 throw BError(m_socket
.GetLastStatus(),
202 "Controller: error getting database database");
205 MAKE_PACKET(rpack
, response
);
206 while( rpack
->command
!= SB_COMMAND_DB_DONE
) {
207 if( rpack
->command
== SB_COMMAND_DB_DATA
) {
209 m_dbdb
.Parse(response
);
213 if( !m_socket
.NextRecord(response
) ) {
214 eout("Response packet:\n" << response
);
215 throw BError(m_socket
.GetLastStatus(),
216 "Controller: error getting command table(next)");
218 rpack
= (const Packet
*) response
.GetData();
223 ///////////////////////////////////////////////////////////////////////////////
229 /// Get numeric database ID by name.
231 /// \param[in] name Name of database, which matches one of the
232 /// names listed in GetDBDB()
234 /// \exception Barry::BError
235 /// Thrown if name not found.
237 unsigned int Controller::GetDBID(const std::string
&name
) const
240 // FIXME - this needs a better error handler...
241 if( !m_dbdb
.GetDBNumber(name
, ID
) ) {
242 throw BError("Controller: database name not found");
250 /// Select device mode. This is required before using any other mode-based
251 /// operations, such as GetDBDB() and LoadDatabase(). Currently only
252 /// Desktop mode is supported, but the following modes are available.
255 /// - Controller::Bypass
256 /// - Controller::Desktop
257 /// - Controller::JavaLoader
259 /// \exception Barry::BError
260 /// Thrown on protocol error.
262 /// \exception std::logic_error()
263 /// Thrown if unsupported mode is requested.
265 void Controller::OpenMode(ModeType mode
)
270 if( m_mode
!= mode
) {
272 SelectMode(mode
, socket
, flag
);
273 m_socket
.Open(socket
, flag
);
282 // get database database
287 throw std::logic_error("Mode not implemented");
295 /// Retrieve a database from the handheld device, using the given parser
296 /// to parse the resulting data, and optionally store it.
298 /// See the RecordParser<> template to create a parser object. The
299 /// RecordParser<> template allows custom storage based on the type of
300 /// database record retrieved. The database ID and the parser Record
303 /// \param[in] dbId Database Database ID - use GetDBID()
304 /// \param[out] parser Parser object which parses the resulting
305 /// protocol data, and optionally stores it in
306 /// a custom fashion. See the RecordParser<>
309 /// \exception Barry::BError
310 /// Thrown on protocol error.
312 /// \exception std::logic_error
313 /// Thrown if not in Desktop mode.
315 void Controller::LoadDatabase(unsigned int dbId
, Parser
&parser
)
317 if( m_mode
!= Desktop
)
318 throw std::logic_error("Wrong mode in LoadDatabase");
321 packet
.socket
= m_socket
.GetSocket();
323 packet
.command
= SB_COMMAND_DB_DATA
;
324 packet
.u
.db
.tableCmd
= GetCommand(DatabaseAccess
);
325 // packet.u.db.u.command.operation = SB_DBOP_GET_RECORDS;
326 packet
.u
.db
.u
.command
.operation
= SB_DBOP_OLD_GET_RECORDS
;
327 packet
.u
.db
.u
.command
.databaseId
= dbId
;
329 Data
command(&packet
, packet
.size
);
332 if( !m_socket
.Packet(command
, response
) ) {
333 eout("Database ID: " << dbId
);
334 eeout(command
, response
);
335 throw BError(m_socket
.GetLastStatus(),
336 "Controller: error loading database");
339 MAKE_PACKET(rpack
, response
);
340 while( response
.GetSize() >= SB_PACKET_HEADER_SIZE
&&
341 rpack
->command
!= SB_COMMAND_DB_DONE
)
343 if( rpack
->command
== SB_COMMAND_DB_DATA
) {
344 // this size is the old header size, since using
346 size_t size
= SB_PACKET_OLD_RESPONSE_HEADER_SIZE
;
347 if( response
.GetSize() >= size
)
348 parser(response
, size
);
352 if( !m_socket
.NextRecord(response
) ) {
353 eout("Response packet:\n" << response
);
354 throw BError(m_socket
.GetLastStatus(),
355 "Controller: error loading database (next)");
357 rpack
= (const Packet
*) response
.GetData();
361 void Controller::SaveDatabase(unsigned int dbId
, Builder
&builder
)
363 if( m_mode
!= Desktop
)
364 throw std::logic_error("Wrong mode in SaveDatabase");
367 packet
.socket
= m_socket
.GetSocket();
369 packet
.command
= SB_COMMAND_DB_DATA
;
370 packet
.u
.db
.tableCmd
= GetCommand(DatabaseAccess
);
371 packet
.u
.db
.u
.command
.operation
= SB_DBOP_CLEAR_DATABASE
;
372 packet
.u
.db
.u
.command
.databaseId
= dbId
;
374 Data
command(&packet
, packet
.size
);
377 if( !m_socket
.Packet(command
, response
) ) {
378 eout("Database ID: " << dbId
);
379 eeout(command
, response
);
380 throw BError(m_socket
.GetLastStatus(),
381 "Controller: error clearing database");
384 // check response to clear command was successful
385 MAKE_PACKET(rpack
, response
);
386 if( rpack
->command
!= SB_COMMAND_DB_DONE
) {
387 throw BError(m_socket
.GetLastStatus(),
388 "Controller: error clearing database, bad response");
391 // loop until builder object has no more data
392 while( builder(command
, SB_PACKET_UPLOAD_HEADER_SIZE
, dbId
) ) {
393 size_t size
= command
.GetSize();
394 unsigned char *data
= command
.GetBuffer(SB_PACKET_UPLOAD_HEADER_SIZE
);
396 // fill in the missing header values
397 MAKE_PACKETPTR_BUF(cpack
, data
);
398 cpack
->socket
= m_socket
.GetSocket();
400 cpack
->command
= SB_COMMAND_DB_DATA
;
401 cpack
->u
.db
.tableCmd
= GetCommand(DatabaseAccess
);
402 cpack
->u
.db
.u
.upload
.operation
= SB_DBOP_SET_RECORD
;
403 cpack
->u
.db
.u
.upload
.databaseId
= dbId
;
404 cpack
->u
.db
.u
.upload
.unknown
= 0; // FIXME - what does this mean? observed 0 and 5 here
406 command
.ReleaseBuffer(size
);
409 if( !m_socket
.Packet(command
, response
) ) {
410 eout("Database ID: " << dbId
);
411 eeout(command
, response
);
412 throw BError(m_socket
.GetLastStatus(),
413 "Controller: error writing to device database");
416 // successful packet transfer, so check the network return code
417 MAKE_PACKET(rpack
, response
);
418 if( rpack
->command
!= SB_COMMAND_DB_DONE
|| rpack
->u
.db
.u
.return_code
!= 0 ) {
419 std::ostringstream oss
;
420 oss
<< "Controller: device responded with error code (command: "
421 << (unsigned int)rpack
->command
<< ", code: "
422 << (unsigned int)rpack
->u
.db
.u
.return_code
<< ")";
423 throw BError(oss
.str());