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