]>
Commit | Line | Data |
---|---|---|
ee38a438 GI |
1 | /* |
2 | * dev_areca.cpp | |
3 | * | |
a86ec89e | 4 | * Home page of code is: http://www.smartmontools.org |
ee38a438 GI |
5 | * |
6 | * Copyright (C) 2012 Hank Wu <hank@areca.com.tw> | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License as published by | |
10 | * the Free Software Foundation; either version 2, or (at your option) | |
11 | * any later version. | |
12 | * | |
13 | * You should have received a copy of the GNU General Public License | |
14 | * (for example COPYING); If not, see <http://www.gnu.org/licenses/>. | |
15 | * | |
16 | */ | |
17 | ||
18 | #include "config.h" | |
19 | #include "int64.h" | |
20 | ||
21 | #include "dev_interface.h" | |
22 | #include "dev_areca.h" | |
23 | ||
f9e10201 | 24 | const char * dev_areca_cpp_cvsid = "$Id: dev_areca.cpp 4582 2017-11-03 20:54:56Z chrfranke $" |
ee38a438 GI |
25 | DEV_ARECA_H_CVSID; |
26 | ||
27 | #include "atacmds.h" | |
28 | #include "scsicmds.h" | |
29 | ||
30 | #include <errno.h> | |
31 | ||
32 | #if 0 // For debugging areca code | |
33 | static void dumpdata(unsigned char *block, int len) | |
34 | { | |
35 | int ln = (len / 16) + 1; // total line# | |
36 | unsigned char c; | |
37 | int pos = 0; | |
38 | ||
39 | printf(" Address = %p, Length = (0x%x)%d\n", block, len, len); | |
40 | printf(" 0 1 2 3 4 5 6 7 8 9 A B C D E F ASCII \n"); | |
41 | printf("=====================================================================\n"); | |
42 | ||
43 | for ( int l = 0; l < ln && len; l++ ) | |
44 | { | |
45 | // printf the line# and the HEX data | |
46 | // if a line data length < 16 then append the space to the tail of line to reach 16 chars | |
47 | printf("%02X | ", l); | |
48 | for ( pos = 0; pos < 16 && len; pos++, len-- ) | |
49 | { | |
50 | c = block[l*16+pos]; | |
51 | printf("%02X ", c); | |
52 | } | |
53 | ||
54 | if ( pos < 16 ) | |
55 | { | |
56 | for ( int loop = pos; loop < 16; loop++ ) | |
57 | { | |
58 | printf(" "); | |
59 | } | |
60 | } | |
61 | ||
62 | // print ASCII char | |
63 | for ( int loop = 0; loop < pos; loop++ ) | |
64 | { | |
65 | c = block[l*16+loop]; | |
66 | if ( c >= 0x20 && c <= 0x7F ) | |
67 | { | |
68 | printf("%c", c); | |
69 | } | |
70 | else | |
71 | { | |
72 | printf("."); | |
73 | } | |
74 | } | |
75 | printf("\n"); | |
76 | } | |
77 | printf("=====================================================================\n"); | |
78 | } | |
79 | #endif | |
80 | ||
81 | generic_areca_device::generic_areca_device(smart_interface * intf, const char * dev_name, int disknum, int encnum) | |
82 | : smart_device(intf, dev_name, "areca", "areca"), | |
83 | m_disknum(disknum), | |
84 | m_encnum(encnum) | |
85 | { | |
86 | set_info().info_name = strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name, disknum, encnum); | |
87 | } | |
88 | ||
89 | generic_areca_device::~generic_areca_device() throw() | |
90 | { | |
91 | ||
92 | } | |
93 | ||
94 | // PURPOSE | |
95 | // This is an interface routine meant to isolate the OS dependent | |
96 | // parts of the code, and to provide a debugging interface. Each | |
97 | // different port and OS needs to provide it's own interface. This | |
98 | // is the Windows interface to the Areca "arcmsr" driver. It allows ATA | |
99 | // commands to be passed through the SCSI driver. | |
100 | // DETAILED DESCRIPTION OF ARGUMENTS | |
101 | // fd: is the file descriptor provided by open() | |
102 | // disknum is the disk number (0 to 127) in the RAID array | |
103 | // command: defines the different operations. | |
104 | // select: additional input data if needed (which log, which type of | |
105 | // self-test). | |
106 | // data: location to write output data, if needed (512 bytes). | |
107 | // Note: not all commands use all arguments. | |
108 | // RETURN VALUES | |
109 | // -1 if the command failed | |
110 | // 0 if the command succeeded, | |
111 | // STATUS_CHECK routine: | |
112 | // -1 if the command failed | |
113 | // 0 if the command succeeded and disk SMART status is "OK" | |
114 | // 1 if the command succeeded and disk SMART status is "FAILING" | |
115 | int generic_areca_device::arcmsr_command_handler(unsigned long arcmsr_cmd, unsigned char *data, int data_len) | |
116 | { | |
d2e702cf GI |
117 | if (arcmsr_cmd >= ARCMSR_CMD_TOTAL) |
118 | return -1; | |
119 | ||
120 | static const unsigned int cmds[ARCMSR_CMD_TOTAL] = | |
ee38a438 GI |
121 | { |
122 | ARCMSR_IOCTL_READ_RQBUFFER, | |
123 | ARCMSR_IOCTL_WRITE_WQBUFFER, | |
124 | ARCMSR_IOCTL_CLEAR_RQBUFFER, | |
125 | ARCMSR_IOCTL_CLEAR_WQBUFFER, | |
126 | ARCMSR_IOCTL_RETURN_CODE_3F | |
127 | }; | |
128 | ||
129 | int ioctlreturn = 0; | |
130 | sSRB_BUFFER sBuf; | |
131 | struct scsi_cmnd_io iop; | |
132 | int dir = DXFER_TO_DEVICE; | |
133 | ||
134 | UINT8 cdb[10]={0}; | |
135 | UINT8 sense[32]={0}; | |
136 | ||
137 | unsigned char *areca_return_packet; | |
138 | int total = 0; | |
139 | int expected = -1; | |
140 | unsigned char return_buff[2048]={0}; | |
141 | unsigned char *ptr = &return_buff[0]; | |
142 | ||
143 | memset((unsigned char *)&sBuf, 0, sizeof(sBuf)); | |
144 | memset(&iop, 0, sizeof(iop)); | |
145 | ||
146 | sBuf.srbioctl.HeaderLength = sizeof(sARCMSR_IO_HDR); | |
147 | memcpy(sBuf.srbioctl.Signature, ARECA_SIG_STR, strlen(ARECA_SIG_STR)); | |
148 | sBuf.srbioctl.Timeout = 10000; | |
149 | sBuf.srbioctl.ControlCode = cmds[arcmsr_cmd]; | |
150 | ||
ee38a438 GI |
151 | switch ( arcmsr_cmd ) |
152 | { | |
153 | // command for writing data to driver | |
154 | case ARCMSR_WRITE_WQBUFFER: | |
155 | if ( data && data_len ) | |
156 | { | |
157 | sBuf.srbioctl.Length = data_len; | |
158 | memcpy((unsigned char *)sBuf.ioctldatabuffer, (unsigned char *)data, data_len); | |
159 | } | |
f9e10201 | 160 | /* FALLTHRU */ |
ee38a438 GI |
161 | // commands for clearing related buffer of driver |
162 | case ARCMSR_CLEAR_RQBUFFER: | |
163 | case ARCMSR_CLEAR_WQBUFFER: | |
164 | cdb[0] = 0x3B; //SCSI_WRITE_BUF command; | |
165 | break; | |
166 | // command for reading data from driver | |
167 | case ARCMSR_READ_RQBUFFER: | |
168 | // command for identifying driver | |
169 | case ARCMSR_RETURN_CODE_3F: | |
170 | cdb[0] = 0x3C; //SCSI_READ_BUF command; | |
171 | dir = DXFER_FROM_DEVICE; | |
172 | break; | |
173 | default: | |
174 | // unknown arcmsr commands | |
175 | return -1; | |
176 | } | |
177 | ||
178 | cdb[1] = 0x01; | |
179 | cdb[2] = 0xf0; | |
180 | cdb[5] = cmds[arcmsr_cmd] >> 24; | |
181 | cdb[6] = cmds[arcmsr_cmd] >> 16; | |
182 | cdb[7] = cmds[arcmsr_cmd] >> 8; | |
183 | cdb[8] = cmds[arcmsr_cmd] & 0x0F; | |
184 | ||
185 | iop.dxfer_dir = dir; | |
186 | iop.dxfer_len = sizeof(sBuf); | |
187 | iop.dxferp = (unsigned char *)&sBuf; | |
188 | iop.cmnd = cdb; | |
189 | iop.cmnd_len = sizeof(cdb); | |
190 | iop.sensep = sense; | |
191 | iop.max_sense_len = sizeof(sense); | |
192 | iop.timeout = SCSI_TIMEOUT_DEFAULT; | |
193 | ||
194 | while ( 1 ) | |
195 | { | |
196 | ioctlreturn = arcmsr_do_scsi_io(&iop); | |
197 | if(ioctlreturn || iop.scsi_status) | |
198 | { | |
199 | break; | |
200 | } | |
201 | ||
202 | if ( arcmsr_cmd != ARCMSR_READ_RQBUFFER ) | |
203 | { | |
204 | // if succeeded, just returns the length of outgoing data | |
205 | return data_len; | |
206 | } | |
207 | ||
208 | if ( sBuf.srbioctl.Length ) | |
209 | { | |
210 | memcpy(ptr, &sBuf.ioctldatabuffer[0], sBuf.srbioctl.Length); | |
211 | ptr += sBuf.srbioctl.Length; | |
212 | total += sBuf.srbioctl.Length; | |
213 | // the returned bytes enough to compute payload length ? | |
214 | if ( expected < 0 && total >= 5 ) | |
215 | { | |
216 | areca_return_packet = (unsigned char *)&return_buff[0]; | |
217 | if ( areca_return_packet[0] == 0x5E && | |
218 | areca_return_packet[1] == 0x01 && | |
219 | areca_return_packet[2] == 0x61 ) | |
220 | { | |
221 | // valid header, let's compute the returned payload length, | |
222 | // we expected the total length is | |
223 | // payload + 3 bytes header + 2 bytes length + 1 byte checksum | |
224 | expected = areca_return_packet[4] * 256 + areca_return_packet[3] + 6; | |
225 | } | |
226 | } | |
227 | ||
228 | if ( total >= 7 && total >= expected ) | |
229 | { | |
230 | //printf("total bytes received = %d, expected length = %d\n", total, expected); | |
231 | ||
232 | // ------ Okay! we received enough -------- | |
233 | break; | |
234 | } | |
235 | } | |
236 | } | |
237 | ||
238 | // Deal with the different error cases | |
239 | if ( arcmsr_cmd == ARCMSR_RETURN_CODE_3F ) | |
240 | { | |
241 | // Silence the ARCMSR_IOCTL_RETURN_CODE_3F's error, no pout(...) | |
242 | return -4; | |
243 | } | |
244 | ||
245 | if ( ioctlreturn ) | |
246 | { | |
247 | pout("do_scsi_cmnd_io with write buffer failed code = %x\n", ioctlreturn); | |
248 | return -2; | |
249 | } | |
250 | ||
251 | if ( iop.scsi_status ) | |
252 | { | |
253 | pout("io_hdr.scsi_status with write buffer failed code = %x\n", iop.scsi_status); | |
254 | return -3; | |
255 | } | |
256 | ||
257 | if ( data ) | |
258 | { | |
259 | memcpy(data, return_buff, total); | |
260 | } | |
261 | ||
262 | return total; | |
263 | } | |
264 | ||
265 | bool generic_areca_device::arcmsr_probe() | |
266 | { | |
267 | if(!is_open()) | |
268 | { | |
269 | open(); | |
270 | } | |
271 | ||
272 | if(arcmsr_command_handler(ARCMSR_RETURN_CODE_3F, NULL, 0) != 0) | |
273 | { | |
274 | return false; | |
275 | } | |
276 | return true; | |
277 | } | |
278 | ||
279 | int generic_areca_device::arcmsr_ui_handler(unsigned char *areca_packet, int areca_packet_len, unsigned char *result) | |
280 | { | |
281 | int expected = 0; | |
282 | unsigned char return_buff[2048]; | |
283 | unsigned char cs = 0; | |
284 | int cs_pos = 0; | |
285 | ||
286 | // ----- ADD CHECKSUM ----- | |
287 | cs_pos = areca_packet_len - 1; | |
288 | for(int i = 3; i < cs_pos; i++) | |
289 | { | |
290 | areca_packet[cs_pos] += areca_packet[i]; | |
291 | } | |
292 | ||
293 | if(!arcmsr_lock()) | |
294 | { | |
295 | return -1; | |
296 | } | |
297 | expected = arcmsr_command_handler(ARCMSR_CLEAR_RQBUFFER, NULL, 0); | |
298 | if (expected==-3) { | |
299 | return set_err(EIO); | |
300 | } | |
a86ec89e | 301 | arcmsr_command_handler(ARCMSR_CLEAR_WQBUFFER, NULL, 0); |
ee38a438 GI |
302 | expected = arcmsr_command_handler(ARCMSR_WRITE_WQBUFFER, areca_packet, areca_packet_len); |
303 | if ( expected > 0 ) | |
304 | { | |
305 | expected = arcmsr_command_handler(ARCMSR_READ_RQBUFFER, return_buff, sizeof(return_buff)); | |
306 | } | |
307 | ||
a86ec89e | 308 | if ( expected < 3 + 1 ) // Prefix + Checksum |
ee38a438 GI |
309 | { |
310 | return -1; | |
311 | } | |
312 | ||
313 | if(!arcmsr_unlock()) | |
314 | { | |
315 | return -1; | |
316 | } | |
317 | ||
318 | // ----- VERIFY THE CHECKSUM ----- | |
319 | cs = 0; | |
320 | for ( int loop = 3; loop < expected - 1; loop++ ) | |
321 | { | |
322 | cs += return_buff[loop]; | |
323 | } | |
324 | ||
325 | if ( return_buff[expected - 1] != cs ) | |
326 | { | |
327 | return -1; | |
328 | } | |
329 | ||
330 | memcpy(result, return_buff, expected); | |
331 | ||
332 | return expected; | |
333 | } | |
334 | ||
335 | int generic_areca_device::arcmsr_get_controller_type() | |
336 | { | |
337 | int expected = 0; | |
338 | unsigned char return_buff[2048]; | |
339 | unsigned char areca_packet[] = {0x5E, 0x01, 0x61, 0x01, 0x00, 0x23, 0x00}; | |
340 | ||
341 | memset(return_buff, 0, sizeof(return_buff)); | |
342 | expected = arcmsr_ui_handler(areca_packet, sizeof(areca_packet), return_buff); | |
343 | if ( expected < 0 ) | |
344 | { | |
345 | return -1; | |
346 | } | |
347 | ||
348 | return return_buff[0xc2]; | |
349 | } | |
350 | ||
351 | int generic_areca_device::arcmsr_get_dev_type() | |
352 | { | |
353 | int expected = 0; | |
354 | unsigned char return_buff[2048]; | |
355 | int ctlr_type = -1; | |
356 | int encnum = get_encnum(); | |
357 | int disknum = get_disknum(); | |
358 | unsigned char areca_packet[] = {0x5E, 0x01, 0x61, 0x03, 0x00, 0x22, | |
359 | (unsigned char)(disknum - 1), (unsigned char)(encnum - 1), 0x00}; | |
360 | ||
361 | memset(return_buff, 0, sizeof(return_buff)); | |
362 | expected = arcmsr_ui_handler(areca_packet, sizeof(areca_packet), return_buff); | |
363 | if ( expected < 0 ) | |
364 | { | |
365 | return -1; | |
366 | } | |
367 | ||
368 | ctlr_type = arcmsr_get_controller_type(); | |
369 | ||
370 | if( ctlr_type < 0 ) | |
371 | { | |
372 | return ctlr_type; | |
373 | } | |
374 | ||
375 | if( ctlr_type == 0x02/* SATA Controllers */ || | |
376 | (ctlr_type == 0x03 /* SAS Controllers */ && return_buff[0x52] & 0x01 /* SATA devices behind SAS Controller */) ) | |
377 | { | |
378 | // SATA device | |
379 | return 1; | |
380 | } | |
381 | ||
382 | // SAS device | |
383 | return 0; | |
384 | } | |
385 | ||
386 | bool generic_areca_device::arcmsr_ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) | |
387 | { | |
388 | // ATA input registers | |
389 | typedef struct _ATA_INPUT_REGISTERS | |
390 | { | |
391 | unsigned char features; | |
392 | unsigned char sector_count; | |
393 | unsigned char sector_number; | |
394 | unsigned char cylinder_low; | |
395 | unsigned char cylinder_high; | |
396 | unsigned char device_head; | |
397 | unsigned char command; | |
398 | unsigned char reserved[8]; | |
399 | unsigned char data[512]; // [in/out] buffer for outgoing/incoming data | |
400 | } sATA_INPUT_REGISTERS; | |
401 | ||
402 | // ATA output registers | |
403 | // Note: The output registers is re-sorted for areca internal use only | |
404 | typedef struct _ATA_OUTPUT_REGISTERS | |
405 | { | |
406 | unsigned char error; | |
407 | unsigned char status; | |
408 | unsigned char sector_count; | |
409 | unsigned char sector_number; | |
410 | unsigned char cylinder_low; | |
411 | unsigned char cylinder_high; | |
412 | } sATA_OUTPUT_REGISTERS; | |
413 | ||
414 | // Areca packet format for outgoing: | |
415 | // B[0~2] : 3 bytes header, fixed value 0x5E, 0x01, 0x61 | |
416 | // B[3~4] : 2 bytes command length + variant data length, little endian | |
417 | // B[5] : 1 bytes areca defined command code, ATA passthrough command code is 0x1c | |
418 | // B[6~last-1] : variant bytes payload data | |
419 | // B[last] : 1 byte checksum, simply sum(B[3] ~ B[last -1]) | |
420 | // | |
421 | // | |
422 | // header 3 bytes length 2 bytes cmd 1 byte payload data x bytes cs 1 byte | |
423 | // +--------------------------------------------------------------------------------+ | |
424 | // + 0x5E 0x01 0x61 | 0x00 0x00 | 0x1c | .................... | 0x00 | | |
425 | // +--------------------------------------------------------------------------------+ | |
426 | // | |
427 | ||
428 | //Areca packet format for incoming: | |
429 | // B[0~2] : 3 bytes header, fixed value 0x5E, 0x01, 0x61 | |
430 | // B[3~4] : 2 bytes payload length, little endian | |
431 | // B[5~last-1] : variant bytes returned payload data | |
432 | // B[last] : 1 byte checksum, simply sum(B[3] ~ B[last -1]) | |
433 | // | |
434 | // | |
435 | // header 3 bytes length 2 bytes payload data x bytes cs 1 byte | |
436 | // +-------------------------------------------------------------------+ | |
437 | // + 0x5E 0x01 0x61 | 0x00 0x00 | .................... | 0x00 | | |
438 | // +-------------------------------------------------------------------+ | |
439 | unsigned char areca_packet[640]; | |
440 | int areca_packet_len = sizeof(areca_packet); | |
441 | unsigned char return_buff[2048]; | |
442 | int expected = 0; | |
443 | ||
444 | sATA_INPUT_REGISTERS *ata_cmd; | |
445 | ||
446 | // For debugging | |
447 | #if 0 | |
448 | memset(sInq, 0, sizeof(sInq)); | |
449 | scsiStdInquiry(fd, (unsigned char *)sInq, (int)sizeof(sInq)); | |
450 | dumpdata((unsigned char *)sInq, sizeof(sInq)); | |
451 | #endif | |
452 | memset(areca_packet, 0, areca_packet_len); | |
453 | ||
454 | // ----- BEGIN TO SETUP HEADERS ------- | |
455 | areca_packet[0] = 0x5E; | |
456 | areca_packet[1] = 0x01; | |
457 | areca_packet[2] = 0x61; | |
458 | areca_packet[3] = (unsigned char)((areca_packet_len - 6) & 0xff); | |
459 | areca_packet[4] = (unsigned char)(((areca_packet_len - 6) >> 8) & 0xff); | |
460 | areca_packet[5] = 0x1c; // areca defined code for ATA passthrough command | |
461 | ||
462 | // ----- BEGIN TO SETUP PAYLOAD DATA ----- | |
463 | memcpy(&areca_packet[7], "SmrT", 4); // areca defined password | |
464 | ata_cmd = (sATA_INPUT_REGISTERS *)&areca_packet[12]; | |
465 | ||
466 | // Set registers | |
467 | { | |
468 | const ata_in_regs & r = in.in_regs; | |
469 | ata_cmd->features = r.features; | |
470 | ata_cmd->sector_count = r.sector_count; | |
471 | ata_cmd->sector_number = r.lba_low; | |
472 | ata_cmd->cylinder_low = r.lba_mid; | |
473 | ata_cmd->cylinder_high = r.lba_high; | |
474 | ata_cmd->device_head = r.device; | |
475 | ata_cmd->command = r.command; | |
476 | } | |
477 | bool readdata = false; | |
478 | if (in.direction == ata_cmd_in::data_in) { | |
479 | readdata = true; | |
480 | // the command will read data | |
481 | areca_packet[6] = 0x13; | |
482 | } | |
483 | else if ( in.direction == ata_cmd_in::no_data ) | |
484 | { | |
485 | // the commands will return no data | |
486 | areca_packet[6] = 0x15; | |
487 | } | |
488 | else if (in.direction == ata_cmd_in::data_out) | |
489 | { | |
490 | // the commands will write data | |
491 | memcpy(ata_cmd->data, in.buffer, in.size); | |
492 | areca_packet[6] = 0x14; | |
493 | } | |
494 | else { | |
495 | // COMMAND NOT SUPPORTED VIA ARECA IOCTL INTERFACE | |
496 | return set_err(ENOSYS); | |
497 | } | |
498 | ||
499 | areca_packet[11] = get_disknum() - 1; // disk# | |
500 | areca_packet[19] = get_encnum() - 1; // enc# | |
501 | ||
502 | // ----- BEGIN TO SEND TO ARECA DRIVER ------ | |
503 | expected = arcmsr_ui_handler(areca_packet, areca_packet_len, return_buff); | |
504 | if ( expected < 0 ) | |
505 | { | |
506 | return set_err(EIO); | |
507 | } | |
508 | ||
509 | sATA_OUTPUT_REGISTERS *ata_out = (sATA_OUTPUT_REGISTERS *)&return_buff[5] ; | |
510 | if ( ata_out->status ) | |
511 | { | |
512 | if ( in.in_regs.command == ATA_IDENTIFY_DEVICE | |
513 | && !nonempty((unsigned char *)in.buffer, in.size)) | |
514 | { | |
515 | return set_err(ENODEV, "No drive on port %d", get_disknum()); | |
516 | } | |
517 | } | |
518 | ||
519 | // returns with data | |
520 | if (readdata) | |
521 | { | |
522 | memcpy(in.buffer, &return_buff[7], in.size); | |
523 | } | |
524 | ||
525 | // Return register values | |
526 | { | |
527 | ata_out_regs & r = out.out_regs; | |
528 | r.error = ata_out->error; | |
529 | r.sector_count = ata_out->sector_count; | |
530 | r.lba_low = ata_out->sector_number; | |
531 | r.lba_mid = ata_out->cylinder_low; | |
532 | r.lba_high = ata_out->cylinder_high; | |
533 | r.status = ata_out->status; | |
534 | } | |
535 | return true; | |
536 | } | |
537 | ||
538 | bool generic_areca_device::arcmsr_scsi_pass_through(struct scsi_cmnd_io * iop) | |
539 | { | |
540 | // Areca packet format for outgoing: | |
541 | // B[0~2] : 3 bytes header, fixed value 0x5E, 0x01, 0x61 | |
542 | // B[3~4] : 2 bytes command length + variant data length, little endian | |
543 | // B[5] : 1 bytes areca defined command code | |
544 | // B[6~last-1] : variant bytes payload data | |
545 | // B[last] : 1 byte checksum, simply sum(B[3] ~ B[last -1]) | |
546 | // | |
547 | // | |
548 | // header 3 bytes length 2 bytes cmd 1 byte payload data x bytes cs 1 byte | |
549 | // +--------------------------------------------------------------------------------+ | |
550 | // + 0x5E 0x01 0x61 | 0x00 0x00 | 0x1c | .................... | 0x00 | | |
551 | // +--------------------------------------------------------------------------------+ | |
552 | // | |
553 | ||
554 | //Areca packet format for incoming: | |
555 | // B[0~2] : 3 bytes header, fixed value 0x5E, 0x01, 0x61 | |
556 | // B[3~4] : 2 bytes payload length, little endian | |
557 | // B[5~last-1] : variant bytes returned payload data | |
558 | // B[last] : 1 byte checksum, simply sum(B[3] ~ B[last -1]) | |
559 | // | |
560 | // | |
561 | // header 3 bytes length 2 bytes payload data x bytes cs 1 byte | |
562 | // +-------------------------------------------------------------------+ | |
563 | // + 0x5E 0x01 0x61 | 0x00 0x00 | .................... | 0x00 | | |
564 | // +-------------------------------------------------------------------+ | |
565 | unsigned char areca_packet[640]; | |
566 | int areca_packet_len = sizeof(areca_packet); | |
567 | unsigned char return_buff[2048]; | |
568 | int expected = 0; | |
569 | ||
570 | if (iop->cmnd_len > 16) { | |
571 | set_err(EINVAL, "cmnd_len too large"); | |
572 | return false; | |
573 | } | |
574 | ||
575 | memset(areca_packet, 0, areca_packet_len); | |
576 | ||
577 | // ----- BEGIN TO SETUP HEADERS ------- | |
578 | areca_packet[0] = 0x5E; | |
579 | areca_packet[1] = 0x01; | |
580 | areca_packet[2] = 0x61; | |
581 | areca_packet[3] = (unsigned char)((areca_packet_len - 6) & 0xff); | |
582 | areca_packet[4] = (unsigned char)(((areca_packet_len - 6) >> 8) & 0xff); | |
583 | areca_packet[5] = 0x1c; | |
584 | ||
585 | // ----- BEGIN TO SETUP PAYLOAD DATA ----- | |
586 | areca_packet[6] = 0x16; // scsi pass through | |
587 | memcpy(&areca_packet[7], "SmrT", 4); // areca defined password | |
588 | areca_packet[12] = iop->cmnd_len; // cdb length | |
589 | memcpy( &areca_packet[35], iop->cmnd, iop->cmnd_len); // cdb | |
590 | areca_packet[15] = (unsigned char)iop->dxfer_len; // 15(LSB) ~ 18(MSB): data length ( max=512 bytes) | |
591 | areca_packet[16] = (unsigned char)(iop->dxfer_len >> 8); | |
592 | areca_packet[17] = (unsigned char)(iop->dxfer_len >> 16); | |
593 | areca_packet[18] = (unsigned char)(iop->dxfer_len >> 24); | |
594 | if(iop->dxfer_dir == DXFER_TO_DEVICE) | |
595 | { | |
596 | areca_packet[13] |= 0x01; | |
597 | memcpy(&areca_packet[67], iop->dxferp, iop->dxfer_len); | |
598 | } | |
599 | else if (iop->dxfer_dir == DXFER_FROM_DEVICE) | |
600 | { | |
601 | } | |
602 | else if( iop->dxfer_dir == DXFER_NONE) | |
603 | { | |
604 | } | |
605 | else { | |
606 | // COMMAND NOT SUPPORTED VIA ARECA IOCTL INTERFACE | |
607 | return set_err(ENOSYS); | |
608 | } | |
609 | ||
610 | areca_packet[11] = get_disknum() - 1; // disk# | |
611 | areca_packet[19] = get_encnum() - 1; // enc# | |
612 | ||
613 | // ----- BEGIN TO SEND TO ARECA DRIVER ------ | |
614 | expected = arcmsr_ui_handler(areca_packet, areca_packet_len, return_buff); | |
3d17a85c GI |
615 | |
616 | if (expected < 0) | |
617 | return set_err(EIO, "arcmsr_scsi_pass_through: I/O error"); | |
618 | if (expected < 15) // 7 bytes if port is empty | |
619 | return set_err(EIO, "arcmsr_scsi_pass_through: missing data (%d bytes, expected %d)", expected, 15); | |
ee38a438 GI |
620 | |
621 | int scsi_status = return_buff[5]; | |
622 | int in_data_len = return_buff[11] | return_buff[12] << 8 | return_buff[13] << 16 | return_buff[14] << 24; | |
623 | ||
624 | if (iop->dxfer_dir == DXFER_FROM_DEVICE) | |
625 | { | |
626 | memset(iop->dxferp, 0, iop->dxfer_len); // need? | |
627 | memcpy(iop->dxferp, &return_buff[15], in_data_len); | |
628 | } | |
629 | ||
630 | if(scsi_status == 0xE1 /* Underrun, actual data length < requested data length */) | |
631 | { | |
632 | // don't care, just ignore | |
633 | scsi_status = 0x0; | |
634 | } | |
635 | ||
636 | if(scsi_status != 0x00 && scsi_status != SCSI_STATUS_CHECK_CONDITION) | |
637 | { | |
638 | return set_err(EIO); | |
639 | } | |
640 | ||
641 | if(scsi_status == SCSI_STATUS_CHECK_CONDITION) | |
642 | { | |
643 | // check condition | |
644 | iop->scsi_status = SCSI_STATUS_CHECK_CONDITION; | |
645 | iop->resp_sense_len = 4; | |
646 | iop->sensep[0] = return_buff[7]; | |
647 | iop->sensep[1] = return_buff[8]; | |
648 | iop->sensep[2] = return_buff[9]; | |
649 | iop->sensep[3] = return_buff[10]; | |
650 | } | |
651 | ||
652 | return true; | |
653 | } | |
654 | ||
655 | ///////////////////////////////////////////////////////////// | |
656 | areca_ata_device::areca_ata_device(smart_interface * intf, const char * dev_name, int disknum, int encnum) | |
657 | : smart_device(intf, dev_name, "areca", "areca") | |
658 | { | |
659 | set_encnum(encnum); | |
660 | set_disknum(disknum); | |
661 | set_info().info_name = strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name, disknum, encnum); | |
662 | } | |
663 | ||
664 | areca_ata_device::~areca_ata_device() throw() | |
665 | { | |
666 | ||
667 | } | |
668 | ||
669 | bool areca_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) | |
670 | { | |
671 | if (!ata_cmd_is_supported(in, | |
672 | ata_device::supports_data_out | | |
673 | ata_device::supports_output_regs | | |
674 | //ata_device::supports_multi_sector | // TODO | |
675 | ata_device::supports_48bit_hi_null, | |
676 | "Areca") | |
677 | ) | |
678 | return false; | |
679 | ||
680 | return arcmsr_ata_pass_through(in, out); | |
681 | } | |
682 | ||
683 | ///////////////////////////////////////////////////////////// | |
684 | areca_scsi_device::areca_scsi_device(smart_interface * intf, const char * dev_name, int disknum, int encnum) | |
685 | : smart_device(intf, dev_name, "areca", "areca") | |
686 | { | |
687 | set_encnum(encnum); | |
688 | set_disknum(disknum); | |
689 | set_info().info_name = strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name, disknum, encnum); | |
690 | } | |
691 | ||
692 | areca_scsi_device::~areca_scsi_device() throw() | |
693 | { | |
694 | ||
695 | } | |
696 | ||
697 | bool areca_scsi_device::scsi_pass_through(struct scsi_cmnd_io * iop) | |
698 | { | |
699 | return arcmsr_scsi_pass_through(iop); | |
700 | } | |
701 | ||
702 | ||
703 | ||
704 |