]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * dev_areca.cpp | |
3 | * | |
4 | * Home page of code is: http://www.smartmontools.org | |
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 | ||
24 | const char * dev_areca_cpp_cvsid = "$Id: dev_areca.cpp 4209 2016-01-22 20:49:44Z chrfranke $" | |
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 | { | |
117 | if (arcmsr_cmd >= ARCMSR_CMD_TOTAL) | |
118 | return -1; | |
119 | ||
120 | static const unsigned int cmds[ARCMSR_CMD_TOTAL] = | |
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 | ||
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 | } | |
160 | // commands for clearing related buffer of driver | |
161 | case ARCMSR_CLEAR_RQBUFFER: | |
162 | case ARCMSR_CLEAR_WQBUFFER: | |
163 | cdb[0] = 0x3B; //SCSI_WRITE_BUF command; | |
164 | break; | |
165 | // command for reading data from driver | |
166 | case ARCMSR_READ_RQBUFFER: | |
167 | // command for identifying driver | |
168 | case ARCMSR_RETURN_CODE_3F: | |
169 | cdb[0] = 0x3C; //SCSI_READ_BUF command; | |
170 | dir = DXFER_FROM_DEVICE; | |
171 | break; | |
172 | default: | |
173 | // unknown arcmsr commands | |
174 | return -1; | |
175 | } | |
176 | ||
177 | cdb[1] = 0x01; | |
178 | cdb[2] = 0xf0; | |
179 | cdb[5] = cmds[arcmsr_cmd] >> 24; | |
180 | cdb[6] = cmds[arcmsr_cmd] >> 16; | |
181 | cdb[7] = cmds[arcmsr_cmd] >> 8; | |
182 | cdb[8] = cmds[arcmsr_cmd] & 0x0F; | |
183 | ||
184 | iop.dxfer_dir = dir; | |
185 | iop.dxfer_len = sizeof(sBuf); | |
186 | iop.dxferp = (unsigned char *)&sBuf; | |
187 | iop.cmnd = cdb; | |
188 | iop.cmnd_len = sizeof(cdb); | |
189 | iop.sensep = sense; | |
190 | iop.max_sense_len = sizeof(sense); | |
191 | iop.timeout = SCSI_TIMEOUT_DEFAULT; | |
192 | ||
193 | while ( 1 ) | |
194 | { | |
195 | ioctlreturn = arcmsr_do_scsi_io(&iop); | |
196 | if(ioctlreturn || iop.scsi_status) | |
197 | { | |
198 | break; | |
199 | } | |
200 | ||
201 | if ( arcmsr_cmd != ARCMSR_READ_RQBUFFER ) | |
202 | { | |
203 | // if succeeded, just returns the length of outgoing data | |
204 | return data_len; | |
205 | } | |
206 | ||
207 | if ( sBuf.srbioctl.Length ) | |
208 | { | |
209 | memcpy(ptr, &sBuf.ioctldatabuffer[0], sBuf.srbioctl.Length); | |
210 | ptr += sBuf.srbioctl.Length; | |
211 | total += sBuf.srbioctl.Length; | |
212 | // the returned bytes enough to compute payload length ? | |
213 | if ( expected < 0 && total >= 5 ) | |
214 | { | |
215 | areca_return_packet = (unsigned char *)&return_buff[0]; | |
216 | if ( areca_return_packet[0] == 0x5E && | |
217 | areca_return_packet[1] == 0x01 && | |
218 | areca_return_packet[2] == 0x61 ) | |
219 | { | |
220 | // valid header, let's compute the returned payload length, | |
221 | // we expected the total length is | |
222 | // payload + 3 bytes header + 2 bytes length + 1 byte checksum | |
223 | expected = areca_return_packet[4] * 256 + areca_return_packet[3] + 6; | |
224 | } | |
225 | } | |
226 | ||
227 | if ( total >= 7 && total >= expected ) | |
228 | { | |
229 | //printf("total bytes received = %d, expected length = %d\n", total, expected); | |
230 | ||
231 | // ------ Okay! we received enough -------- | |
232 | break; | |
233 | } | |
234 | } | |
235 | } | |
236 | ||
237 | // Deal with the different error cases | |
238 | if ( arcmsr_cmd == ARCMSR_RETURN_CODE_3F ) | |
239 | { | |
240 | // Silence the ARCMSR_IOCTL_RETURN_CODE_3F's error, no pout(...) | |
241 | return -4; | |
242 | } | |
243 | ||
244 | if ( ioctlreturn ) | |
245 | { | |
246 | pout("do_scsi_cmnd_io with write buffer failed code = %x\n", ioctlreturn); | |
247 | return -2; | |
248 | } | |
249 | ||
250 | if ( iop.scsi_status ) | |
251 | { | |
252 | pout("io_hdr.scsi_status with write buffer failed code = %x\n", iop.scsi_status); | |
253 | return -3; | |
254 | } | |
255 | ||
256 | if ( data ) | |
257 | { | |
258 | memcpy(data, return_buff, total); | |
259 | } | |
260 | ||
261 | return total; | |
262 | } | |
263 | ||
264 | bool generic_areca_device::arcmsr_probe() | |
265 | { | |
266 | if(!is_open()) | |
267 | { | |
268 | open(); | |
269 | } | |
270 | ||
271 | if(arcmsr_command_handler(ARCMSR_RETURN_CODE_3F, NULL, 0) != 0) | |
272 | { | |
273 | return false; | |
274 | } | |
275 | return true; | |
276 | } | |
277 | ||
278 | int generic_areca_device::arcmsr_ui_handler(unsigned char *areca_packet, int areca_packet_len, unsigned char *result) | |
279 | { | |
280 | int expected = 0; | |
281 | unsigned char return_buff[2048]; | |
282 | unsigned char cs = 0; | |
283 | int cs_pos = 0; | |
284 | ||
285 | // ----- ADD CHECKSUM ----- | |
286 | cs_pos = areca_packet_len - 1; | |
287 | for(int i = 3; i < cs_pos; i++) | |
288 | { | |
289 | areca_packet[cs_pos] += areca_packet[i]; | |
290 | } | |
291 | ||
292 | if(!arcmsr_lock()) | |
293 | { | |
294 | return -1; | |
295 | } | |
296 | expected = arcmsr_command_handler(ARCMSR_CLEAR_RQBUFFER, NULL, 0); | |
297 | if (expected==-3) { | |
298 | return set_err(EIO); | |
299 | } | |
300 | arcmsr_command_handler(ARCMSR_CLEAR_WQBUFFER, NULL, 0); | |
301 | expected = arcmsr_command_handler(ARCMSR_WRITE_WQBUFFER, areca_packet, areca_packet_len); | |
302 | if ( expected > 0 ) | |
303 | { | |
304 | expected = arcmsr_command_handler(ARCMSR_READ_RQBUFFER, return_buff, sizeof(return_buff)); | |
305 | } | |
306 | ||
307 | if ( expected < 3 + 1 ) // Prefix + Checksum | |
308 | { | |
309 | return -1; | |
310 | } | |
311 | ||
312 | if(!arcmsr_unlock()) | |
313 | { | |
314 | return -1; | |
315 | } | |
316 | ||
317 | // ----- VERIFY THE CHECKSUM ----- | |
318 | cs = 0; | |
319 | for ( int loop = 3; loop < expected - 1; loop++ ) | |
320 | { | |
321 | cs += return_buff[loop]; | |
322 | } | |
323 | ||
324 | if ( return_buff[expected - 1] != cs ) | |
325 | { | |
326 | return -1; | |
327 | } | |
328 | ||
329 | memcpy(result, return_buff, expected); | |
330 | ||
331 | return expected; | |
332 | } | |
333 | ||
334 | int generic_areca_device::arcmsr_get_controller_type() | |
335 | { | |
336 | int expected = 0; | |
337 | unsigned char return_buff[2048]; | |
338 | unsigned char areca_packet[] = {0x5E, 0x01, 0x61, 0x01, 0x00, 0x23, 0x00}; | |
339 | ||
340 | memset(return_buff, 0, sizeof(return_buff)); | |
341 | expected = arcmsr_ui_handler(areca_packet, sizeof(areca_packet), return_buff); | |
342 | if ( expected < 0 ) | |
343 | { | |
344 | return -1; | |
345 | } | |
346 | ||
347 | return return_buff[0xc2]; | |
348 | } | |
349 | ||
350 | int generic_areca_device::arcmsr_get_dev_type() | |
351 | { | |
352 | int expected = 0; | |
353 | unsigned char return_buff[2048]; | |
354 | int ctlr_type = -1; | |
355 | int encnum = get_encnum(); | |
356 | int disknum = get_disknum(); | |
357 | unsigned char areca_packet[] = {0x5E, 0x01, 0x61, 0x03, 0x00, 0x22, | |
358 | (unsigned char)(disknum - 1), (unsigned char)(encnum - 1), 0x00}; | |
359 | ||
360 | memset(return_buff, 0, sizeof(return_buff)); | |
361 | expected = arcmsr_ui_handler(areca_packet, sizeof(areca_packet), return_buff); | |
362 | if ( expected < 0 ) | |
363 | { | |
364 | return -1; | |
365 | } | |
366 | ||
367 | ctlr_type = arcmsr_get_controller_type(); | |
368 | ||
369 | if( ctlr_type < 0 ) | |
370 | { | |
371 | return ctlr_type; | |
372 | } | |
373 | ||
374 | if( ctlr_type == 0x02/* SATA Controllers */ || | |
375 | (ctlr_type == 0x03 /* SAS Controllers */ && return_buff[0x52] & 0x01 /* SATA devices behind SAS Controller */) ) | |
376 | { | |
377 | // SATA device | |
378 | return 1; | |
379 | } | |
380 | ||
381 | // SAS device | |
382 | return 0; | |
383 | } | |
384 | ||
385 | bool generic_areca_device::arcmsr_ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) | |
386 | { | |
387 | // ATA input registers | |
388 | typedef struct _ATA_INPUT_REGISTERS | |
389 | { | |
390 | unsigned char features; | |
391 | unsigned char sector_count; | |
392 | unsigned char sector_number; | |
393 | unsigned char cylinder_low; | |
394 | unsigned char cylinder_high; | |
395 | unsigned char device_head; | |
396 | unsigned char command; | |
397 | unsigned char reserved[8]; | |
398 | unsigned char data[512]; // [in/out] buffer for outgoing/incoming data | |
399 | } sATA_INPUT_REGISTERS; | |
400 | ||
401 | // ATA output registers | |
402 | // Note: The output registers is re-sorted for areca internal use only | |
403 | typedef struct _ATA_OUTPUT_REGISTERS | |
404 | { | |
405 | unsigned char error; | |
406 | unsigned char status; | |
407 | unsigned char sector_count; | |
408 | unsigned char sector_number; | |
409 | unsigned char cylinder_low; | |
410 | unsigned char cylinder_high; | |
411 | } sATA_OUTPUT_REGISTERS; | |
412 | ||
413 | // Areca packet format for outgoing: | |
414 | // B[0~2] : 3 bytes header, fixed value 0x5E, 0x01, 0x61 | |
415 | // B[3~4] : 2 bytes command length + variant data length, little endian | |
416 | // B[5] : 1 bytes areca defined command code, ATA passthrough command code is 0x1c | |
417 | // B[6~last-1] : variant bytes payload data | |
418 | // B[last] : 1 byte checksum, simply sum(B[3] ~ B[last -1]) | |
419 | // | |
420 | // | |
421 | // header 3 bytes length 2 bytes cmd 1 byte payload data x bytes cs 1 byte | |
422 | // +--------------------------------------------------------------------------------+ | |
423 | // + 0x5E 0x01 0x61 | 0x00 0x00 | 0x1c | .................... | 0x00 | | |
424 | // +--------------------------------------------------------------------------------+ | |
425 | // | |
426 | ||
427 | //Areca packet format for incoming: | |
428 | // B[0~2] : 3 bytes header, fixed value 0x5E, 0x01, 0x61 | |
429 | // B[3~4] : 2 bytes payload length, little endian | |
430 | // B[5~last-1] : variant bytes returned payload data | |
431 | // B[last] : 1 byte checksum, simply sum(B[3] ~ B[last -1]) | |
432 | // | |
433 | // | |
434 | // header 3 bytes length 2 bytes payload data x bytes cs 1 byte | |
435 | // +-------------------------------------------------------------------+ | |
436 | // + 0x5E 0x01 0x61 | 0x00 0x00 | .................... | 0x00 | | |
437 | // +-------------------------------------------------------------------+ | |
438 | unsigned char areca_packet[640]; | |
439 | int areca_packet_len = sizeof(areca_packet); | |
440 | unsigned char return_buff[2048]; | |
441 | int expected = 0; | |
442 | ||
443 | sATA_INPUT_REGISTERS *ata_cmd; | |
444 | ||
445 | // For debugging | |
446 | #if 0 | |
447 | memset(sInq, 0, sizeof(sInq)); | |
448 | scsiStdInquiry(fd, (unsigned char *)sInq, (int)sizeof(sInq)); | |
449 | dumpdata((unsigned char *)sInq, sizeof(sInq)); | |
450 | #endif | |
451 | memset(areca_packet, 0, areca_packet_len); | |
452 | ||
453 | // ----- BEGIN TO SETUP HEADERS ------- | |
454 | areca_packet[0] = 0x5E; | |
455 | areca_packet[1] = 0x01; | |
456 | areca_packet[2] = 0x61; | |
457 | areca_packet[3] = (unsigned char)((areca_packet_len - 6) & 0xff); | |
458 | areca_packet[4] = (unsigned char)(((areca_packet_len - 6) >> 8) & 0xff); | |
459 | areca_packet[5] = 0x1c; // areca defined code for ATA passthrough command | |
460 | ||
461 | // ----- BEGIN TO SETUP PAYLOAD DATA ----- | |
462 | memcpy(&areca_packet[7], "SmrT", 4); // areca defined password | |
463 | ata_cmd = (sATA_INPUT_REGISTERS *)&areca_packet[12]; | |
464 | ||
465 | // Set registers | |
466 | { | |
467 | const ata_in_regs & r = in.in_regs; | |
468 | ata_cmd->features = r.features; | |
469 | ata_cmd->sector_count = r.sector_count; | |
470 | ata_cmd->sector_number = r.lba_low; | |
471 | ata_cmd->cylinder_low = r.lba_mid; | |
472 | ata_cmd->cylinder_high = r.lba_high; | |
473 | ata_cmd->device_head = r.device; | |
474 | ata_cmd->command = r.command; | |
475 | } | |
476 | bool readdata = false; | |
477 | if (in.direction == ata_cmd_in::data_in) { | |
478 | readdata = true; | |
479 | // the command will read data | |
480 | areca_packet[6] = 0x13; | |
481 | } | |
482 | else if ( in.direction == ata_cmd_in::no_data ) | |
483 | { | |
484 | // the commands will return no data | |
485 | areca_packet[6] = 0x15; | |
486 | } | |
487 | else if (in.direction == ata_cmd_in::data_out) | |
488 | { | |
489 | // the commands will write data | |
490 | memcpy(ata_cmd->data, in.buffer, in.size); | |
491 | areca_packet[6] = 0x14; | |
492 | } | |
493 | else { | |
494 | // COMMAND NOT SUPPORTED VIA ARECA IOCTL INTERFACE | |
495 | return set_err(ENOSYS); | |
496 | } | |
497 | ||
498 | areca_packet[11] = get_disknum() - 1; // disk# | |
499 | areca_packet[19] = get_encnum() - 1; // enc# | |
500 | ||
501 | // ----- BEGIN TO SEND TO ARECA DRIVER ------ | |
502 | expected = arcmsr_ui_handler(areca_packet, areca_packet_len, return_buff); | |
503 | if ( expected < 0 ) | |
504 | { | |
505 | return set_err(EIO); | |
506 | } | |
507 | ||
508 | sATA_OUTPUT_REGISTERS *ata_out = (sATA_OUTPUT_REGISTERS *)&return_buff[5] ; | |
509 | if ( ata_out->status ) | |
510 | { | |
511 | if ( in.in_regs.command == ATA_IDENTIFY_DEVICE | |
512 | && !nonempty((unsigned char *)in.buffer, in.size)) | |
513 | { | |
514 | return set_err(ENODEV, "No drive on port %d", get_disknum()); | |
515 | } | |
516 | } | |
517 | ||
518 | // returns with data | |
519 | if (readdata) | |
520 | { | |
521 | memcpy(in.buffer, &return_buff[7], in.size); | |
522 | } | |
523 | ||
524 | // Return register values | |
525 | { | |
526 | ata_out_regs & r = out.out_regs; | |
527 | r.error = ata_out->error; | |
528 | r.sector_count = ata_out->sector_count; | |
529 | r.lba_low = ata_out->sector_number; | |
530 | r.lba_mid = ata_out->cylinder_low; | |
531 | r.lba_high = ata_out->cylinder_high; | |
532 | r.status = ata_out->status; | |
533 | } | |
534 | return true; | |
535 | } | |
536 | ||
537 | bool generic_areca_device::arcmsr_scsi_pass_through(struct scsi_cmnd_io * iop) | |
538 | { | |
539 | // Areca packet format for outgoing: | |
540 | // B[0~2] : 3 bytes header, fixed value 0x5E, 0x01, 0x61 | |
541 | // B[3~4] : 2 bytes command length + variant data length, little endian | |
542 | // B[5] : 1 bytes areca defined command code | |
543 | // B[6~last-1] : variant bytes payload data | |
544 | // B[last] : 1 byte checksum, simply sum(B[3] ~ B[last -1]) | |
545 | // | |
546 | // | |
547 | // header 3 bytes length 2 bytes cmd 1 byte payload data x bytes cs 1 byte | |
548 | // +--------------------------------------------------------------------------------+ | |
549 | // + 0x5E 0x01 0x61 | 0x00 0x00 | 0x1c | .................... | 0x00 | | |
550 | // +--------------------------------------------------------------------------------+ | |
551 | // | |
552 | ||
553 | //Areca packet format for incoming: | |
554 | // B[0~2] : 3 bytes header, fixed value 0x5E, 0x01, 0x61 | |
555 | // B[3~4] : 2 bytes payload length, little endian | |
556 | // B[5~last-1] : variant bytes returned payload data | |
557 | // B[last] : 1 byte checksum, simply sum(B[3] ~ B[last -1]) | |
558 | // | |
559 | // | |
560 | // header 3 bytes length 2 bytes payload data x bytes cs 1 byte | |
561 | // +-------------------------------------------------------------------+ | |
562 | // + 0x5E 0x01 0x61 | 0x00 0x00 | .................... | 0x00 | | |
563 | // +-------------------------------------------------------------------+ | |
564 | unsigned char areca_packet[640]; | |
565 | int areca_packet_len = sizeof(areca_packet); | |
566 | unsigned char return_buff[2048]; | |
567 | int expected = 0; | |
568 | ||
569 | if (iop->cmnd_len > 16) { | |
570 | set_err(EINVAL, "cmnd_len too large"); | |
571 | return false; | |
572 | } | |
573 | ||
574 | memset(areca_packet, 0, areca_packet_len); | |
575 | ||
576 | // ----- BEGIN TO SETUP HEADERS ------- | |
577 | areca_packet[0] = 0x5E; | |
578 | areca_packet[1] = 0x01; | |
579 | areca_packet[2] = 0x61; | |
580 | areca_packet[3] = (unsigned char)((areca_packet_len - 6) & 0xff); | |
581 | areca_packet[4] = (unsigned char)(((areca_packet_len - 6) >> 8) & 0xff); | |
582 | areca_packet[5] = 0x1c; | |
583 | ||
584 | // ----- BEGIN TO SETUP PAYLOAD DATA ----- | |
585 | areca_packet[6] = 0x16; // scsi pass through | |
586 | memcpy(&areca_packet[7], "SmrT", 4); // areca defined password | |
587 | areca_packet[12] = iop->cmnd_len; // cdb length | |
588 | memcpy( &areca_packet[35], iop->cmnd, iop->cmnd_len); // cdb | |
589 | areca_packet[15] = (unsigned char)iop->dxfer_len; // 15(LSB) ~ 18(MSB): data length ( max=512 bytes) | |
590 | areca_packet[16] = (unsigned char)(iop->dxfer_len >> 8); | |
591 | areca_packet[17] = (unsigned char)(iop->dxfer_len >> 16); | |
592 | areca_packet[18] = (unsigned char)(iop->dxfer_len >> 24); | |
593 | if(iop->dxfer_dir == DXFER_TO_DEVICE) | |
594 | { | |
595 | areca_packet[13] |= 0x01; | |
596 | memcpy(&areca_packet[67], iop->dxferp, iop->dxfer_len); | |
597 | } | |
598 | else if (iop->dxfer_dir == DXFER_FROM_DEVICE) | |
599 | { | |
600 | } | |
601 | else if( iop->dxfer_dir == DXFER_NONE) | |
602 | { | |
603 | } | |
604 | else { | |
605 | // COMMAND NOT SUPPORTED VIA ARECA IOCTL INTERFACE | |
606 | return set_err(ENOSYS); | |
607 | } | |
608 | ||
609 | areca_packet[11] = get_disknum() - 1; // disk# | |
610 | areca_packet[19] = get_encnum() - 1; // enc# | |
611 | ||
612 | // ----- BEGIN TO SEND TO ARECA DRIVER ------ | |
613 | expected = arcmsr_ui_handler(areca_packet, areca_packet_len, return_buff); | |
614 | ||
615 | if (expected < 0) | |
616 | return set_err(EIO, "arcmsr_scsi_pass_through: I/O error"); | |
617 | if (expected < 15) // 7 bytes if port is empty | |
618 | return set_err(EIO, "arcmsr_scsi_pass_through: missing data (%d bytes, expected %d)", expected, 15); | |
619 | ||
620 | int scsi_status = return_buff[5]; | |
621 | int in_data_len = return_buff[11] | return_buff[12] << 8 | return_buff[13] << 16 | return_buff[14] << 24; | |
622 | ||
623 | if (iop->dxfer_dir == DXFER_FROM_DEVICE) | |
624 | { | |
625 | memset(iop->dxferp, 0, iop->dxfer_len); // need? | |
626 | memcpy(iop->dxferp, &return_buff[15], in_data_len); | |
627 | } | |
628 | ||
629 | if(scsi_status == 0xE1 /* Underrun, actual data length < requested data length */) | |
630 | { | |
631 | // don't care, just ignore | |
632 | scsi_status = 0x0; | |
633 | } | |
634 | ||
635 | if(scsi_status != 0x00 && scsi_status != SCSI_STATUS_CHECK_CONDITION) | |
636 | { | |
637 | return set_err(EIO); | |
638 | } | |
639 | ||
640 | if(scsi_status == SCSI_STATUS_CHECK_CONDITION) | |
641 | { | |
642 | // check condition | |
643 | iop->scsi_status = SCSI_STATUS_CHECK_CONDITION; | |
644 | iop->resp_sense_len = 4; | |
645 | iop->sensep[0] = return_buff[7]; | |
646 | iop->sensep[1] = return_buff[8]; | |
647 | iop->sensep[2] = return_buff[9]; | |
648 | iop->sensep[3] = return_buff[10]; | |
649 | } | |
650 | ||
651 | return true; | |
652 | } | |
653 | ||
654 | ///////////////////////////////////////////////////////////// | |
655 | areca_ata_device::areca_ata_device(smart_interface * intf, const char * dev_name, int disknum, int encnum) | |
656 | : smart_device(intf, dev_name, "areca", "areca") | |
657 | { | |
658 | set_encnum(encnum); | |
659 | set_disknum(disknum); | |
660 | set_info().info_name = strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name, disknum, encnum); | |
661 | } | |
662 | ||
663 | areca_ata_device::~areca_ata_device() throw() | |
664 | { | |
665 | ||
666 | } | |
667 | ||
668 | bool areca_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) | |
669 | { | |
670 | if (!ata_cmd_is_supported(in, | |
671 | ata_device::supports_data_out | | |
672 | ata_device::supports_output_regs | | |
673 | //ata_device::supports_multi_sector | // TODO | |
674 | ata_device::supports_48bit_hi_null, | |
675 | "Areca") | |
676 | ) | |
677 | return false; | |
678 | ||
679 | return arcmsr_ata_pass_through(in, out); | |
680 | } | |
681 | ||
682 | ///////////////////////////////////////////////////////////// | |
683 | areca_scsi_device::areca_scsi_device(smart_interface * intf, const char * dev_name, int disknum, int encnum) | |
684 | : smart_device(intf, dev_name, "areca", "areca") | |
685 | { | |
686 | set_encnum(encnum); | |
687 | set_disknum(disknum); | |
688 | set_info().info_name = strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name, disknum, encnum); | |
689 | } | |
690 | ||
691 | areca_scsi_device::~areca_scsi_device() throw() | |
692 | { | |
693 | ||
694 | } | |
695 | ||
696 | bool areca_scsi_device::scsi_pass_through(struct scsi_cmnd_io * iop) | |
697 | { | |
698 | return arcmsr_scsi_pass_through(iop); | |
699 | } | |
700 | ||
701 | ||
702 | ||
703 |