- updated time conversion calls to match opensync's latest SVN
[barry.git] / src / controller.cc
blob4bf157b8892c0ceed1557a61865e4584569d9674
1 ///
2 /// \file controller.cc
3 /// High level Barry API class
4 ///
6 /*
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"
23 #include "common.h"
24 #include "protocol.h"
25 #include "protostructs.h"
26 #include "error.h"
27 #include "data.h"
28 #include "parser.h"
29 #include "builder.h"
31 #define __DEBUG_MODE__
32 #include "debug.h"
34 #include <sstream>
36 #include <iomanip>
38 namespace Barry {
41 // Controller constructor
43 /// Constructor for the Controller class. Requires a valid ProbeResult
44 /// object to find the USB device to talk to.
45 ///
46 /// \param[in] device One of the ProbeResult objects from the
47 /// Probe class.
48 ///
49 Controller::Controller(const ProbeResult &device)
50 : m_dev(device.m_dev),
51 m_pin(device.m_pin),
52 m_socket(m_dev, device.m_ep.write, device.m_ep.read),
53 m_mode(Unspecified)
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()
64 delete m_iface;
67 ///////////////////////////////////////////////////////////////////////////////
68 // protected members
70 void Controller::SelectMode(ModeType mode, uint16_t &socket, uint8_t &flag)
72 // select mode
73 Packet packet;
74 packet.socket = 0;
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;
82 switch( mode )
84 case Bypass:
85 strcpy(modeName, "RIM Bypass");
86 break;
88 case Desktop:
89 strcpy(modeName, "RIM Desktop");
90 break;
92 case JavaLoader:
93 strcpy(modeName, "RIM_JavaLoader");
94 break;
96 default:
97 throw std::logic_error("Controller: Invalid mode in SelectMode");
98 break;
101 // send mode command before we open, as a default socket is socket 0
102 Data command(&packet, packet.size);
103 Data response;
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";
130 switch( ct )
132 case DatabaseAccess:
133 cmdName = "Database Access";
134 cmd = m_commandTable.GetCommand(cmdName);
135 break;
136 default:
137 throw std::logic_error("Controller: unknown command type");
140 if( cmd == 0 ) {
141 std::ostringstream oss;
142 oss << "Controller: unable to get command code: " << cmdName;
143 throw BError(oss.str());
146 return cmd;
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));
157 Data response;
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
175 // the command table
176 m_commandTable.Clear();
177 m_commandTable.Parse(response, 6);
181 ddout(m_commandTable);
184 void Controller::LoadDBDB()
186 assert( m_mode == Desktop );
188 Packet packet;
189 packet.socket = m_socket.GetSocket();
190 packet.size = 7;
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);
197 Data response;
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 ) {
208 m_dbdb.Clear();
209 m_dbdb.Parse(response);
212 // advance!
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 ///////////////////////////////////////////////////////////////////////////////
224 // public API
227 // GetDBID
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
239 unsigned int ID = 0;
240 // FIXME - this needs a better error handler...
241 if( !m_dbdb.GetDBNumber(name, ID) ) {
242 throw BError("Controller: database name not found");
244 return ID;
248 // OpenMode
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.
253 /// (See ModeType)
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)
267 uint16_t socket;
268 uint8_t flag;
270 if( m_mode != mode ) {
271 m_socket.Close();
272 SelectMode(mode, socket, flag);
273 m_socket.Open(socket, flag);
274 m_mode = mode;
276 switch( m_mode )
278 case Desktop:
279 // get command table
280 LoadCommandTable();
282 // get database database
283 LoadDBDB();
284 break;
286 default:
287 throw std::logic_error("Mode not implemented");
293 // LoadDatabase
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
301 /// type must match.
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<>
307 /// template.
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");
320 Packet packet;
321 packet.socket = m_socket.GetSocket();
322 packet.size = 9;
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);
330 Data response;
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
345 // old command above
346 size_t size = SB_PACKET_OLD_RESPONSE_HEADER_SIZE;
347 if( response.GetSize() >= size )
348 parser(response, size);
351 // advance!
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");
366 Packet packet;
367 packet.socket = m_socket.GetSocket();
368 packet.size = 9;
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);
375 Data response;
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();
399 cpack->size = size;
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);
408 // write
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");
415 else {
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());
430 } // namespace Barry