- changed headers so that any low level protocol-specific sizes and
[barry.git] / src / controller.cc
blob93a64a6017d6d1a11b442c010ad4994e6544b198
1 ///
2 /// \file controller.cc
3 /// High level Barry API class
4 ///
6 /*
7 Copyright (C) 2005, 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"
30 #define __DEBUG_MODE__
31 #include "debug.h"
33 #include <sstream>
35 #include <iomanip>
37 namespace Barry {
40 // Controller constructor
42 /// Constructor for the Controller class. Requires a valid ProbeResult
43 /// object to find the USB device to talk to.
44 ///
45 /// \param[in] device One of the ProbeResult objects from the
46 /// Probe class.
47 ///
48 Controller::Controller(const ProbeResult &device)
49 : m_dev(device.m_dev),
50 m_iface(m_dev, BLACKBERRY_INTERFACE),
51 m_pin(device.m_pin),
52 m_socket(m_dev, WRITE_ENDPOINT, READ_ENDPOINT),
53 m_mode(Unspecified)
55 if( !m_dev.SetConfiguration(BLACKBERRY_CONFIGURATION) )
56 throw BError(m_dev.GetLastError(),
57 "Controller: SetConfiguration failed");
60 Controller::~Controller()
64 ///////////////////////////////////////////////////////////////////////////////
65 // protected members
67 void Controller::SelectMode(ModeType mode, uint16_t &socket, uint8_t &flag)
69 // select mode
70 Packet packet;
71 packet.socket = 0;
72 packet.size = SB_MODE_PACKET_COMMAND_SIZE;
73 packet.command = SB_COMMAND_SELECT_MODE;
74 packet.data.mode.socket = SB_MODE_REQUEST_SOCKET;
75 packet.data.mode.flag = 0x05; // FIXME
76 memset(packet.data.mode.modeName, 0,
77 sizeof(packet.data.mode.modeName));
79 char *modeName = (char *) packet.data.mode.modeName;
80 switch( mode )
82 case Bypass:
83 strcpy(modeName, "RIM Bypass");
84 break;
86 case Desktop:
87 strcpy(modeName, "RIM Desktop");
88 break;
90 case JavaLoader:
91 strcpy(modeName, "RIM_JavaLoader");
92 break;
94 default:
95 throw std::logic_error("Controller: Invalid mode in SelectMode");
96 break;
99 // send mode command before we open, as a default socket is socket 0
100 Data command(&packet, packet.size);
101 Data response;
102 if( !m_socket.Send(command, response) ) {
103 eeout(command, response);
104 throw BError(m_socket.GetLastStatus(),
105 "Controller: error setting desktop mode");
108 // get the data socket number
109 // indicates the socket number that
110 // should be used below in the Open() call
111 CheckSize(response, SB_MODE_PACKET_RESPONSE_SIZE);
112 MAKE_PACKET(modepack, response);
113 if( modepack->command != SB_COMMAND_MODE_SELECTED ) {
114 eeout(command, response);
115 throw BError("Controller: mode not selected");
118 // return the socket and flag that the device is expecting us to use
119 socket = modepack->data.mode.socket;
120 flag = modepack->data.mode.flag + 1;
123 unsigned int Controller::GetCommand(CommandType ct)
125 unsigned int cmd = 0;
126 char *cmdName = "Unknown";
128 switch( ct )
130 case DatabaseAccess:
131 cmdName = "Database Access";
132 cmd = m_commandTable.GetCommand(cmdName);
133 break;
134 default:
135 throw std::logic_error("Controller: unknown command type");
138 if( cmd == 0 ) {
139 std::ostringstream oss;
140 oss << "Controller: unable to get command code: " << cmdName;
141 throw BError(oss.str());
144 return cmd;
147 void Controller::LoadCommandTable()
149 assert( m_mode == Desktop );
151 char rawCommand[] = { 6, 0, 0x0a, 0, 0x40, 0, 0, 1, 0, 0 };
152 *((uint16_t*) rawCommand) = m_socket.GetSocket();
154 Data command(rawCommand, sizeof(rawCommand));
155 Data response;
156 if( !m_socket.Packet(command, response) ) {
157 eeout(command, response);
158 throw BError(m_socket.GetLastStatus(),
159 "Controller: error getting command table");
162 MAKE_PACKET(firstpack, response);
163 while( firstpack->command != SB_COMMAND_DB_DONE ) {
164 if( !m_socket.NextRecord(response) ) {
165 eout("Response packet:\n" << response);
166 throw BError(m_socket.GetLastStatus(),
167 "Controller: error getting command table(next)");
170 MAKE_PACKET(rpack, response);
171 if( rpack->command == SB_COMMAND_DB_DATA && rpack->size > 10 ) {
172 // second packet is generally large, and contains
173 // the command table
174 m_commandTable.Clear();
175 m_commandTable.Parse(response, 6);
179 ddout(m_commandTable);
182 void Controller::LoadDBDB()
184 assert( m_mode == Desktop );
186 Packet packet;
187 packet.socket = m_socket.GetSocket();
188 packet.size = 7;
189 packet.command = SB_COMMAND_DB_DATA;
190 packet.data.db.tableCmd = GetCommand(DatabaseAccess);
191 // packet.data.db.data.db.operation = SB_DBOP_GET_DBDB;
192 packet.data.db.data.db.operation = SB_DBOP_OLD_GET_DBDB;
194 Data command(&packet, packet.size);
195 Data response;
197 if( !m_socket.Packet(command, response) ) {
198 eeout(command, response);
199 throw BError(m_socket.GetLastStatus(),
200 "Controller: error getting database database");
203 MAKE_PACKET(rpack, response);
204 while( rpack->command != SB_COMMAND_DB_DONE ) {
205 if( rpack->command == SB_COMMAND_DB_DATA ) {
206 m_dbdb.Clear();
207 m_dbdb.Parse(response);
210 // advance!
211 if( !m_socket.NextRecord(response) ) {
212 eout("Response packet:\n" << response);
213 throw BError(m_socket.GetLastStatus(),
214 "Controller: error getting command table(next)");
216 rpack = (const Packet *) response.GetData();
221 ///////////////////////////////////////////////////////////////////////////////
222 // public API
225 // GetDBID
227 /// Get numeric database ID by name.
229 /// \param[in] name Name of database, which matches one of the
230 /// names listed in GetDBDB()
232 /// \exception Barry::BError
233 /// Thrown if name not found.
235 unsigned int Controller::GetDBID(const std::string &name) const
237 unsigned int ID = m_dbdb.GetDBNumber(name);
238 // FIXME - this needs a better error handler... the dbdb needs one too!
239 if( ID == 0 ) {
240 throw BError("Controller: Address Book not found");
242 return ID;
246 // OpenMode
248 /// Select device mode. This is required before using any other mode-based
249 /// operations, such as GetDBDB() and LoadDatabase(). Currently only
250 /// Desktop mode is supported, but the following modes are available.
251 /// (See ModeType)
253 /// - Controller::Bypass
254 /// - Controller::Desktop
255 /// - Controller::JavaLoader
257 /// \exception Barry::BError
258 /// Thrown on protocol error.
260 /// \exception std::logic_error()
261 /// Thrown if unsupported mode is requested.
263 void Controller::OpenMode(ModeType mode)
265 uint16_t socket;
266 uint8_t flag;
268 if( m_mode != mode ) {
269 m_socket.Close();
270 SelectMode(mode, socket, flag);
271 m_socket.Open(socket, flag);
272 m_mode = mode;
274 switch( m_mode )
276 case Desktop:
277 // get command table
278 LoadCommandTable();
280 // get database database
281 LoadDBDB();
282 break;
284 default:
285 throw std::logic_error("Mode not implemented");
291 // LoadDatabase
293 /// Retrieve a database from the handheld device, using the given parser
294 /// to parse the resulting data, and optionally store it.
296 /// See the RecordParser<> template to create a parser object. The
297 /// RecordParser<> template allows custom storage based on the type of
298 /// database record retrieved. The database ID and the parser Record
299 /// type must match.
301 /// \param[in] dbId Database Database ID - use GetDBID()
302 /// \param[out] parser Parser object which parses the resulting
303 /// protocol data, and optionally stores it in
304 /// a custom fashion. See the RecordParser<>
305 /// template.
307 /// \exception Barry::BError
308 /// Thrown on protocol error.
310 /// \exception std::logic_error
311 /// Thrown if not in Desktop mode.
313 void Controller::LoadDatabase(unsigned int dbId, Parser &parser)
315 if( m_mode != Desktop )
316 throw std::logic_error("Wrong mode in LoadDatabase");
318 Packet packet;
319 packet.socket = m_socket.GetSocket();
320 packet.size = 9;
321 packet.command = SB_COMMAND_DB_DATA;
322 packet.data.db.tableCmd = GetCommand(DatabaseAccess);
323 // packet.data.db.data.db.operation = SB_DBOP_GET_RECORDS;
324 packet.data.db.data.db.operation = SB_DBOP_OLD_GET_RECORDS;
325 packet.data.db.data.db.databaseId = dbId;
327 Data command(&packet, packet.size);
328 Data response;
330 if( !m_socket.Packet(command, response) ) {
331 eout("Database ID: " << dbId);
332 eeout(command, response);
333 throw BError(m_socket.GetLastStatus(),
334 "Controller: error loading database");
337 MAKE_PACKET(rpack, response);
338 while( rpack->command != SB_COMMAND_DB_DONE ) {
339 if( rpack->command == SB_COMMAND_DB_DATA ) {
340 parser(response);
343 // advance!
344 if( !m_socket.NextRecord(response) ) {
345 eout("Response packet:\n" << response);
346 throw BError(m_socket.GetLastStatus(),
347 "Controller: error loading database (next)");
349 rpack = (const Packet *) response.GetData();
354 } // namespace Barry