]>
Commit | Line | Data |
---|---|---|
ee38a438 GI |
1 | /* |
2 | * dev_areca.cpp | |
3 | * | |
4 | * Home page of code is: http://smartmontools.sourceforge.net | |
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 3803 2013-03-24 18:52:54Z 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 | unsigned int cmds[] = | |
118 | { | |
119 | ARCMSR_IOCTL_READ_RQBUFFER, | |
120 | ARCMSR_IOCTL_WRITE_WQBUFFER, | |
121 | ARCMSR_IOCTL_CLEAR_RQBUFFER, | |
122 | ARCMSR_IOCTL_CLEAR_WQBUFFER, | |
123 | ARCMSR_IOCTL_RETURN_CODE_3F | |
124 | }; | |
125 | ||
126 | int ioctlreturn = 0; | |
127 | sSRB_BUFFER sBuf; | |
128 | struct scsi_cmnd_io iop; | |
129 | int dir = DXFER_TO_DEVICE; | |
130 | ||
131 | UINT8 cdb[10]={0}; | |
132 | UINT8 sense[32]={0}; | |
133 | ||
134 | unsigned char *areca_return_packet; | |
135 | int total = 0; | |
136 | int expected = -1; | |
137 | unsigned char return_buff[2048]={0}; | |
138 | unsigned char *ptr = &return_buff[0]; | |
139 | ||
140 | memset((unsigned char *)&sBuf, 0, sizeof(sBuf)); | |
141 | memset(&iop, 0, sizeof(iop)); | |
142 | ||
143 | sBuf.srbioctl.HeaderLength = sizeof(sARCMSR_IO_HDR); | |
144 | memcpy(sBuf.srbioctl.Signature, ARECA_SIG_STR, strlen(ARECA_SIG_STR)); | |
145 | sBuf.srbioctl.Timeout = 10000; | |
146 | sBuf.srbioctl.ControlCode = cmds[arcmsr_cmd]; | |
147 | ||
148 | if(arcmsr_cmd >= ARCMSR_CMD_TOTAL) | |
149 | { | |
150 | return -1; | |
151 | } | |
152 | ||
153 | switch ( arcmsr_cmd ) | |
154 | { | |
155 | // command for writing data to driver | |
156 | case ARCMSR_WRITE_WQBUFFER: | |
157 | if ( data && data_len ) | |
158 | { | |
159 | sBuf.srbioctl.Length = data_len; | |
160 | memcpy((unsigned char *)sBuf.ioctldatabuffer, (unsigned char *)data, data_len); | |
161 | } | |
162 | // commands for clearing related buffer of driver | |
163 | case ARCMSR_CLEAR_RQBUFFER: | |
164 | case ARCMSR_CLEAR_WQBUFFER: | |
165 | cdb[0] = 0x3B; //SCSI_WRITE_BUF command; | |
166 | break; | |
167 | // command for reading data from driver | |
168 | case ARCMSR_READ_RQBUFFER: | |
169 | // command for identifying driver | |
170 | case ARCMSR_RETURN_CODE_3F: | |
171 | cdb[0] = 0x3C; //SCSI_READ_BUF command; | |
172 | dir = DXFER_FROM_DEVICE; | |
173 | break; | |
174 | default: | |
175 | // unknown arcmsr commands | |
176 | return -1; | |
177 | } | |
178 | ||
179 | cdb[1] = 0x01; | |
180 | cdb[2] = 0xf0; | |
181 | cdb[5] = cmds[arcmsr_cmd] >> 24; | |
182 | cdb[6] = cmds[arcmsr_cmd] >> 16; | |
183 | cdb[7] = cmds[arcmsr_cmd] >> 8; | |
184 | cdb[8] = cmds[arcmsr_cmd] & 0x0F; | |
185 | ||
186 | iop.dxfer_dir = dir; | |
187 | iop.dxfer_len = sizeof(sBuf); | |
188 | iop.dxferp = (unsigned char *)&sBuf; | |
189 | iop.cmnd = cdb; | |
190 | iop.cmnd_len = sizeof(cdb); | |
191 | iop.sensep = sense; | |
192 | iop.max_sense_len = sizeof(sense); | |
193 | iop.timeout = SCSI_TIMEOUT_DEFAULT; | |
194 | ||
195 | while ( 1 ) | |
196 | { | |
197 | ioctlreturn = arcmsr_do_scsi_io(&iop); | |
198 | if(ioctlreturn || iop.scsi_status) | |
199 | { | |
200 | break; | |
201 | } | |
202 | ||
203 | if ( arcmsr_cmd != ARCMSR_READ_RQBUFFER ) | |
204 | { | |
205 | // if succeeded, just returns the length of outgoing data | |
206 | return data_len; | |
207 | } | |
208 | ||
209 | if ( sBuf.srbioctl.Length ) | |
210 | { | |
211 | memcpy(ptr, &sBuf.ioctldatabuffer[0], sBuf.srbioctl.Length); | |
212 | ptr += sBuf.srbioctl.Length; | |
213 | total += sBuf.srbioctl.Length; | |
214 | // the returned bytes enough to compute payload length ? | |
215 | if ( expected < 0 && total >= 5 ) | |
216 | { | |
217 | areca_return_packet = (unsigned char *)&return_buff[0]; | |
218 | if ( areca_return_packet[0] == 0x5E && | |
219 | areca_return_packet[1] == 0x01 && | |
220 | areca_return_packet[2] == 0x61 ) | |
221 | { | |
222 | // valid header, let's compute the returned payload length, | |
223 | // we expected the total length is | |
224 | // payload + 3 bytes header + 2 bytes length + 1 byte checksum | |
225 | expected = areca_return_packet[4] * 256 + areca_return_packet[3] + 6; | |
226 | } | |
227 | } | |
228 | ||
229 | if ( total >= 7 && total >= expected ) | |
230 | { | |
231 | //printf("total bytes received = %d, expected length = %d\n", total, expected); | |
232 | ||
233 | // ------ Okay! we received enough -------- | |
234 | break; | |
235 | } | |
236 | } | |
237 | } | |
238 | ||
239 | // Deal with the different error cases | |
240 | if ( arcmsr_cmd == ARCMSR_RETURN_CODE_3F ) | |
241 | { | |
242 | // Silence the ARCMSR_IOCTL_RETURN_CODE_3F's error, no pout(...) | |
243 | return -4; | |
244 | } | |
245 | ||
246 | if ( ioctlreturn ) | |
247 | { | |
248 | pout("do_scsi_cmnd_io with write buffer failed code = %x\n", ioctlreturn); | |
249 | return -2; | |
250 | } | |
251 | ||
252 | if ( iop.scsi_status ) | |
253 | { | |
254 | pout("io_hdr.scsi_status with write buffer failed code = %x\n", iop.scsi_status); | |
255 | return -3; | |
256 | } | |
257 | ||
258 | if ( data ) | |
259 | { | |
260 | memcpy(data, return_buff, total); | |
261 | } | |
262 | ||
263 | return total; | |
264 | } | |
265 | ||
266 | bool generic_areca_device::arcmsr_probe() | |
267 | { | |
268 | if(!is_open()) | |
269 | { | |
270 | open(); | |
271 | } | |
272 | ||
273 | if(arcmsr_command_handler(ARCMSR_RETURN_CODE_3F, NULL, 0) != 0) | |
274 | { | |
275 | return false; | |
276 | } | |
277 | return true; | |
278 | } | |
279 | ||
280 | int generic_areca_device::arcmsr_ui_handler(unsigned char *areca_packet, int areca_packet_len, unsigned char *result) | |
281 | { | |
282 | int expected = 0; | |
283 | unsigned char return_buff[2048]; | |
284 | unsigned char cs = 0; | |
285 | int cs_pos = 0; | |
286 | ||
287 | // ----- ADD CHECKSUM ----- | |
288 | cs_pos = areca_packet_len - 1; | |
289 | for(int i = 3; i < cs_pos; i++) | |
290 | { | |
291 | areca_packet[cs_pos] += areca_packet[i]; | |
292 | } | |
293 | ||
294 | if(!arcmsr_lock()) | |
295 | { | |
296 | return -1; | |
297 | } | |
298 | expected = arcmsr_command_handler(ARCMSR_CLEAR_RQBUFFER, NULL, 0); | |
299 | if (expected==-3) { | |
300 | return set_err(EIO); | |
301 | } | |
302 | expected = arcmsr_command_handler(ARCMSR_CLEAR_WQBUFFER, NULL, 0); | |
303 | expected = arcmsr_command_handler(ARCMSR_WRITE_WQBUFFER, areca_packet, areca_packet_len); | |
304 | if ( expected > 0 ) | |
305 | { | |
306 | expected = arcmsr_command_handler(ARCMSR_READ_RQBUFFER, return_buff, sizeof(return_buff)); | |
307 | } | |
308 | ||
309 | if ( expected < 0 ) | |
310 | { | |
311 | return -1; | |
312 | } | |
313 | ||
314 | if(!arcmsr_unlock()) | |
315 | { | |
316 | return -1; | |
317 | } | |
318 | ||
319 | // ----- VERIFY THE CHECKSUM ----- | |
320 | cs = 0; | |
321 | for ( int loop = 3; loop < expected - 1; loop++ ) | |
322 | { | |
323 | cs += return_buff[loop]; | |
324 | } | |
325 | ||
326 | if ( return_buff[expected - 1] != cs ) | |
327 | { | |
328 | return -1; | |
329 | } | |
330 | ||
331 | memcpy(result, return_buff, expected); | |
332 | ||
333 | return expected; | |
334 | } | |
335 | ||
336 | int generic_areca_device::arcmsr_get_controller_type() | |
337 | { | |
338 | int expected = 0; | |
339 | unsigned char return_buff[2048]; | |
340 | unsigned char areca_packet[] = {0x5E, 0x01, 0x61, 0x01, 0x00, 0x23, 0x00}; | |
341 | ||
342 | memset(return_buff, 0, sizeof(return_buff)); | |
343 | expected = arcmsr_ui_handler(areca_packet, sizeof(areca_packet), return_buff); | |
344 | if ( expected < 0 ) | |
345 | { | |
346 | return -1; | |
347 | } | |
348 | ||
349 | return return_buff[0xc2]; | |
350 | } | |
351 | ||
352 | int generic_areca_device::arcmsr_get_dev_type() | |
353 | { | |
354 | int expected = 0; | |
355 | unsigned char return_buff[2048]; | |
356 | int ctlr_type = -1; | |
357 | int encnum = get_encnum(); | |
358 | int disknum = get_disknum(); | |
359 | unsigned char areca_packet[] = {0x5E, 0x01, 0x61, 0x03, 0x00, 0x22, | |
360 | (unsigned char)(disknum - 1), (unsigned char)(encnum - 1), 0x00}; | |
361 | ||
362 | memset(return_buff, 0, sizeof(return_buff)); | |
363 | expected = arcmsr_ui_handler(areca_packet, sizeof(areca_packet), return_buff); | |
364 | if ( expected < 0 ) | |
365 | { | |
366 | return -1; | |
367 | } | |
368 | ||
369 | ctlr_type = arcmsr_get_controller_type(); | |
370 | ||
371 | if( ctlr_type < 0 ) | |
372 | { | |
373 | return ctlr_type; | |
374 | } | |
375 | ||
376 | if( ctlr_type == 0x02/* SATA Controllers */ || | |
377 | (ctlr_type == 0x03 /* SAS Controllers */ && return_buff[0x52] & 0x01 /* SATA devices behind SAS Controller */) ) | |
378 | { | |
379 | // SATA device | |
380 | return 1; | |
381 | } | |
382 | ||
383 | // SAS device | |
384 | return 0; | |
385 | } | |
386 | ||
387 | bool generic_areca_device::arcmsr_ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) | |
388 | { | |
389 | // ATA input registers | |
390 | typedef struct _ATA_INPUT_REGISTERS | |
391 | { | |
392 | unsigned char features; | |
393 | unsigned char sector_count; | |
394 | unsigned char sector_number; | |
395 | unsigned char cylinder_low; | |
396 | unsigned char cylinder_high; | |
397 | unsigned char device_head; | |
398 | unsigned char command; | |
399 | unsigned char reserved[8]; | |
400 | unsigned char data[512]; // [in/out] buffer for outgoing/incoming data | |
401 | } sATA_INPUT_REGISTERS; | |
402 | ||
403 | // ATA output registers | |
404 | // Note: The output registers is re-sorted for areca internal use only | |
405 | typedef struct _ATA_OUTPUT_REGISTERS | |
406 | { | |
407 | unsigned char error; | |
408 | unsigned char status; | |
409 | unsigned char sector_count; | |
410 | unsigned char sector_number; | |
411 | unsigned char cylinder_low; | |
412 | unsigned char cylinder_high; | |
413 | } sATA_OUTPUT_REGISTERS; | |
414 | ||
415 | // Areca packet format for outgoing: | |
416 | // B[0~2] : 3 bytes header, fixed value 0x5E, 0x01, 0x61 | |
417 | // B[3~4] : 2 bytes command length + variant data length, little endian | |
418 | // B[5] : 1 bytes areca defined command code, ATA passthrough command code is 0x1c | |
419 | // B[6~last-1] : variant bytes payload data | |
420 | // B[last] : 1 byte checksum, simply sum(B[3] ~ B[last -1]) | |
421 | // | |
422 | // | |
423 | // header 3 bytes length 2 bytes cmd 1 byte payload data x bytes cs 1 byte | |
424 | // +--------------------------------------------------------------------------------+ | |
425 | // + 0x5E 0x01 0x61 | 0x00 0x00 | 0x1c | .................... | 0x00 | | |
426 | // +--------------------------------------------------------------------------------+ | |
427 | // | |
428 | ||
429 | //Areca packet format for incoming: | |
430 | // B[0~2] : 3 bytes header, fixed value 0x5E, 0x01, 0x61 | |
431 | // B[3~4] : 2 bytes payload length, little endian | |
432 | // B[5~last-1] : variant bytes returned payload data | |
433 | // B[last] : 1 byte checksum, simply sum(B[3] ~ B[last -1]) | |
434 | // | |
435 | // | |
436 | // header 3 bytes length 2 bytes payload data x bytes cs 1 byte | |
437 | // +-------------------------------------------------------------------+ | |
438 | // + 0x5E 0x01 0x61 | 0x00 0x00 | .................... | 0x00 | | |
439 | // +-------------------------------------------------------------------+ | |
440 | unsigned char areca_packet[640]; | |
441 | int areca_packet_len = sizeof(areca_packet); | |
442 | unsigned char return_buff[2048]; | |
443 | int expected = 0; | |
444 | ||
445 | sATA_INPUT_REGISTERS *ata_cmd; | |
446 | ||
447 | // For debugging | |
448 | #if 0 | |
449 | memset(sInq, 0, sizeof(sInq)); | |
450 | scsiStdInquiry(fd, (unsigned char *)sInq, (int)sizeof(sInq)); | |
451 | dumpdata((unsigned char *)sInq, sizeof(sInq)); | |
452 | #endif | |
453 | memset(areca_packet, 0, areca_packet_len); | |
454 | ||
455 | // ----- BEGIN TO SETUP HEADERS ------- | |
456 | areca_packet[0] = 0x5E; | |
457 | areca_packet[1] = 0x01; | |
458 | areca_packet[2] = 0x61; | |
459 | areca_packet[3] = (unsigned char)((areca_packet_len - 6) & 0xff); | |
460 | areca_packet[4] = (unsigned char)(((areca_packet_len - 6) >> 8) & 0xff); | |
461 | areca_packet[5] = 0x1c; // areca defined code for ATA passthrough command | |
462 | ||
463 | // ----- BEGIN TO SETUP PAYLOAD DATA ----- | |
464 | memcpy(&areca_packet[7], "SmrT", 4); // areca defined password | |
465 | ata_cmd = (sATA_INPUT_REGISTERS *)&areca_packet[12]; | |
466 | ||
467 | // Set registers | |
468 | { | |
469 | const ata_in_regs & r = in.in_regs; | |
470 | ata_cmd->features = r.features; | |
471 | ata_cmd->sector_count = r.sector_count; | |
472 | ata_cmd->sector_number = r.lba_low; | |
473 | ata_cmd->cylinder_low = r.lba_mid; | |
474 | ata_cmd->cylinder_high = r.lba_high; | |
475 | ata_cmd->device_head = r.device; | |
476 | ata_cmd->command = r.command; | |
477 | } | |
478 | bool readdata = false; | |
479 | if (in.direction == ata_cmd_in::data_in) { | |
480 | readdata = true; | |
481 | // the command will read data | |
482 | areca_packet[6] = 0x13; | |
483 | } | |
484 | else if ( in.direction == ata_cmd_in::no_data ) | |
485 | { | |
486 | // the commands will return no data | |
487 | areca_packet[6] = 0x15; | |
488 | } | |
489 | else if (in.direction == ata_cmd_in::data_out) | |
490 | { | |
491 | // the commands will write data | |
492 | memcpy(ata_cmd->data, in.buffer, in.size); | |
493 | areca_packet[6] = 0x14; | |
494 | } | |
495 | else { | |
496 | // COMMAND NOT SUPPORTED VIA ARECA IOCTL INTERFACE | |
497 | return set_err(ENOSYS); | |
498 | } | |
499 | ||
500 | areca_packet[11] = get_disknum() - 1; // disk# | |
501 | areca_packet[19] = get_encnum() - 1; // enc# | |
502 | ||
503 | // ----- BEGIN TO SEND TO ARECA DRIVER ------ | |
504 | expected = arcmsr_ui_handler(areca_packet, areca_packet_len, return_buff); | |
505 | if ( expected < 0 ) | |
506 | { | |
507 | return set_err(EIO); | |
508 | } | |
509 | ||
510 | sATA_OUTPUT_REGISTERS *ata_out = (sATA_OUTPUT_REGISTERS *)&return_buff[5] ; | |
511 | if ( ata_out->status ) | |
512 | { | |
513 | if ( in.in_regs.command == ATA_IDENTIFY_DEVICE | |
514 | && !nonempty((unsigned char *)in.buffer, in.size)) | |
515 | { | |
516 | return set_err(ENODEV, "No drive on port %d", get_disknum()); | |
517 | } | |
518 | } | |
519 | ||
520 | // returns with data | |
521 | if (readdata) | |
522 | { | |
523 | memcpy(in.buffer, &return_buff[7], in.size); | |
524 | } | |
525 | ||
526 | // Return register values | |
527 | { | |
528 | ata_out_regs & r = out.out_regs; | |
529 | r.error = ata_out->error; | |
530 | r.sector_count = ata_out->sector_count; | |
531 | r.lba_low = ata_out->sector_number; | |
532 | r.lba_mid = ata_out->cylinder_low; | |
533 | r.lba_high = ata_out->cylinder_high; | |
534 | r.status = ata_out->status; | |
535 | } | |
536 | return true; | |
537 | } | |
538 | ||
539 | bool generic_areca_device::arcmsr_scsi_pass_through(struct scsi_cmnd_io * iop) | |
540 | { | |
541 | // Areca packet format for outgoing: | |
542 | // B[0~2] : 3 bytes header, fixed value 0x5E, 0x01, 0x61 | |
543 | // B[3~4] : 2 bytes command length + variant data length, little endian | |
544 | // B[5] : 1 bytes areca defined command code | |
545 | // B[6~last-1] : variant bytes payload data | |
546 | // B[last] : 1 byte checksum, simply sum(B[3] ~ B[last -1]) | |
547 | // | |
548 | // | |
549 | // header 3 bytes length 2 bytes cmd 1 byte payload data x bytes cs 1 byte | |
550 | // +--------------------------------------------------------------------------------+ | |
551 | // + 0x5E 0x01 0x61 | 0x00 0x00 | 0x1c | .................... | 0x00 | | |
552 | // +--------------------------------------------------------------------------------+ | |
553 | // | |
554 | ||
555 | //Areca packet format for incoming: | |
556 | // B[0~2] : 3 bytes header, fixed value 0x5E, 0x01, 0x61 | |
557 | // B[3~4] : 2 bytes payload length, little endian | |
558 | // B[5~last-1] : variant bytes returned payload data | |
559 | // B[last] : 1 byte checksum, simply sum(B[3] ~ B[last -1]) | |
560 | // | |
561 | // | |
562 | // header 3 bytes length 2 bytes payload data x bytes cs 1 byte | |
563 | // +-------------------------------------------------------------------+ | |
564 | // + 0x5E 0x01 0x61 | 0x00 0x00 | .................... | 0x00 | | |
565 | // +-------------------------------------------------------------------+ | |
566 | unsigned char areca_packet[640]; | |
567 | int areca_packet_len = sizeof(areca_packet); | |
568 | unsigned char return_buff[2048]; | |
569 | int expected = 0; | |
570 | ||
571 | if (iop->cmnd_len > 16) { | |
572 | set_err(EINVAL, "cmnd_len too large"); | |
573 | return false; | |
574 | } | |
575 | ||
576 | memset(areca_packet, 0, areca_packet_len); | |
577 | ||
578 | // ----- BEGIN TO SETUP HEADERS ------- | |
579 | areca_packet[0] = 0x5E; | |
580 | areca_packet[1] = 0x01; | |
581 | areca_packet[2] = 0x61; | |
582 | areca_packet[3] = (unsigned char)((areca_packet_len - 6) & 0xff); | |
583 | areca_packet[4] = (unsigned char)(((areca_packet_len - 6) >> 8) & 0xff); | |
584 | areca_packet[5] = 0x1c; | |
585 | ||
586 | // ----- BEGIN TO SETUP PAYLOAD DATA ----- | |
587 | areca_packet[6] = 0x16; // scsi pass through | |
588 | memcpy(&areca_packet[7], "SmrT", 4); // areca defined password | |
589 | areca_packet[12] = iop->cmnd_len; // cdb length | |
590 | memcpy( &areca_packet[35], iop->cmnd, iop->cmnd_len); // cdb | |
591 | areca_packet[15] = (unsigned char)iop->dxfer_len; // 15(LSB) ~ 18(MSB): data length ( max=512 bytes) | |
592 | areca_packet[16] = (unsigned char)(iop->dxfer_len >> 8); | |
593 | areca_packet[17] = (unsigned char)(iop->dxfer_len >> 16); | |
594 | areca_packet[18] = (unsigned char)(iop->dxfer_len >> 24); | |
595 | if(iop->dxfer_dir == DXFER_TO_DEVICE) | |
596 | { | |
597 | areca_packet[13] |= 0x01; | |
598 | memcpy(&areca_packet[67], iop->dxferp, iop->dxfer_len); | |
599 | } | |
600 | else if (iop->dxfer_dir == DXFER_FROM_DEVICE) | |
601 | { | |
602 | } | |
603 | else if( iop->dxfer_dir == DXFER_NONE) | |
604 | { | |
605 | } | |
606 | else { | |
607 | // COMMAND NOT SUPPORTED VIA ARECA IOCTL INTERFACE | |
608 | return set_err(ENOSYS); | |
609 | } | |
610 | ||
611 | areca_packet[11] = get_disknum() - 1; // disk# | |
612 | areca_packet[19] = get_encnum() - 1; // enc# | |
613 | ||
614 | // ----- BEGIN TO SEND TO ARECA DRIVER ------ | |
615 | expected = arcmsr_ui_handler(areca_packet, areca_packet_len, return_buff); | |
616 | if ( expected < 0 ) | |
617 | { | |
618 | return set_err(EIO); | |
619 | } | |
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 |