xref: /haiku/src/add-ons/kernel/busses/scsi/usb/transform_procs.c (revision 1acbe440b8dd798953bec31d18ee589aa3f71b73)
1 /**
2  *
3  * TODO: description
4  *
5  * This file is a part of USB SCSI CAM for Haiku OS.
6  * May be used under terms of the MIT License
7  *
8  * Author(s):
9  * 	Siarzhuk Zharski <imker@gmx.li>
10  *
11  *
12  */
13 /** SCSI commands transformations support */
14 
15 #include "usb_scsi.h"
16 
17 #include "device_info.h"
18 #include "transform_procs.h"
19 #include "tracing.h"
20 #include "scsi_commands.h"
21 //#include "proto_common.h"
22 #include "settings.h"
23 #include "strings.h"
24 
25 #define UFI_COMMAND_LEN		12
26 #define ATAPI_COMMAND_LEN	12
27 
28 static status_t
29 scsi_transform(usb_device_info *udi, uint8	*cmd,
30 								uint8 len, uint8 **rcmd, uint8	*rlen);
31 static status_t rbc_transform(usb_device_info *udi, uint8	*cmd,
32 							 	uint8 len, uint8 **rcmd, uint8	*rlen);
33 static status_t ufi_transform(usb_device_info *udi, uint8	*cmd,
34 								uint8 len, uint8 **rcmd, uint8	*rlen);
35 static status_t atapi_transform(usb_device_info *udi, uint8	*cmd,
36 								uint8 len, uint8 **rcmd, uint8	*rlen);
37 static status_t qic157_transform(usb_device_info *udi, uint8	*cmd,
38 								uint8 len, uint8 **rcmd, uint8	*rlen);
39 /**
40 	\fn:transform_6_to_10
41 	\param cmd: SCSI command buffer to be transformed
42 	\param len: length of buffer, pointed by cmd parameter
43 	\param rcmd: a place for buffer pointer with transformed command
44 	\param rlen: a place for length of transformed command
45 	\return: true if transformation was performed
46 	transforms a 6-byte command to 10-byte one if required.Transformed command is
47 	returned in rcmd parameter. In case if no transformation was performed the return
48 	buffer is untouched.
49 */
50 static bool transform_6_to_10(uint8	*cmd, uint8	 len,
51 								uint8 **rcmd, uint8	*rlen)
52 {
53 	bool transformed = true;
54 	scsi_cmd_generic_6 *from = (scsi_cmd_generic_6 *)cmd;
55 	memset(*rcmd, 0, *rlen);
56 	switch (from->opcode){
57 		case READ_6:
58 		case WRITE_6:{
59 			scsi_cmd_rw_10 *to	= (scsi_cmd_rw_10 *)(*rcmd);
60 			to->opcode	 = (from->opcode == READ_6) ? READ_10 : WRITE_10;
61 			memcpy(to->addr + 1, from->addr, 3);
62 			to->byte2		= from->addr[0];
63 			to->byte2	 &= ~CMD_GEN_6_ADDR;
64 			to->addr[1] &= CMD_GEN_6_ADDR;
65 			to->len[1]	 = from->len;
66 			to->ctrl		 = from->ctrl;
67 			if(0 == from->len){ /* special case! in 6-byte R/W commands	*/
68 				to->len[0] = 1;	 /* the length 0x00 assume transfering 0x100 blocks! */
69 			}
70 		}break;
71 		case MODE_SENSE_6:
72 		case MODE_SELECT_6:{
73 			scsi_cmd_generic_10 *to	 = (scsi_cmd_generic_10 *)(*rcmd);
74 			if(from->opcode == MODE_SENSE_6){
75 				to->opcode = MODE_SENSE_10;
76 				((scsi_cmd_mode_sense_10 *)to)->byte3 =
77 												 ((scsi_cmd_mode_sense_6 *)from)->byte3;
78 			}else
79 				to->opcode = MODE_SELECT_10;
80 			to->byte2	= from->addr[0];
81 			to->len[1]	= from->len + 4; /*TODO: hardcoded length*/
82 			to->ctrl	= from->ctrl;
83 		}break;
84 		default:{ /* no transformation needed */
85 			transformed = false;
86 		}break;
87 	}
88 	return transformed;
89 }
90 /**
91 	\fn:transform_cmd_6_to_10
92 	\param udi: usb_device_info object for wich transformation is requested
93 	\param cmd: SCSI command buffer to be transformed
94 	\param len: length of buffer, pointed by cmd parameter
95 	\param rcmd: a place for buffer pointer with transformed command
96 	\param rlen: a place for length of transformed command
97 	\return: true if transformation was performed
98 	transforms a 6-byte command to 10-byte depending on information provided with
99 	udi object.
100 */
101 static bool transform_cmd_6_to_10(usb_device_info *udi, uint8	*cmd,
102 									 uint8	 len, uint8 **rcmd,	uint8	*rlen)
103 {
104 	bool transformed = true;
105 	scsi_cmd_generic_6 *from = (scsi_cmd_generic_6 *)cmd;
106 	switch (from->opcode){
107 		case READ_6:
108 		case WRITE_6:{
109 //			if(!(HAS_FEATURES(udi->descr.properties, PROP_FORCE_TO_6))){
110 			if(!HAS_FIXES(udi->properties, FIX_FORCE_RW_TO_6)){
111 				if((transformed = transform_6_to_10(cmd, len, rcmd, rlen)) == true)
112 					*rlen = 10;
113 			}else
114 				transformed = false;
115 		}break;
116 		case MODE_SENSE_6:
117 		case MODE_SELECT_6:{
118 //			if(HAS_FEATURES(udi->descr.properties, PROP_USE_MODESENSE_10)){
119 			if(HAS_FIXES(udi->properties, FIX_FORCE_MS_TO_10)){
120 				if((transformed = transform_6_to_10(cmd, len, rcmd, rlen)) == true)
121 					*rlen = 10;
122 			}else
123 				transformed = false;
124 		}
125 	}
126 	return transformed;
127 }
128 /**
129 	\fn:transform_cmd_test_unit_ready
130 	\param udi: usb_device_info object for wich transformation is requested
131 	\param cmd: SCSI command buffer to be transformed
132 	\param len: length of buffer, pointed by cmd parameter
133 	\param rcmd: a place for buffer pointer with transformed command
134 	\param rlen: a place for length of transformed command
135 	\return: true if transformation was performed
136 	transforms a TEST_UNIT_COMAND SCSI command to START_STOP_UNIT one depending
137 	on properties provided with udi object.
138 */
139 static bool transform_cmd_test_unit_ready(usb_device_info *udi,
140 										 uint8	*cmd, uint8	 len,
141 										 uint8 **rcmd, uint8	*rlen)
142 {
143 	bool transformed = false;
144 	if(HAS_FIXES(udi->properties, FIX_TRANS_TEST_UNIT)){
145 		scsi_cmd_start_stop_unit *command = (scsi_cmd_start_stop_unit *)(*rcmd);
146 		memset(*rcmd, 0, *rlen);
147 		command->opcode = START_STOP_UNIT;
148 		command->start_loej = CMD_SSU_START;
149 		*rlen = 6;
150 		transformed = true;
151 	}
152 	return transformed;
153 }
154 /**
155 	\fn:scsi_transform
156 	\param udi: usb_device_info object for wich transformation is requested
157 	\param cmd: SCSI command buffer to be transformed
158 	\param len: length of buffer, pointed by cmd parameter
159 	\param rcmd: a place for buffer pointer with transformed command
160 	\param rlen: a place for length of transformed command
161 	\return: B_OK if transformation was successfull, B_ERROR otherwise
162 	this is the "transformation procedure" for transparent SCSI (0x06) USB subclass.It
163 	performs all SCSI commands transformations required by this protocol. Additionally
164 	it tries to make some workarounds for "brocken" USB devices. If no transformation
165 	was performed resulting command buffer points to original one.
166 */
167 status_t
168 scsi_transform(usb_device_info *udi, uint8	*cmd,
169 				uint8	 len, uint8 **rcmd, uint8	*rlen)
170 {
171 	status_t status = B_OK;
172 	bool transformed = false;
173 	scsi_cmd_generic *command = (scsi_cmd_generic *)cmd;
174 	TRACE_SCSI_COMMAND(cmd, len);
175 	switch(command->opcode){
176 		case READ_6:
177 		case WRITE_6:
178 		case MODE_SENSE_6:
179 		case MODE_SELECT_6:
180 			transformed = transform_cmd_6_to_10(udi, cmd, len, rcmd, rlen);
181 			break;
182 		case TEST_UNIT_READY:
183 			transformed = transform_cmd_test_unit_ready(udi, cmd, len, rcmd, rlen);
184 			break;
185 		default: break;
186 	}
187 	if(!transformed){ /* transformation was not required */
188 		*rcmd = cmd;
189 		*rlen = len;
190 	}else
191 		TRACE_SCSI_COMMAND_HLIGHT(*rcmd, *rlen);
192 	return status;
193 }
194 /**
195 	\fn:rbc_transform
196 	\param udi: usb_device_info object for wich transformation is requested
197 	\param cmd: SCSI command buffer to be transformed
198 	\param len: length of buffer, pointed by cmd parameter
199 	\param rcmd: a place for buffer pointer with transformed command
200 	\param rlen: a place for length of transformed command
201 	\return: B_OK if transformation was successfull, B_ERROR otherwise
202 	this is the "transformation procedure" for RBC USB subclass (0x01).It
203 	performs all SCSI commands transformations required by this protocol. Additionally
204 	it tries to make some workarounds for "brocken" USB devices. If no transformation
205 	was performed resulting command buffer points to original one.
206 */
207 status_t rbc_transform(usb_device_info *udi, uint8	*cmd,
208 					 uint8	 len, uint8 **rcmd, uint8	*rlen)
209 {
210 	status_t status = B_OK;
211 	bool transformed = false;
212 	scsi_cmd_generic *command = (scsi_cmd_generic *)cmd;
213 	TRACE_SCSI_COMMAND(cmd, len);
214 	switch(command->opcode){
215 		case TEST_UNIT_READY:
216 			transformed = transform_cmd_test_unit_ready(udi, cmd, len, rcmd, rlen);
217 			break;
218 		case READ_6:
219 		case WRITE_6: /* there are no such command in list of allowed - transform*/
220 			transformed = transform_cmd_6_to_10(udi, cmd, len, rcmd, rlen);
221 			break;
222 		case FORMAT_UNIT: /* TODO: all following ones are not checked against specs !!!*/
223 		case INQUIRY: /*TODO: check !!! */
224 		case MODE_SELECT_6: /*TODO: check !!! */
225 		case MODE_SENSE_6: /*TODO: check !!! */
226 		case PERSISTENT_RESERVE_IN: /*TODO: check !!! */
227 		case PERSISTENT_RESERVE_OUT: /*TODO: check !!! */
228 		case PREVENT_ALLOW_MEDIA_REMOVAL: /*TODO: check !!! */
229 		case READ_10: /*TODO: check !!! */
230 		case READ_CAPACITY: /*TODO: check !!! */
231 		case RELEASE_6: /*TODO: check !!! */
232 		case REQUEST_SENSE: /*TODO: check !!! */
233 		case RESERVE_6: /*TODO: check !!! */
234 		case START_STOP_UNIT: /*TODO: check !!! */
235 		case SYNCHRONIZE_CACHE: /*TODO: check !!! */
236 		case VERIFY: /*TODO: check !!! */
237 		case WRITE_10: /*TODO: check !!! */
238 		case WRITE_BUFFER:/*TODO Check correctnes of such translation!*/
239 			*rcmd = cmd;		/* No need to copy */
240 			*rlen = len;	/*TODO: check !!! */
241 		default:
242 			TRACE_ALWAYS("An unsupported RBC command: %08x\n", command->opcode);
243 			status = B_ERROR;
244 			break;
245 	}
246 	if(transformed)
247 		TRACE_SCSI_COMMAND_HLIGHT(*rcmd, *rlen);
248 	return status;
249 }
250 /**
251 	\fn:ufi_transform
252 	\param udi: usb_device_info object for wich transformation is requested
253 	\param cmd: SCSI command buffer to be transformed
254 	\param len: length of buffer, pointed by cmd parameter
255 	\param rcmd: a place for buffer pointer with transformed command
256 	\param rlen: a place for length of transformed command
257 	\return: B_OK if transformation was successfull, B_ERROR otherwise
258 	this is the "transformation procedure" for UFI USB subclass (0x04).It
259 	performs all SCSI commands transformations required by this protocol. Additionally
260 	it tries to make some workarounds for "brocken" USB devices. If no transformation
261 	was performed resulting command buffer points to original one.
262 */
263 status_t ufi_transform(usb_device_info *udi,
264 															 uint8	*cmd,
265 															 uint8	 len,
266 															 uint8 **rcmd,
267 															 uint8	*rlen)
268 {
269 	status_t status = B_OK;
270 	TRACE_SCSI_COMMAND(cmd, len);
271 	memset(*rcmd, 0, UFI_COMMAND_LEN);
272 	if(!transform_6_to_10(cmd, len, rcmd, rlen)){ /* was not transformed ?*/
273 		scsi_cmd_generic *command = (scsi_cmd_generic *)cmd;
274 		switch(command->opcode){
275 			case TEST_UNIT_READY:
276 				if(transform_cmd_test_unit_ready(udi, cmd, len, rcmd, rlen)){
277 					break; /* if TEST UNIT READY was transformed*/
278 				}
279 			case FORMAT_UNIT:	/* TODO: mismatch */
280 			case INQUIRY:
281 			case START_STOP_UNIT:
282 			case MODE_SELECT_10:
283 			case MODE_SENSE_10:
284 			case PREVENT_ALLOW_MEDIA_REMOVAL:
285 			case READ_10:
286 			case READ_12:
287 			case READ_CAPACITY:
288 			case READ_FORMAT_CAPACITY: /* TODO: not in the SCSI-2 specs */
289 			case REQUEST_SENSE:
290 			case REZERO_UNIT:
291 			case SEEK_10:
292 			case SEND_DIAGNOSTICS: /* TODO: parameter list len mismatch */
293 			case VERIFY:
294 			case WRITE_10:
295 			case WRITE_12: /* TODO: EBP. mismatch */
296 			case WRITE_AND_VERIFY:
297 				memcpy(*rcmd, cmd, len); /*TODO what about control? ignored in UFI?*/
298 				break;
299 			default:
300 				TRACE_ALWAYS("An unsupported UFI command: %08x\n", command->opcode);
301 				status = B_ERROR;
302 				break;
303 		}
304 	}
305 	*rlen = UFI_COMMAND_LEN; /* override any value set in transform funcs !!!*/
306 	if(status == B_OK)
307 		TRACE_SCSI_COMMAND_HLIGHT(*rcmd, *rlen);
308 	return status;
309 }
310 /**
311 	\fn:atapi_transform
312 	\param udi: usb_device_info object for wich transformation is requested
313 	\param cmd: SCSI command buffer to be transformed
314 	\param len: length of buffer, pointed by cmd parameter
315 	\param rcmd: a place for buffer pointer with transformed command
316 	\param rlen: a place for length of transformed command
317 	\return: B_OK if transformation was successfull, B_ERROR otherwise
318 	this is the "transformation procedure" for SFF8020I and SFF8070I
319 	USB subclassses (0x02 and 0x05). It performs all SCSI commands transformations
320 	required by this protocol. Additionally it tries to make some workarounds for
321 	"broken" USB devices. If no transformation was performed resulting command
322 	buffer points to original one.
323 */
324 status_t atapi_transform(usb_device_info *udi,
325 															 uint8	*cmd,
326 															 uint8	 len,
327 															 uint8 **rcmd,
328 															 uint8	*rlen)
329 {
330 	status_t status = B_OK;
331 	TRACE_SCSI_COMMAND(cmd, len);
332 	memset(*rcmd, 0, ATAPI_COMMAND_LEN);
333 	if(!transform_6_to_10(cmd, len, rcmd, rlen)){ /* was not transformed ?*/
334 		scsi_cmd_generic *command = (scsi_cmd_generic *)cmd;
335 		switch(command->opcode){
336 			case TEST_UNIT_READY:
337 				if(transform_cmd_test_unit_ready(udi, cmd, len, rcmd, rlen)){
338 					break; /* if TEST UNIT READY was transformed*/
339 				}
340 			case FORMAT_UNIT:
341 			case INQUIRY:
342 			case MODE_SELECT_10:
343 			case MODE_SENSE_10:
344 			case PREVENT_ALLOW_MEDIA_REMOVAL:
345 			case READ_10:
346 			case READ_12: /* mismatch in byte 1 */
347 			case READ_CAPACITY: /* mismatch. no transf len defined... */
348 			case READ_FORMAT_CAPACITY: /* TODO: check!!! */
349 			case REQUEST_SENSE:
350 			case SEEK_10:
351 			case START_STOP_UNIT:
352 			case VERIFY: /* mismatch DPO */
353 			case WRITE_10: /* mismatch in byte 1 */
354 			case WRITE_12: /* mismatch in byte 1 */
355 			case WRITE_AND_VERIFY: /* mismatch byte 1 */
356 			case PAUSE_RESUME:
357 			case PLAY_AUDIO:
358 			case PLAY_AUDIO_MSF:
359 			case REWIND:
360 			case PLAY_AUDIO_TRACK:
361 	/* are in FreeBSD driver but no in 8070/8020 specs ...
362 	//case REZERO_UNIT:
363 	//case SEND_DIAGNOSTIC:
364 	//case POSITION_TO_ELEMENT:	*/
365 	case GET_CONFIGURATION:
366 	case SYNCHRONIZE_CACHE:
367 	case READ_BUFFER:
368  	case READ_SUBCHANNEL:
369 	case READ_TOC: /* some mismatch */
370 	case READ_HEADER:
371 	case READ_DISK_INFO:
372 	case READ_TRACK_INFO:
373 	case SEND_OPC:
374 	case READ_MASTER_CUE:
375 	case CLOSE_TR_SESSION:
376 	case READ_BUFFER_CAP:
377 	case SEND_CUE_SHEET:
378 	case BLANK:
379 	case EXCHANGE_MEDIUM:
380 	case READ_DVD_STRUCTURE:
381 	case SET_CD_SPEED:
382 	case DVD_REPORT_KEY:
383 		case DVD_SEND_KEY:
384 //	case 0xe5: /* READ_TRACK_INFO_PHILIPS *//* TODO: check!!! */
385 				memcpy(*rcmd, cmd, len); /* TODO: check!!! */
386 				break;
387 			default:
388 				TRACE_ALWAYS("An unsupported (?) ATAPI command: %08x\n", command->opcode);
389 				status = B_ERROR;
390 				break;
391 		}
392 	}
393 	*rlen = ATAPI_COMMAND_LEN;
394 	if(status == B_OK)
395 		TRACE_SCSI_COMMAND_HLIGHT(*rcmd, *rlen);
396 	return status;
397 }
398 /**
399 	\fn:qic157_transform
400 	\param udi: usb_device_info object for wich transformation is requested
401 	\param cmd: SCSI command buffer to be transformed
402 	\param len: length of buffer, pointed by cmd parameter
403 	\param rcmd: a place for buffer pointer with transformed command
404 	\param rlen: a place for length of transformed command
405 	\return: B_OK if transformation was successfull, B_ERROR otherwise
406 	this is the "transformation procedure" for QIC157 USB subclass (0x03).It
407 	performs all SCSI commands transformations required by this protocol. Additionally
408 	it tries to make some workarounds for "brocken" USB devices. If no transformation
409 	was performed resulting command buffer points to original one.
410 */
411 status_t qic157_transform(usb_device_info *udi,
412 															 uint8	*cmd,
413 															 uint8	 len,
414 															 uint8 **rcmd,
415 															 uint8	*rlen)
416 {
417 	status_t status = B_OK;
418 	TRACE_SCSI_COMMAND(cmd, len);
419 	*rlen = ATAPI_COMMAND_LEN;
420 	memset(*rcmd, 0, *rlen);
421 	if(!transform_6_to_10(cmd, len, rcmd, rlen)){ // was not transformed ?
422 		scsi_cmd_generic *command = (scsi_cmd_generic *)cmd;
423 		switch(command->opcode){
424 			case TEST_UNIT_READY:
425 				if(transform_cmd_test_unit_ready(udi, cmd, len, rcmd, rlen)){
426 					break; // if TEST UNIT READY was transformed
427 				}
428 			case ERASE: /*TODO: check !!! */
429 			case INQUIRY: /*TODO: check !!! */
430 			case LOAD_UNLOAD: /*TODO: check !!! */
431 			case LOCATE: /*TODO: check !!! */
432 			case LOG_SELECT: /*TODO: check !!! */
433 			case LOG_SENSE: /*TODO: check !!! */
434 			case MODE_SELECT_6: /*TODO: check !!! */
435 			case MODE_SENSE_6: /*TODO: check !!! */
436 			case READ_6: /*TODO: check !!! */
437 			case READ_POSITION: /*TODO: check !!! */
438 			case REQUEST_SENSE: /*TODO: check !!! */
439 			case REWIND: /*TODO: check !!! */
440 			case SPACE: /*TODO: check !!! */
441 			case WRITE_6: /*TODO: check !!! */
442 			case WRITE_FILEMARK: /*TODO: check !!! */
443 				*rcmd = cmd; /*TODO: check !!! */
444 				*rlen = len;
445 				break;
446 			default:
447 				TRACE_ALWAYS("An unsupported QIC-157 command: %08x\n", command->opcode);
448 				status = B_ERROR;
449 				break;
450 		}
451 	}
452 	if(status == B_OK)
453 		TRACE_SCSI_COMMAND_HLIGHT(*rcmd, *rlen);
454 	return status;
455 }
456 
457 
458 transform_module_info scsi_transform_m = {
459 	{0, 0, 0}, /* this is not a real kernel module - just interface */
460 	scsi_transform,
461 };
462 
463 transform_module_info rbc_transform_m = {
464 	{0, 0, 0}, /* this is not a real kernel module - just interface */
465 	rbc_transform,
466 };
467 
468 transform_module_info ufi_transform_m = {
469 	{0, 0, 0}, /* this is not a real kernel module - just interface */
470 	ufi_transform,
471 };
472 
473 transform_module_info atapi_transform_m = {
474 	{0, 0, 0}, /* this is not a real kernel module - just interface */
475 	atapi_transform,
476 };
477 
478 transform_module_info qic157_transform_m = {
479 	{0, 0, 0}, /* this is not a real kernel module - just interface */
480 	qic157_transform,
481 };
482