1 /*
2  * File: loader.cpp
3  *
4  * Copyright (c) 2011 Eric J. Holmes, Orion Telescopes & Binoculars
5  *
6  */
7 
8 #ifdef HAVE_CONFIG_H
9 #   include "config.h"
10 #endif
11 
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <libusb.h>
16 #if !defined(_MSC_VER)
17 # include <unistd.h>
18 #endif
19 
20 #include "openssag.h"
21 #include "openssag_priv.h"
22 #include "firmware.h"
23 
24 #define CPUCS_ADDRESS 0xe600
25 
26 /* USB commands to control device */
27 enum USB_REQUEST {
28     USB_RQ_LOAD_FIRMWARE = 0xa0,
29     USB_RQ_WRITE_SMALL_EEPROM = 0xa2
30 };
31 
32 using namespace OpenSSAG;
33 
34 /* Bootloader data */
35 static unsigned char bootloader[] = { SSAG_BOOTLOADER };
36 /* Firmware data */
37 static unsigned char firmware[] = { SSAG_FIRMWARE };
38 /* EEPROM data (shouldn't be needed) */
39 static unsigned char eeprom[] = { SSAG_EEPROM };
40 
Loader()41 Loader::Loader()
42     :
43     handle(NULL)
44 {
45 }
46 
~Loader()47 Loader::~Loader()
48 {
49     Disconnect();
50 }
51 
Connect(int loader_vid,int loader_pid)52 bool Loader::Connect(int loader_vid, int loader_pid)
53 {
54     if (usb_open_device(&this->handle, loader_vid, loader_pid, NULL))
55         return true;
56 
57     return false;
58 }
59 
Disconnect()60 void Loader::Disconnect()
61 {
62     if (this->handle) {
63         libusb_close(this->handle);
64         this->handle = NULL;
65     }
66 }
67 
EnterResetMode()68 void Loader::EnterResetMode()
69 {
70     char data = 0x01;
71     //usb_control_msg(this->handle, 0x40, USB_RQ_LOAD_FIRMWARE, 0x7f92, 0, &data, 1, 5000);
72     int r = libusb_control_transfer(
73         this->handle,
74         0x40 & 0xff,
75         USB_RQ_LOAD_FIRMWARE & 0xff,
76         0x7f92 & 0xffff,
77         0 & 0xffff,
78         (unsigned char*)&data,
79         1,
80         5000);
81 
82     if (r < 0)
83     {
84         DBG("Loader::EnterResetMode: error sending command (1)");
85     }
86 
87 
88     //usb_control_msg(this->handle, 0x40, USB_RQ_LOAD_FIRMWARE, CPUCS_ADDRESS, 0, &data, 1, 5000);
89     r = libusb_control_transfer(
90         this->handle,
91         0x40 & 0xff,
92         USB_RQ_LOAD_FIRMWARE & 0xff,
93         CPUCS_ADDRESS & 0xffff,
94         0 & 0xffff,
95         (unsigned char*)&data,
96         1,
97         5000);
98 
99     if (r < 0)
100     {
101         DBG("Loader::EnterResetMode: error sending command (2)");
102     }
103 }
104 
ExitResetMode()105 void Loader::ExitResetMode()
106 {
107     char data = 0x00;
108     //usb_control_msg(this->handle, 0x40, USB_RQ_LOAD_FIRMWARE, 0x7f92, 0, &data, 1, 5000);
109     int r = libusb_control_transfer(
110         this->handle,
111         0x40 & 0xff,
112         USB_RQ_LOAD_FIRMWARE & 0xff,
113         0x7f92 & 0xffff,
114         0 & 0xffff,
115         (unsigned char*)&data,
116         1,
117         5000);
118 
119     if (r < 0)
120     {
121         DBG("Loader::ExitResetMode: error sending command (1)");
122     }
123 
124 
125     //usb_control_msg(this->handle, 0x40, USB_RQ_LOAD_FIRMWARE, CPUCS_ADDRESS, 0, &data, 1, 5000);
126     r = libusb_control_transfer(
127         this->handle,
128         0x40 & 0xff,
129         USB_RQ_LOAD_FIRMWARE & 0xff,
130         CPUCS_ADDRESS & 0xffff,
131         0 & 0xffff,
132         (unsigned char*)&data,
133         1,
134         5000);
135 
136     if (r < 0)
137     {
138         DBG("Loader::ExitResetMode: error sending command (2)");
139     }
140 
141 }
142 
Upload(unsigned char * data)143 bool Loader::Upload(unsigned char *data)
144 {
145     for (;;) {
146 
147         /*
148         unsigned char byte_count = *data;
149         if (byte_count == 0)
150             break;
151         unsigned short address = *(unsigned int *)(data+1);
152         int received = 0;
153         if ((received = usb_control_msg(this->handle, 0x40, USB_RQ_LOAD_FIRMWARE, address, 0, (char *)(data+3), byte_count, 5000)) != byte_count) {
154             DBG("ERROR:  Tried to send %d bytes of data but device reported back with %d\n", byte_count, received);
155             return false;
156         }
157         data += byte_count + 3;
158         */
159 
160 
161         unsigned char byte_count = *data;
162         if (byte_count == 0)
163             break;
164         unsigned short address = *(unsigned int *)(data+1);
165         int received = 0;
166 
167         //usb_control_msg(this->handle, 0x40, USB_RQ_LOAD_FIRMWARE, address, 0, (char *)(data+3), byte_count, 5000))
168         received = libusb_control_transfer(
169             this->handle,
170             0x40 & 0xff,
171             USB_RQ_LOAD_FIRMWARE & 0xff,
172             address & 0xffff,
173             0 & 0xffff,
174             (unsigned char*)(data+3),
175             byte_count,
176             5000);
177 
178 
179         //if ((received = usb_control_msg(this->handle, 0x40, USB_RQ_LOAD_FIRMWARE, address, 0, (char *)(data+3), byte_count, 5000)) != byte_count) {
180 
181         if (received != byte_count) {
182 
183             DBG("ERROR:  Tried to send %d bytes of data but device reported back with %d\n", byte_count, received);
184             return false;
185         }
186         data += byte_count + 3;
187 
188 
189 
190     }
191     return true;
192 }
193 
LoadFirmware()194 bool Loader::LoadFirmware()
195 {
196     /* Load bootloader */
197     this->EnterResetMode();
198     this->EnterResetMode();
199     DBG("Loading bootloader...");
200     if (!this->Upload(bootloader))
201         return false;
202     DBG("done\n");
203     this->ExitResetMode(); /* Transfer execution to the reset vector */
204 
205     sleep(1); /* Wait for renumeration */
206 
207     /* Load firmware */
208     this->EnterResetMode();
209     DBG("Loading firmware...");
210     if (!this->Upload(firmware))
211         return false;
212     DBG("done\n");
213     this->EnterResetMode(); /* Make sure the CPU is in reset */
214     this->ExitResetMode(); /* Transfer execution to the reset vector */
215 
216     return true;
217 }
218 
LoadEEPROM()219 bool Loader::LoadEEPROM()
220 {
221     size_t length = *eeprom;
222     char *data = (char *)(eeprom+3);
223     //usb_control_msg(this->handle, 0x40, USB_RQ_WRITE_SMALL_EEPROM, 0x00, 0xBEEF, data, length, 5000);
224     int received = libusb_control_transfer(
225             this->handle,
226             0x40 & 0xff,
227             USB_RQ_WRITE_SMALL_EEPROM & 0xff,
228             0x00 & 0xffff,
229             0xBEEF & 0xffff,
230             (unsigned char*)(data),
231             length,
232             5000);
233     if(received != length)
234     {
235         DBG("ERROR: during the loading of the EEPROM");
236     }
237     return true;
238 }
239