1 |
/* |
---|
2 |
* Copyright (C) 2009 by Pieter Palmers |
---|
3 |
* |
---|
4 |
* This file is part of FFADO |
---|
5 |
* FFADO = Free FireWire (pro-)audio drivers for linux |
---|
6 |
* |
---|
7 |
* FFADO is based upon FreeBoB. |
---|
8 |
* |
---|
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) version 3 of the License. |
---|
13 |
* |
---|
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. See the |
---|
17 |
* GNU General Public License for more details. |
---|
18 |
* |
---|
19 |
* You should have received a copy of the GNU General Public License |
---|
20 |
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
---|
21 |
* |
---|
22 |
*/ |
---|
23 |
|
---|
24 |
#include "scs.h" |
---|
25 |
|
---|
26 |
#include "libieee1394/configrom.h" |
---|
27 |
#include "libieee1394/ieee1394service.h" |
---|
28 |
|
---|
29 |
#include "libutil/ByteSwap.h" |
---|
30 |
#include "libutil/Functors.h" |
---|
31 |
|
---|
32 |
namespace GenericAVC { |
---|
33 |
namespace Stanton { |
---|
34 |
|
---|
35 |
ScsDevice::ScsDevice( DeviceManager& d, |
---|
36 |
ffado_smartptr<ConfigRom>( configRom )) |
---|
37 |
: GenericAVC::Device( d , configRom) |
---|
38 |
, m_hss1394handler( NULL ) |
---|
39 |
{ |
---|
40 |
debugOutput( DEBUG_LEVEL_VERBOSE, "Created GenericAVC::Stanton::ScsDevice (NodeID %d)\n", |
---|
41 |
getConfigRom().getNodeId() ); |
---|
42 |
} |
---|
43 |
|
---|
44 |
ScsDevice::~ScsDevice() |
---|
45 |
{ |
---|
46 |
if (m_hss1394handler) { |
---|
47 |
get1394Service().unregisterARMHandler(m_hss1394handler); |
---|
48 |
delete m_hss1394handler; |
---|
49 |
m_hss1394handler = NULL; |
---|
50 |
} |
---|
51 |
} |
---|
52 |
|
---|
53 |
bool |
---|
54 |
ScsDevice::discover() |
---|
55 |
{ |
---|
56 |
// HSS1394 is now being handled by an ALSA MIDI driver. It is therefore |
---|
57 |
// not appropriate that FFADO install an ARM to receive these messages; |
---|
58 |
// doing so will clearly interfere with the other driver. |
---|
59 |
// if(!initMessageHandler()) { |
---|
60 |
// debugError("Could not initialize HSS1394 message handler\n"); |
---|
61 |
// return false; |
---|
62 |
// } |
---|
63 |
return Device::discover(); |
---|
64 |
} |
---|
65 |
|
---|
66 |
bool |
---|
67 |
ScsDevice::initMessageHandler() { |
---|
68 |
fb_nodeaddr_t addr = HSS1394_BASE_ADDRESS; |
---|
69 |
quadlet_t cmdBuffer[2]; |
---|
70 |
|
---|
71 |
memset(cmdBuffer, 0, sizeof(cmdBuffer)); |
---|
72 |
// read the current value present in the register, i.e. read-ping |
---|
73 |
if(!readRegBlock(addr, (quadlet_t *)cmdBuffer, 1) ) { |
---|
74 |
debugError("Could not read from addr 0x%012" PRIX64 "\n", addr); |
---|
75 |
} else { |
---|
76 |
int version = cmdBuffer[0] & 0xFFFF; |
---|
77 |
debugOutput(DEBUG_LEVEL_VERBOSE, "Read Ping response: %08X, Version: %d\n", cmdBuffer[0], version); |
---|
78 |
if((cmdBuffer[0] >> 24 & 0xFF) != HSS1394_CMD_PING_RESPONSE) { |
---|
79 |
debugWarning("Bogus device response to ping! (%08X)\n", cmdBuffer[0]); |
---|
80 |
} |
---|
81 |
} |
---|
82 |
|
---|
83 |
// do a write ping |
---|
84 |
memset(cmdBuffer, 0, sizeof(cmdBuffer)); |
---|
85 |
cmdBuffer[0] |= HSS1394_CMD_PING << 24; |
---|
86 |
|
---|
87 |
// execute the command |
---|
88 |
if(!writeRegBlock(addr, (quadlet_t *)cmdBuffer, 1)) { |
---|
89 |
debugError("Could not write to addr 0x%012" PRIX64 "\n", addr); |
---|
90 |
} else { |
---|
91 |
debugOutput(DEBUG_LEVEL_VERBOSE, "Write Ping succeeded\n"); |
---|
92 |
} |
---|
93 |
|
---|
94 |
// get a notifier to handle device notifications |
---|
95 |
nodeaddr_t notify_address; |
---|
96 |
notify_address = get1394Service().findFreeARMBlock( |
---|
97 |
HSS1394_RESPONSE_ADDRESS, |
---|
98 |
HSS1394_MAX_PACKET_SIZE+1, |
---|
99 |
HSS1394_MAX_PACKET_SIZE+1); |
---|
100 |
|
---|
101 |
if (notify_address == 0xFFFFFFFFFFFFFFFFLLU) { |
---|
102 |
debugError("Could not find free ARM block for notification\n"); |
---|
103 |
return false; |
---|
104 |
} |
---|
105 |
|
---|
106 |
m_hss1394handler = new ScsDevice::HSS1394Handler(*this, notify_address); |
---|
107 |
|
---|
108 |
if(!m_hss1394handler) { |
---|
109 |
debugError("Could not allocate notifier\n"); |
---|
110 |
return false; |
---|
111 |
} |
---|
112 |
|
---|
113 |
if (!get1394Service().registerARMHandler(m_hss1394handler)) { |
---|
114 |
debugError("Could not register HSS1394 handler\n"); |
---|
115 |
delete m_hss1394handler; |
---|
116 |
m_hss1394handler = NULL; |
---|
117 |
return false; |
---|
118 |
} |
---|
119 |
|
---|
120 |
// configure device to use the allocated ARM address for communication |
---|
121 |
// the address change command |
---|
122 |
cmdBuffer[0] = 0; |
---|
123 |
cmdBuffer[0] |= HSS1394_CMD_CHANGE_ADDRESS << 24; |
---|
124 |
|
---|
125 |
// the address is the argument |
---|
126 |
cmdBuffer[0] |= (notify_address >> 32) & 0xFFFF; |
---|
127 |
cmdBuffer[1] = notify_address & 0xFFFFFFFF; |
---|
128 |
|
---|
129 |
// execute the command |
---|
130 |
if(!writeRegBlock(addr, (quadlet_t *)cmdBuffer, 2)) { |
---|
131 |
debugError("Could not write to addr 0x%012" PRIX64 "\n", addr); |
---|
132 |
return false; |
---|
133 |
} |
---|
134 |
|
---|
135 |
// request the device to echo something |
---|
136 |
cmdBuffer[0] = 0; |
---|
137 |
cmdBuffer[0] |= HSS1394_CMD_ECHO_AS_USER_DATA << 24; |
---|
138 |
|
---|
139 |
// the address is the argument |
---|
140 |
cmdBuffer[0] |= 0x1234; |
---|
141 |
cmdBuffer[1] = 0x56789ABC; |
---|
142 |
|
---|
143 |
// execute the command |
---|
144 |
if(!writeRegBlock(addr, (quadlet_t *)cmdBuffer, 2)) { |
---|
145 |
debugError("Could not write to addr 0x%012" PRIX64 "\n", addr); |
---|
146 |
return false; |
---|
147 |
} |
---|
148 |
|
---|
149 |
return true; |
---|
150 |
} |
---|
151 |
|
---|
152 |
bool |
---|
153 |
ScsDevice::writeHSS1394Message(enum eMessageType message_type, byte_t* buffer, size_t len) { |
---|
154 |
debugOutput(DEBUG_LEVEL_VERY_VERBOSE, |
---|
155 |
"Writing message type: %02X, length: %zd bytes\n", |
---|
156 |
message_type, len); |
---|
157 |
size_t len_quadlets = len/4 + 1; |
---|
158 |
|
---|
159 |
fb_nodeaddr_t addr = HSS1394_BASE_ADDRESS; |
---|
160 |
|
---|
161 |
unsigned char tmpbuffer[len_quadlets*4]; |
---|
162 |
tmpbuffer[0] = message_type; |
---|
163 |
memcpy(tmpbuffer+1, buffer, len); |
---|
164 |
// memcpy(tmpbuffer, buffer, len); |
---|
165 |
|
---|
166 |
quadlet_t *cmdBuffer = (quadlet_t *)tmpbuffer; |
---|
167 |
|
---|
168 |
hexDump(tmpbuffer, len_quadlets*4); |
---|
169 |
// we have to cond-byte-swap this because the memory stuff is assumed to be |
---|
170 |
// in big-endian |
---|
171 |
byteSwapFromBus(cmdBuffer, len_quadlets); |
---|
172 |
|
---|
173 |
// execute the command |
---|
174 |
if(!writeRegBlock(addr, (quadlet_t *)cmdBuffer, len_quadlets)) { |
---|
175 |
debugError("Could not write to addr 0x%012" PRIX64 "\n", addr); |
---|
176 |
return false; |
---|
177 |
} |
---|
178 |
return true; |
---|
179 |
} |
---|
180 |
|
---|
181 |
bool |
---|
182 |
ScsDevice::readRegBlock(fb_nodeaddr_t addr, fb_quadlet_t *data, size_t length_quads, size_t blocksize_quads) { |
---|
183 |
debugOutput(DEBUG_LEVEL_VERBOSE,"Reading register 0x%016" PRIX64 ", length %zd quadlets, to %p\n", |
---|
184 |
addr, length_quads, data); |
---|
185 |
|
---|
186 |
fb_nodeid_t nodeId = getNodeId() | 0xFFC0; |
---|
187 |
int quads_done = 0; |
---|
188 |
while(quads_done < (int)length_quads) { |
---|
189 |
fb_nodeaddr_t curr_addr = addr + quads_done*4; |
---|
190 |
fb_quadlet_t *curr_data = data + quads_done; |
---|
191 |
int quads_todo = length_quads - quads_done; |
---|
192 |
if (quads_todo > (int)blocksize_quads) { |
---|
193 |
debugOutput(DEBUG_LEVEL_VERBOSE, "Truncating read from %d to %zd quadlets\n", quads_todo, blocksize_quads); |
---|
194 |
quads_todo = blocksize_quads; |
---|
195 |
} |
---|
196 |
#ifdef DEBUG |
---|
197 |
if (quads_todo < 0) { |
---|
198 |
debugError("BUG: quads_todo < 0: %d\n", quads_todo); |
---|
199 |
} |
---|
200 |
#endif |
---|
201 |
|
---|
202 |
debugOutput(DEBUG_LEVEL_VERBOSE, "reading addr: 0x%016" PRIX64 ", %d quads to %p\n", curr_addr, quads_todo, curr_data); |
---|
203 |
if(!get1394Service().read( nodeId, curr_addr, quads_todo, curr_data ) ) { |
---|
204 |
debugError("Could not read %d quadlets from node 0x%04X addr 0x%012" PRIX64 "\n", quads_todo, nodeId, curr_addr); |
---|
205 |
return false; |
---|
206 |
} |
---|
207 |
|
---|
208 |
quads_done += quads_todo; |
---|
209 |
} |
---|
210 |
byteSwapFromBus(data, length_quads); |
---|
211 |
return true; |
---|
212 |
} |
---|
213 |
|
---|
214 |
bool |
---|
215 |
ScsDevice::writeRegBlock(fb_nodeaddr_t addr, fb_quadlet_t *data, size_t length_quads, size_t blocksize_quads) { |
---|
216 |
debugOutput(DEBUG_LEVEL_VERY_VERBOSE, |
---|
217 |
"Writing register 0x%016" PRIX64 ", length: %zd quadlets, from %p\n", |
---|
218 |
addr, length_quads, data); |
---|
219 |
|
---|
220 |
fb_quadlet_t data_out[length_quads]; |
---|
221 |
memcpy(data_out, data, length_quads*4); |
---|
222 |
byteSwapToBus(data_out, length_quads); |
---|
223 |
|
---|
224 |
fb_nodeid_t nodeId = getNodeId() | 0xFFC0; |
---|
225 |
int quads_done = 0; |
---|
226 |
while(quads_done < (int)length_quads) { |
---|
227 |
fb_nodeaddr_t curr_addr = addr + quads_done*4; |
---|
228 |
fb_quadlet_t *curr_data = data_out + quads_done; |
---|
229 |
int quads_todo = length_quads - quads_done; |
---|
230 |
if (quads_todo > (int)blocksize_quads) { |
---|
231 |
debugOutput(DEBUG_LEVEL_VERBOSE, "Truncating write from %d to %zd quadlets\n", quads_todo, blocksize_quads); |
---|
232 |
quads_todo = blocksize_quads; |
---|
233 |
} |
---|
234 |
#ifdef DEBUG |
---|
235 |
if (quads_todo < 0) { |
---|
236 |
debugError("BUG: quads_todo < 0: %d\n", quads_todo); |
---|
237 |
} |
---|
238 |
#endif |
---|
239 |
|
---|
240 |
debugOutput(DEBUG_LEVEL_VERBOSE, "writing addr: 0x%016" PRIX64 ", %d quads from %p\n", curr_addr, quads_todo, curr_data); |
---|
241 |
if(!get1394Service().write( nodeId, addr, quads_todo, curr_data ) ) { |
---|
242 |
debugError("Could not write %d quadlets to node 0x%04X addr 0x%012" PRIX64 "\n", quads_todo, nodeId, curr_addr); |
---|
243 |
return false; |
---|
244 |
} |
---|
245 |
quads_done += quads_todo; |
---|
246 |
} |
---|
247 |
|
---|
248 |
return true; |
---|
249 |
} |
---|
250 |
|
---|
251 |
void |
---|
252 |
ScsDevice::showDevice() |
---|
253 |
{ |
---|
254 |
debugOutput(DEBUG_LEVEL_VERBOSE, "This is a GenericAVC::Stanton::ScsDevice\n"); |
---|
255 |
GenericAVC::Device::showDevice(); |
---|
256 |
} |
---|
257 |
|
---|
258 |
// the incoming HSS1394 handler |
---|
259 |
|
---|
260 |
ScsDevice::HSS1394Handler::HSS1394Handler(Device &d, nodeaddr_t start) |
---|
261 |
: ARMHandler(d.get1394Service(), start, HSS1394_MAX_PACKET_SIZE+1, |
---|
262 |
RAW1394_ARM_READ | RAW1394_ARM_WRITE | RAW1394_ARM_LOCK, |
---|
263 |
RAW1394_ARM_WRITE, 0) |
---|
264 |
, m_device(d) |
---|
265 |
{ |
---|
266 |
// switch over the debug module to that of this device instead of the 1394 service |
---|
267 |
m_debugModule = d.m_debugModule; |
---|
268 |
|
---|
269 |
} |
---|
270 |
|
---|
271 |
ScsDevice::HSS1394Handler::~HSS1394Handler() |
---|
272 |
{ |
---|
273 |
} |
---|
274 |
|
---|
275 |
bool |
---|
276 |
ScsDevice::HSS1394Handler::handleRead(struct raw1394_arm_request *arm_req) { |
---|
277 |
debugWarning("Unexpected Read transaction received\n"); |
---|
278 |
printRequest(arm_req); |
---|
279 |
return true; |
---|
280 |
} |
---|
281 |
|
---|
282 |
bool |
---|
283 |
ScsDevice::HSS1394Handler::handleWrite(struct raw1394_arm_request *arm_req) { |
---|
284 |
debugOutput(DEBUG_LEVEL_VERBOSE, "HSS Write\n"); |
---|
285 |
printRequest(arm_req); |
---|
286 |
size_t payload_len = 0; |
---|
287 |
enum eMessageType message_type = eMT_Undefined; |
---|
288 |
|
---|
289 |
// extract the data |
---|
290 |
if(arm_req->buffer_length > 1) { |
---|
291 |
message_type = byteToMessageType(arm_req->buffer[0]); |
---|
292 |
payload_len = arm_req->buffer_length - 1; |
---|
293 |
} else { |
---|
294 |
debugWarning("Received empty message\n"); |
---|
295 |
return false; |
---|
296 |
} |
---|
297 |
|
---|
298 |
// figure out what handler to call |
---|
299 |
switch(message_type) { |
---|
300 |
case eMT_UserData: |
---|
301 |
for ( MessageHandlerVectorIterator it = m_userDataMessageHandlers.begin(); |
---|
302 |
it != m_userDataMessageHandlers.end(); |
---|
303 |
++it ) |
---|
304 |
{ |
---|
305 |
MessageFunctor* func = *it; |
---|
306 |
debugOutput(DEBUG_LEVEL_VERBOSE, "Calling functor %p\n", func); |
---|
307 |
( *func )(arm_req->buffer + 1, payload_len); |
---|
308 |
} |
---|
309 |
break; |
---|
310 |
case eMT_DebugData: |
---|
311 |
case eMT_UserTagBase: |
---|
312 |
case eMT_UserTagTop: |
---|
313 |
case eMT_Reset: |
---|
314 |
case eMT_ChangeAddress: |
---|
315 |
case eMT_Ping: |
---|
316 |
case eMT_PingResponse: |
---|
317 |
case eMT_EchoAsUserData: |
---|
318 |
case eMT_Undefined: |
---|
319 |
default: |
---|
320 |
debugWarning("Unexpected Message of type: %02X\n", message_type); |
---|
321 |
} |
---|
322 |
return true; |
---|
323 |
} |
---|
324 |
|
---|
325 |
bool |
---|
326 |
ScsDevice::HSS1394Handler::handleLock(struct raw1394_arm_request *arm_req) { |
---|
327 |
debugWarning("Unexpected Lock transaction received\n"); |
---|
328 |
printRequest(arm_req); |
---|
329 |
return true; |
---|
330 |
} |
---|
331 |
|
---|
332 |
bool |
---|
333 |
ScsDevice::HSS1394Handler::addMessageHandler(enum eMessageType message_type, MessageFunctor* functor) |
---|
334 |
{ |
---|
335 |
debugOutput(DEBUG_LEVEL_VERBOSE, "Adding Message handler (%p) for message type %02X\n", functor, message_type); |
---|
336 |
// figure out what handler to call |
---|
337 |
switch(message_type) { |
---|
338 |
case eMT_UserData: |
---|
339 |
// FIXME: one handler can be added multiple times, is this what we want? |
---|
340 |
m_userDataMessageHandlers.push_back( functor ); |
---|
341 |
return true; |
---|
342 |
case eMT_DebugData: |
---|
343 |
case eMT_UserTagBase: |
---|
344 |
case eMT_UserTagTop: |
---|
345 |
case eMT_Reset: |
---|
346 |
case eMT_ChangeAddress: |
---|
347 |
case eMT_Ping: |
---|
348 |
case eMT_PingResponse: |
---|
349 |
case eMT_EchoAsUserData: |
---|
350 |
case eMT_Undefined: |
---|
351 |
default: |
---|
352 |
debugError("Handlers not supported for messages of type: %02X\n", message_type); |
---|
353 |
return false; |
---|
354 |
} |
---|
355 |
} |
---|
356 |
|
---|
357 |
bool |
---|
358 |
ScsDevice::HSS1394Handler::removeMessageHandler(enum eMessageType message_type, MessageFunctor* functor) |
---|
359 |
{ |
---|
360 |
debugOutput(DEBUG_LEVEL_VERBOSE, "Removing Message handler (%p) for message type %02X\n", functor, message_type); |
---|
361 |
// figure out what handler to call |
---|
362 |
switch(message_type) { |
---|
363 |
case eMT_UserData: |
---|
364 |
// FIXME: one handler can be present multiple times, how do we handle this? |
---|
365 |
for ( MessageHandlerVectorIterator it = m_userDataMessageHandlers.begin(); |
---|
366 |
it != m_userDataMessageHandlers.end(); |
---|
367 |
++it ) |
---|
368 |
{ |
---|
369 |
if ( *it == functor ) { |
---|
370 |
debugOutput(DEBUG_LEVEL_VERBOSE, " found\n"); |
---|
371 |
m_userDataMessageHandlers.erase( it ); |
---|
372 |
return true; |
---|
373 |
} |
---|
374 |
} |
---|
375 |
debugOutput(DEBUG_LEVEL_VERBOSE, " not found\n"); |
---|
376 |
return false; |
---|
377 |
case eMT_DebugData: |
---|
378 |
case eMT_UserTagBase: |
---|
379 |
case eMT_UserTagTop: |
---|
380 |
case eMT_Reset: |
---|
381 |
case eMT_ChangeAddress: |
---|
382 |
case eMT_Ping: |
---|
383 |
case eMT_PingResponse: |
---|
384 |
case eMT_EchoAsUserData: |
---|
385 |
case eMT_Undefined: |
---|
386 |
default: |
---|
387 |
debugError("Handlers not supported for messages of type: %02X\n", message_type); |
---|
388 |
return false; |
---|
389 |
} |
---|
390 |
} |
---|
391 |
|
---|
392 |
enum ScsDevice::eMessageType |
---|
393 |
ScsDevice::HSS1394Handler::byteToMessageType(byte_t tag) |
---|
394 |
{ |
---|
395 |
switch(tag) { |
---|
396 |
case HSS1394_CMD_USER_DATA: |
---|
397 |
return eMT_UserData; |
---|
398 |
case HSS1394_CMD_DEBUG_DATA: |
---|
399 |
return eMT_DebugData; |
---|
400 |
case HSS1394_CMD_USER_TAG_BASE: |
---|
401 |
return eMT_UserTagBase; |
---|
402 |
case HSS1394_CMD_USER_TAG_TOP: |
---|
403 |
return eMT_UserTagTop; |
---|
404 |
case HSS1394_CMD_RESET: |
---|
405 |
return eMT_Reset; |
---|
406 |
case HSS1394_CMD_CHANGE_ADDRESS: |
---|
407 |
return eMT_ChangeAddress; |
---|
408 |
case HSS1394_CMD_PING: |
---|
409 |
return eMT_Ping; |
---|
410 |
case HSS1394_CMD_PING_RESPONSE: |
---|
411 |
return eMT_PingResponse; |
---|
412 |
case HSS1394_CMD_ECHO_AS_USER_DATA: |
---|
413 |
return eMT_EchoAsUserData; |
---|
414 |
case HSS1394_CMD_UNDEFINED: |
---|
415 |
default: |
---|
416 |
return eMT_Undefined; |
---|
417 |
} |
---|
418 |
} |
---|
419 |
|
---|
420 |
} |
---|
421 |
} |
---|