xref: /haiku/src/add-ons/kernel/busses/scsi/usb/usb_scsi.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 /** Main part of USB SIM implementation */
14 
15 #include "usb_scsi.h"
16 
17 #include <KernelExport.h>
18 #include <module.h>
19 #include <malloc.h>
20 #include <strings.h>
21 #include <stdio.h>
22 #include "device_info.h"
23 #include "settings.h"
24 #include "transform_procs.h"
25 #include "tracing.h"
26 #include "scsi_commands.h"
27 #include "proto_common.h"
28 #include "proto_bulk.h"
29 #include "proto_cbi.h"
30 #include "usb_defs.h"
31 #include "fake_device.h"
32 #include "sg_buffer.h"
33 
34 status_t device_added(const usb_device device, void **cookie);
35 
36 status_t device_removed(void *cookie);
37 
38 static long sim_action(CCB_HEADER *ccbh);
39 static long sim_init();
40 
41 #define SIM_VERSION 1
42 #define HBA_VERSION 1
43 
44 //#define ROUNDUP(size, seg) (((size) + (seg) - 1) & ~((seg) - 1))
45 
46 #define INQ_VENDOR_LEN		0x08
47 #define INQ_PRODUCT_LEN		0x10
48 #define INQ_REVISION_LEN	0x04
49 
50 #define TRANS_TIMEOUT		7500000
51 
52 static long path_id		= -1;
53 static int32 load_count	= 0;
54 
55 static char sim_vendor_name[]	= "S.Zharski";	/* who wrote this driver */
56 static char hba_vendor_name[]	= "USB";		/* who made the hardware */
57 static char controller_family[]	= "USB SCSI";	/* what family of products */
58 
59 struct usb_support_descriptor supported_devices[] = {
60 	{0, 0, 0, 0, 0}
61 };
62 
63 #define SIZEOF(array) (sizeof(array)/sizeof(array[0])) //?????
64 
65 usb_device_info *usb_devices[MAX_DEVICES_COUNT];
66 /* main devices table locking semaphore */
67 sem_id usb_serial_lock = -1;
68 
69 usb_module_info *usb;
70 static cam_for_sim_module_info *cam;
71 
72 struct usb_notify_hooks notify_hooks = {
73 	device_added,
74 	device_removed
75 };
76 
77 /* function prototupes */
78 static status_t get_interface_properties(usb_interface_info *uii, uint32 *pproperties);
79 static status_t load_vendor_module(char **path, const char *path_mask, const char *prop, module_info **mi);
80 static status_t setup_transport_modules(usb_device_info *udi, usb_device_settings *uds);
81 static status_t setup_endpoints(usb_interface_info *uii, usb_device_info *udi);
82 static status_t allocate_resources(usb_device_info *udi);
83 static void 	release_resources(usb_device_info *udi);
84 static status_t xpt_scsi_io(CCB_SCSIIO *ccbio);
85 static status_t xpt_path_inquiry(CCB_PATHINQ *ccbp);
86 static status_t xpt_extended_path_inquiry(CCB_EXTENDED_PATHINQ *ccbep);
87 
88 /**
89 	\fn:match_device
90 	\param device:???
91 	\param uii:???
92 	\param pproperties:???
93 	\return:B_BAD_TYPE , B_ENTRY_NOT_FOUND, B_OK
94 
95 	??
96 */
97 status_t get_interface_properties(usb_interface_info *uii, uint32 *pproperties)
98 {
99 	status_t status = B_BAD_TYPE;
100 	if(uii->descr->interface_class == USB_DEV_CLASS_MASS){
101 		status = B_OK;
102 		switch(uii->descr->interface_subclass){
103 		case USB_DEV_SUBCLASS_RBC:		*pproperties |= CMDSET_RBC; break;
104 		case USB_DEV_SUBCLASS_UFI:		*pproperties |= CMDSET_UFI;	break;
105 		case USB_DEV_SUBCLASS_SFF8020I:
106 		case USB_DEV_SUBCLASS_SFF8070I: *pproperties |= CMDSET_ATAPI;	break;
107 		case USB_DEV_SUBCLASS_SCSI:		*pproperties |= CMDSET_SCSI;	break;
108 		case USB_DEV_SUBCLASS_QIC157:	*pproperties |= CMDSET_QIC157;	break;
109 		default:
110 			TRACE_ALWAYS("get_interface_properties:unknown USB subclass:%02x\n",
111 									 uii->descr->interface_subclass);
112 			/*status = B_ENTRY_NOT_FOUND;*/ /*B_BAD_TYPE assumed*/
113 			break;
114 		}
115 		switch(uii->descr->interface_protocol){
116 		case USB_DEV_PROTOCOL_CBI:	*pproperties |= PROTO_CBI;	break;
117 		case USB_DEV_PROTOCOL_CB:	*pproperties |= PROTO_CB;	break;
118 		case USB_DEV_PROTOCOL_BULK:	*pproperties |= PROTO_BULK_ONLY; break;
119 		default:
120 			TRACE_ALWAYS("get_interface_properties:unknown USB protocol:%02x\n",
121 									 uii->descr->interface_protocol);
122 			/*status = B_ENTRY_NOT_FOUND;*/ /*B_BAD_TYPE assumed*/
123 			break;
124 		}
125 		if(status == B_OK){
126 			TRACE("get_interface_properties: standard properties:%08x\n", *pproperties);
127 		}
128 	}
129 	return status;
130 }
131 /**
132 	\fn:
133 
134 */
135 status_t load_vendor_module(char **path, const char *path_mask,
136 							const char *prop, module_info **mi)
137 {
138 	status_t status = B_NO_MEMORY;
139 	int path_len = strlen(path_mask) + strlen(prop);
140 	*path = malloc(path_len);
141 	if(*path){
142 		sprintf(*path, path_mask, prop);
143 		status = get_module(*path, mi);
144 		if(status != B_OK)
145 			TRACE_ALWAYS("load_vendor_module:get_module(%s) failed:%08x\n", *path, status);
146 	} else {
147 		TRACE_ALWAYS("load_vendor_module:couldn't allocate %d bytes\n", path_len);
148 	}
149 	return status;
150 }
151 /**
152 	\fn:
153 
154 */
155 status_t setup_transport_modules(usb_device_info *udi,
156 									 usb_device_settings *uds)
157 {
158 	status_t status = B_OK;
159 	switch(PROTO(udi->properties)){
160 	case PROTO_BULK_ONLY:
161 		udi->protocol_m = &bulk_only_protocol_m;
162 		break;
163 	case PROTO_CB:
164 	case PROTO_CBI:
165 		udi->protocol_m = &cbi_protocol_m;
166 		break;
167 	case PROTO_VENDOR:{
168 			status = load_vendor_module(&udi->protocol_m_path,
169 										PROTOCOL_MODULE_MASK,
170 										uds->vendor_protocol,
171 										(module_info**)&udi->protocol_m);
172 		}break;
173 	default:
174 		TRACE_ALWAYS("setup_transport_modules: "
175 					 "transport %02x is not supported\n", PROTO(udi->properties));
176 		status = B_ENTRY_NOT_FOUND;
177 	}
178 	if(status == B_OK){
179 		switch(CMDSET(udi->properties)){
180 		case CMDSET_SCSI:
181 			udi->transform_m = &scsi_transform_m;
182 			break;
183 		case CMDSET_UFI:
184 			udi->transform_m = &ufi_transform_m;
185 			udi->properties |= FIX_FORCE_MS_TO_10; /* always use 10-byte request */
186 			break;
187 		case CMDSET_ATAPI:
188 			udi->transform_m = &atapi_transform_m;
189 			udi->properties |= FIX_FORCE_MS_TO_10; /* always use 10-byte request */
190 			break;
191 		case CMDSET_RBC:
192 			udi->transform_m = &rbc_transform_m;
193 			break;
194 		case CMDSET_QIC157:
195 			udi->transform_m = &qic157_transform_m;
196 			udi->properties |= FIX_FORCE_MS_TO_10; /* always use 10-byte request */
197 			break;
198 		case CMDSET_VENDOR:{
199 				status = load_vendor_module(&udi->transform_m_path,
200 										TRANSFORM_MODULE_MASK,
201 										uds->vendor_commandset,
202 										(module_info**)&udi->transform_m);
203 			}break;
204 		default:
205 			TRACE_ALWAYS("setup_transport_modules: "
206 						 "protocol %02x is not supported\n", CMDSET(udi->properties));
207 			status = B_ENTRY_NOT_FOUND;
208 		}
209 	}
210 	return status;
211 }
212 
213 static void
214 release_transport_modules(usb_device_info *udi)
215 {
216 	if(PROTO(udi->properties) == PROTO_VENDOR && 0 != udi->protocol_m_path){
217 		put_module(udi->protocol_m_path);
218 		udi->protocol_m = 0;
219 		free(udi->protocol_m_path);
220 	}
221 	if(CMDSET(udi->properties) == CMDSET_VENDOR && 0 != udi->transform_m_path){
222 		put_module(udi->transform_m_path);
223 		udi->transform_m = 0;
224 		free(udi->transform_m_path);
225 	}
226 }
227 
228 /**
229 	\fn:
230 */
231 status_t setup_endpoints(usb_interface_info *uii, usb_device_info *udi)
232 {
233 	status_t status = B_OK;
234 	int16 idx = 0;
235 	enum{ epIn = 0, epOut, epIntr, epCount };
236 	size_t epts[epCount] = { -1, -1, -1 };
237 	char	*epnames[epCount] = {"input", "output", "interrupt"};
238 	size_t ep = 0;
239 	for(; ep < uii->endpoint_count; ep++){
240 		usb_endpoint_descriptor *ed = uii->endpoint[ep].descr;
241 		TRACE("try endpoint:%d %x %x %x\n", ep, (int32)ed->attributes, (int32)ed->endpoint_address, uii->endpoint[ep].handle);
242 		if((ed->attributes & USB_EP_ATTR_MASK) == USB_EP_ATTR_BULK){
243 			if((ed->endpoint_address & USB_EP_ADDR_DIR_IN) == USB_EP_ADDR_DIR_IN){
244 				epts[epIn]	= ep;
245 			}else{
246 				epts[epOut] = ep;
247 			}
248 		}else{
249 			if((ed->attributes & USB_EP_ATTR_MASK) == USB_EP_ATTR_INTERRUPT)
250 				epts[epIntr] = ep;
251 		}
252 	}
253 	switch(PROTO(udi->properties)){
254 	case PROTO_CB:
255 	case PROTO_BULK_ONLY:
256 		if(epts[epIntr] == -1)
257 			epts[epIntr] = 0; /* not required for this transports - set it to default*/
258 		break;
259 	case PROTO_CBI:
260 	default:
261 	}
262 	for(idx = 0; idx < epCount; idx++){
263 		if(epts[idx] == -1 && PROTO(udi->properties) != PROTO_VENDOR){
264 			TRACE_ALWAYS("setup_endpoints: required %s endpoint not found. "
265 						 "ignore this interface\n", epnames[idx]);
266 			// DEBUG!!!!!!!!!!
267 			status = B_ERROR;
268 		}
269 	}
270 	if(status == B_OK){
271 		udi->pipe_in	= uii->endpoint[epts[epIn]].handle;
272 		udi->pipe_out	= uii->endpoint[epts[epOut]].handle;
273 		udi->pipe_intr	= uii->endpoint[epts[epIntr]].handle;
274 		//TRACE("setup_endpoints: input:%d output:%d "
275 		//		 "interrupt:%d\n", epts[epIn], epts[epOut], epts[epIntr]);
276 		TRACE("endpoint:%x %x %x\n", udi->pipe_in, udi->pipe_out, udi->pipe_intr);
277 	}
278 	return status;
279 }
280 /**
281 	\fn:create_device_info
282 	\param device:???
283 	\param ppudi:???
284 	\return:???
285 
286 	???
287 */
288 status_t allocate_resources(usb_device_info *udi)
289 {
290 	char name[32];
291 	status_t status = B_NO_MEMORY;
292 	uint16 dev = 0;
293 	acquire_sem(usb_serial_lock);
294 	for(; dev < MAX_DEVICES_COUNT; dev++){
295 		if(!usb_devices[dev])
296 			break;
297 	}
298 	if(dev < MAX_DEVICES_COUNT){
299 		usb_devices[dev] = udi;
300 		udi->lock_sem =
301 		udi->trans_sem = -1;
302 		sprintf(name, "usb_scsi lock_sem:%d", dev);
303 		if((udi->lock_sem = create_sem(0, name)) >= 0){
304 			sprintf(name, "usb_scsi trans_sem:%d", dev);
305 			if((udi->trans_sem = create_sem(0, name))>=0){
306 				udi->dev_num = dev;
307 				release_sem(udi->lock_sem);
308 				status = B_OK;
309 			}else
310 				status = udi->trans_sem;
311 		} else
312 			status = udi->lock_sem;
313 
314 		if(status != B_OK){
315 			TRACE_ALWAYS("allocate_resources:error:%s", strerror(status));
316 			if(udi->lock_sem >= 0)
317 				delete_sem(udi->lock_sem);
318 			if(udi->trans_sem >= 0)
319 				delete_sem(udi->trans_sem);
320 			usb_devices[dev] = NULL;
321 			free(udi);
322 		}
323 	}else{
324 		TRACE_ALWAYS("allocate_resources:reserved devices space exhausted."
325 								 "Unplug unnesesary devices or reconfigure this driver.\n");
326 	}
327 	release_sem(usb_serial_lock);
328 	return status;
329 }
330 /**
331 	\fn:
332 
333 */
334 void release_resources(usb_device_info *udi)
335 {
336 	release_transport_modules(udi);
337 	udi->usb_m = 0;
338 	(*usb->cancel_queued_transfers)(udi->pipe_in);
339 	(*usb->cancel_queued_transfers)(udi->pipe_out);
340 	delete_sem(udi->lock_sem);
341 	delete_sem(udi->trans_sem);
342 	acquire_sem(usb_serial_lock);
343 	usb_devices[udi->dev_num] = NULL;
344 	release_sem(usb_serial_lock);
345 }
346 /**
347 	\fn:device_added
348 	\param device:??
349 	\param cookie:??
350 	\return:??
351 
352 	??
353 */
354 status_t device_added(const usb_device device, void **cookie){
355 	status_t status = B_NO_MEMORY;
356 	const usb_configuration_info *uci = NULL;
357 	uint16 cfg = 0;
358 	bool b_found = false;
359 	bool b_has_extra_settings = false;
360 	const usb_device_descriptor *udd = (*usb->get_device_descriptor)(device);
361 	usb_device_info *udi = (usb_device_info *)malloc(sizeof(usb_device_info));
362 	TRACE("device_added: probing canidate: "
363 		"%04x/%04x %02d/%02d/%02d\n",
364 			udd->vendor_id, udd->product_id,
365 			udd->device_class,
366 			udd->device_subclass,
367 			udd->device_protocol);
368 	if(udi){
369 		status = B_NO_INIT;
370 		while(!b_found && (uci = (*usb->get_nth_configuration)(device, cfg++))){
371 			uint16 itf = 0;
372 			for(; itf < uci->interface_count && !b_found; itf++){
373 				usb_interface_list *ifl = &uci->interface[itf];
374 				uint16 alt = 0;
375 				for(; alt < ifl->alt_count; alt++){
376 					usb_interface_info *uii = &ifl->alt[alt];
377 					usb_device_settings ud_settings;
378 					memset(udi, 0, sizeof(usb_device_info));
379 					memset(&ud_settings, 0, sizeof(usb_device_settings));
380 					udi->device = device;
381 					udi->interface	= itf;
382 					b_has_extra_settings = lookup_device_settings(udd, &ud_settings);
383 					switch(get_interface_properties(uii, &udi->properties)){
384 					case B_BAD_TYPE: /* non-standard USB class*/
385 						if(!b_has_extra_settings){
386 							continue; /* skip to next interface */
387 						} /* no break - fall through */
388 					case B_OK:
389 						if(b_has_extra_settings){
390 							if(PROTO(ud_settings.properties) != PROTO_NONE){
391 								udi->properties &= ~PROTO_MASK;
392 								udi->properties |= PROTO(ud_settings.properties);
393 							}
394 							if(CMDSET(ud_settings.properties) != CMDSET_NONE){
395 								udi->properties &= ~CMDSET_MASK;
396 								udi->properties |= CMDSET(ud_settings.properties);
397 							}
398 							udi->properties |= ud_settings.properties & FIX_MASK;
399 							TRACE("device_added: properties merged:%08x\n", udi->properties);
400 						}
401 						if( B_OK == setup_transport_modules(udi, &ud_settings)){
402 							break;
403 						} /* else - no break - fall through */
404 					default:
405 						continue; /* skip to next interface */
406 					}
407 					if(alt != 0){ /*TODO: are we need this ???*/
408 						if((status = (*usb->set_alt_interface)(device, uii)) != B_OK){
409 							TRACE_ALWAYS("device_added:setting alt interface failed:%s",
410 												strerror(status));
411 							goto Failed;/* Break - is it right?*/
412 						}
413 					}
414 					if((*usb->get_configuration)(device) != uci){
415 						if((status = (*usb->set_configuration)(device, uci)) != B_OK){
416 								TRACE_ALWAYS("device_added:setting configuration failed:%08x uci: %08x\n",
417 											 (*usb->get_configuration)(device), uci);
418 							TRACE_ALWAYS("device_added:setting configuration failed:%s\n",
419 													strerror(status));
420 
421 							goto Failed;/* Break - is it right?*/
422 						}
423 					}
424 					if(B_OK != setup_endpoints(uii, udi)){
425 						continue; /* skip to next interface */
426 					}
427 					if((status = allocate_resources(udi)) == B_OK){
428 						udi->b_trace = b_log_protocol;
429 						udi->trace = usb_scsi_trace;
430 						udi->trace_bytes = usb_scsi_trace_bytes;
431 						udi->trans_timeout = TRANS_TIMEOUT;
432 						udi->usb_m = usb;
433 						udi->not_ready_luns = 0xff; /*assume all LUNs initially not ready */
434 						if((status = (*udi->protocol_m->init)(udi)) == B_OK){
435 							TRACE("device_added[%d]: SUCCESS! Enjoy using!\n", udi->dev_num);
436 							*cookie = udi;
437 							b_found = true; /* we have found something useful - time to go out! */
438 							break;					/* ... now break alternatives iteration.*/
439 						} else {
440 							release_resources(udi);
441 						}
442 					}
443 					/* go to next iteration - check all configurations for possible devices */
444 				}/* for(...) iterate interface alternates*/
445 			}/* for(...) iterate interfaces*/
446 		}/* while(...) iterate configurations */
447 		if(status == B_OK){
448 			(*cam->minfo.rescan)();
449 		} else {
450 			free(udi);
451 		}
452 	} /* if(udi){ */
453 	if(status != B_OK){
454 		TRACE("device_added: probing failed (%s) for: %04x/%04x\n",
455 				strerror(status), udd->vendor_id, udd->product_id);
456 	}
457 Failed:
458 	return status;
459 }
460 /**
461 	\fn:device_removed
462 	\param cookie:???
463 	\return:???
464 
465 	???
466 */
467 status_t device_removed(void *cookie)
468 {
469 	status_t status = B_OK;
470 	usb_device_info *udi = (usb_device_info *)cookie;
471 	acquire_sem(udi->lock_sem); /* wait for possible I/O operation complete */
472 	release_resources(udi);
473 	/* no corresponding call of release_sem(udi->lock_sem);
474 		 - semaphore was deleted in release_resources, any waiting thread
475 		   was failed with BAD_SEM_ID.
476 	*/
477 	TRACE_ALWAYS("device_removed[%d]:All The Best !!!\n", udi->dev_num);
478 	free(udi);
479 	(*cam->minfo.rescan)();
480 	return status;
481 }
482 /**
483 	\fn:sim_init
484 	\return: ???
485 
486 	called on SIM init
487 */
488 static long sim_init(void)
489 {
490 	status_t status = B_OK;
491 	TRACE("sim_init\n");
492 	return status;
493 }
494 
495 /**
496 */
497 static bool
498 pre_check_scsi_io_request(usb_device_info *udi, CCB_SCSIIO *ccbio,
499 												status_t *ret_status)
500 {
501 	int target_id	 = ccbio->cam_ch.cam_target_id;
502 	uint8 target_lun = ccbio->cam_ch.cam_target_lun;
503 	*ret_status 	 = B_OK;
504 	/* handle reserved device and luns entries */
505 	if(b_reservation_on && udi == NULL &&
506 		 target_id < reserved_devices &&
507 		 target_lun < reserved_luns)
508 	{
509 		*ret_status = fake_scsi_io(ccbio);
510 		return false;
511 	}
512 	/* no device for this target | LUN */
513 	if(udi == NULL || target_lun > udi->max_lun){
514 		ccbio->cam_ch.cam_status = CAM_DEV_NOT_THERE;
515 		*ret_status = B_DEV_BAD_DRIVE_NUM;
516 		return false;
517 	}
518 	/* check command length */
519 	if(ccbio->cam_cdb_len != 6	&&
520 		 ccbio->cam_cdb_len != 10 &&
521 		 ccbio->cam_cdb_len != 12)
522 	{
523 		TRACE("Bad SCSI command length:%d.Ignore\n", ccbio->cam_cdb_len);
524 		ccbio->cam_ch.cam_status = CAM_REQ_INVALID;
525 		*ret_status = B_BAD_VALUE;
526 		return false;
527 	}
528 	/* Clean up auto sense buffer.
529 		 To avoid misunderstanding in not ready luns logic detection.*/
530 	if(NULL == ccbio->cam_sense_ptr){
531 		memset(&udi->autosense_data, 0, sizeof(udi->autosense_data));
532 	} else {
533 		memset(ccbio->cam_sense_ptr, 0, ccbio->cam_sense_len);
534 	}
535 	return true;
536 }
537 /**
538 */
539 static bool
540 pre_handle_features(usb_device_info *udi, CCB_SCSIIO *ccbio,
541 					scsi_cmd_generic *command, sg_buffer *sgb,
542 											status_t *ret_status)
543 {
544 	uint8 target_lun = ccbio->cam_ch.cam_target_lun;
545 	*ret_status = B_OK;
546 	udi->trans_timeout = TRANS_TIMEOUT;
547 	switch(command->opcode){
548 	case MODE_SELECT_6:
549 	case MODE_SENSE_6:{
550 		bool b_select = (MODE_SELECT_6 == command->opcode);
551 		const char*cmd_name = b_select ? "MODE_SELECT" : "MODE_SENSE";
552 		if(udi->not_ready_luns & (1 << target_lun)){
553 			TRACE("pre_handle_features:%s_6 bypassed for LUN:%d\n", cmd_name, target_lun);
554 			goto set_REQ_INVALID_and_return;
555 		}
556 		if(HAS_FIXES(udi->properties, FIX_FORCE_MS_TO_10)){
557 			if( B_OK != realloc_sg_buffer(sgb, ccbio->cam_dxfer_len + 4)){ /* TODO: 4 - hardcoded? */
558 				TRACE_ALWAYS("pre_handle_features:error allocating %d bytes for %s_10\n",
559 								 ccbio->cam_dxfer_len + 4, cmd_name);
560 				goto set_REQ_INVALID_and_return;
561 			}
562 			if(b_select){
563 				sg_buffer sgb_sense_6;
564 				/*TODO: implemenet and try refragment_sg_buffer - to check handling of real scatter/gather!!*/
565 				if(B_OK != init_sg_buffer(&sgb_sense_6, ccbio)){
566 					/* In case of error TRACE-ing was already performed in sg_ functions */
567 					goto set_REQ_INVALID_and_return;
568 				}
569 				TRACE_MODE_SENSE_SGB("MODE SELECT 6:", &sgb_sense_6);
570 				if(B_OK != sg_memcpy(sgb, 1, &sgb_sense_6, 0, 3) ||
571 					 B_OK != sg_memcpy(sgb, 7, &sgb_sense_6, 3, 1) ||
572 					 B_OK != sg_memcpy(sgb, 8, &sgb_sense_6, 4, ccbio->cam_dxfer_len - sizeof(scsi_mode_param_header_6)))
573 				{
574 					/* In case of error TRACE-ing was already performed in sg_ functions */
575 					goto set_REQ_INVALID_and_return;
576 				}
577 				TRACE_MODE_SENSE_SGB("MODE SELECT 10:", sgb);
578 			}
579 		} /*else {
580 			if(b_select){ / * trace data if configured in settings * /
581 				TRACE_MODE_SENSE_DATA("MODE_SELECT:", ccbio->cam_data_ptr, ccbio->cam_dxfer_len);
582 			}
583 		}*/
584 	}break;
585 	case INQUIRY: /* fake INQUIRY request */
586 		if(HAS_FIXES(udi->properties, FIX_NO_INQUIRY)){
587 			fake_inquiry_request(udi, ccbio);
588 			goto set_REQ_CMP_and_return;
589 		} break;
590 	case TEST_UNIT_READY: /* fake INQUIRY request */
591 		if(HAS_FIXES(udi->properties, FIX_NO_TEST_UNIT)){
592 			goto set_REQ_CMP_and_return;
593 		} break;
594 	case PREVENT_ALLOW_MEDIA_REMOVAL: /* fake PREVENT_ALLOW_MEDIA_REMOVAL request */
595 		if(HAS_FIXES(udi->properties, FIX_NO_PREVENT_MEDIA)){
596 			goto set_REQ_CMP_and_return;
597 		} break;
598 	case FORMAT_UNIT:
599 		udi->trans_timeout = B_INFINITE_TIMEOUT;
600 		break;
601 	default: break;
602 	}
603 	return true;
604 
605 set_REQ_CMP_and_return:
606 	ccbio->cam_ch.cam_status = CAM_REQ_CMP;
607 	return false;//*ret_status = B_OK;
608 
609 set_REQ_INVALID_and_return:
610 	ccbio->cam_ch.cam_status = CAM_REQ_INVALID;
611 	*ret_status = B_BAD_VALUE;
612 	return false;
613 }
614 /**
615 */
616 static bool
617 post_handle_features(usb_device_info *udi, CCB_SCSIIO *ccbio,
618 					scsi_cmd_generic *command, sg_buffer *sgb,
619 												status_t *ret_status)
620 {
621 	bool b_cmd_ok = (ccbio->cam_ch.cam_status == CAM_REQ_CMP);
622 	uint8 target_lun = ccbio->cam_ch.cam_target_lun;
623 	switch(command->opcode){
624 	case READ_6:
625 	case WRITE_6:
626 #if 0
627 /* Disabled - single problem can switch to 6-bytes mode. If device doesn't
628 	 support 6-bytes command all goes totally wrong. That's bad. */
629 		if(!b_cmd_ok && !HAS_FEATURES(udi->descr.properties, PROP_FORCE_RW_TO_6)){
630 			TRACE("post_handle_features:READ(10)/WRITE(10) failed - retry 6-byte one\n");
631 			udi->descr.properties |= PROP_FORCE_RW_TO_6;
632 			ccbio->cam_scsi_status = SCSI_STATUS_OK; /* clear the scsi_status. */
633 			ccbio->cam_ch.cam_status = CAM_REQ_INPROG; /* set status in progress again. */
634 			b_retry = true; /* inform caller about retry */
635 		}
636 #endif
637 		break;
638 	case MODE_SENSE_6:
639 		if(!b_cmd_ok && !HAS_FIXES(udi->properties, FIX_FORCE_MS_TO_10)){
640 			TRACE("post_handle_features:MODE SENSE(6) failed - retry 10-byte one\n");
641 			udi->properties |= FIX_FORCE_MS_TO_10;
642 			ccbio->cam_scsi_status = SCSI_STATUS_OK; /* clear the scsi_status. */
643 			ccbio->cam_ch.cam_status = CAM_REQ_INPROG; /* set status in progress again. */
644 			return true; /* inform caller about retry */
645 		}	/* no break, - fallthrough! */
646 	case MODE_SELECT_6:{
647 		if(MODE_SENSE_6 == command->opcode){
648 			sg_buffer sgb_sense_6;
649 			if(B_OK != init_sg_buffer(&sgb_sense_6, ccbio)){
650 				TRACE_ALWAYS("post_hanlde_features: initialize sgb failed\n");
651 				goto set_REQ_INVALID_and_return;
652 			}
653 			/* convert sense information from 10-byte request result to 6-byte one */
654 			if(HAS_FIXES(udi->properties, FIX_FORCE_MS_TO_10)){
655 				uchar mode_data_len_msb = 0, block_descr_len_msb = 0;
656 				if( B_OK != sg_access_byte(sgb, 0, &mode_data_len_msb, false) ||
657 						B_OK != sg_access_byte(sgb, 6, &block_descr_len_msb, false) ||
658 							 0 != mode_data_len_msb || 0 != block_descr_len_msb)
659 				{
660 					/* In case of error TRACE-ing was already performed in sg_ functions */
661 					TRACE_ALWAYS("MODE_SENSE 10->6 conversion overflow: %d, %d\n",
662 								 mode_data_len_msb, block_descr_len_msb);
663 					goto set_REQ_INVALID_and_return;
664 				}
665 				TRACE_MODE_SENSE_SGB("MODE SENSE 10:", sgb);
666 				if( B_OK != sg_memcpy(&sgb_sense_6, 0, sgb, 1, 3) ||
667 						B_OK != sg_memcpy(&sgb_sense_6, 3, sgb, 7, 1) ||
668 						B_OK != sg_memcpy(&sgb_sense_6, 4, sgb, 8, ccbio->cam_dxfer_len - sizeof(scsi_mode_param_header_6)))
669 				{
670 					/* In case of error TRACE-ing was already performed in sg_ functions */
671 					TRACE_ALWAYS("MODE_SENSE 10->6 conversion failed\n");
672 					goto set_REQ_INVALID_and_return;
673 				}
674 			}
675 			/* set write-protected flag if required by user */
676 			if(HAS_FIXES(udi->properties, FIX_FORCE_READ_ONLY)){
677 				status_t status = B_OK;
678 				uchar device_spec_params = 0;
679 				if(B_OK == (status = sg_access_byte(sgb, 2, &device_spec_params, false))){
680 					device_spec_params |= 0x80;
681 					status = sg_access_byte(sgb, 2, &device_spec_params, true);
682 				}
683 				if(B_OK != status){
684 					TRACE_ALWAYS("MODE_SENSE set READ-ONLY mode failed. Writing ALLOWED!\n");
685 					/*goto set_req_invalid_and_return;*/ /* not urgent. do not fail processing...	*/
686 				}
687 			}
688 			TRACE_MODE_SENSE_SGB("MODE SENSE 6:", &sgb_sense_6);
689 		}
690 		if(HAS_FIXES(udi->properties, FIX_FORCE_MS_TO_10)){
691 			free_sg_buffer(sgb);
692 		}
693 	} break;
694 	case TEST_UNIT_READY: { /* set the not ready luns flag */
695 			scsi_sense_data *sense_data = (ccbio->cam_sense_ptr == NULL) ?
696 				&udi->autosense_data : (scsi_sense_data *)ccbio->cam_sense_ptr;
697 			if((sense_data->flags & SSD_KEY) == SSD_KEY_NOT_READY){
698 				udi->not_ready_luns |= (1 << target_lun);
699 			} else {
700 				udi->not_ready_luns &= ~(1 << target_lun);
701 			}
702 			usb_scsi_trace_bytes("NOT_READY_LUNS:", &udi->not_ready_luns, 1);
703 		} break;
704 	case READ_CAPACITY:{
705 		/*uint8 *bts = sgb->piov->iov_base;
706 		uint32 capacity = (bts[0]<<24) + (bts[1]<<16) + (bts[2]<<8) + (bts[3]);
707 		TRACE_ALWAYS("CAPAC:%d\n", capacity);
708 		//bts[3] -= 3;*/
709 		TRACE_CAPACITY("READ_CAPACITY:", sgb);
710 	} break;
711 	default: break;
712 	}
713 	return false; /* do not retry - all is OK */ //b_retry;
714 
715 set_REQ_INVALID_and_return:
716 	ccbio->cam_ch.cam_status = CAM_REQ_INVALID;
717 	*ret_status = B_BAD_VALUE;
718 	return false; /* do not retry - fatal error */
719 }
720 /**
721 	\fn:xpt_scsi_io
722 	\param ccbio: ????
723 	\return: ???
724 
725 	xpt_scsi_io - handle XPT_SCSI_IO sim action
726 */
727 status_t xpt_scsi_io(CCB_SCSIIO *ccbio)
728 {
729 	status_t status			= B_OK;
730 	usb_device_info *udi = usb_devices[ccbio->cam_ch.cam_target_id/*target_id*/];
731 	uint8 *cmd;
732 	sg_buffer sgb;
733 
734 	/* clear the scsi_status. It can come from system with some garbage value ...*/
735 	ccbio->cam_scsi_status = SCSI_STATUS_OK;
736 	/* check the request for correct parameters, valid targets, luns etc ... */
737 	if(false == pre_check_scsi_io_request(udi, ccbio, &status)){
738 		return status;
739 	}
740 
741 #if 0 /* activate if you need detailed logging of CCB_SCSI request*/
742 	usb_scsi_trace_CCB_SCSIIO(ccbio);
743 #endif
744 
745 
746 	/* acquire semaphore - avoid re-enters */
747 /*	if((status = acquire_sem_etc(udi->lock_sem, 1,
748 								B_RELATIVE_TIMEOUT,
749 								udi->trans_timeout)) != B_OK) */
750 //	TRACE_ALWAYS("sem before acq:%08x\n",udi->lock_sem);
751 	if((status = acquire_sem(udi->lock_sem)) != B_OK){
752 		/* disabled - CAM_BUSY flag is not recognized by BeOS ... :-(
753 		if(status == B_WOULD_BLOCK){
754 			TRACE("locked sema bypass OK\n");
755 			ccbio->cam_ch.cam_status = CAM_BUSY;
756 			return B_OK;
757 		}*/
758 		TRACE("xpt_scsi_io:acquire_sem_etc() failed:%08x\n", status);
759 		ccbio->cam_ch.cam_status = CAM_DEV_NOT_THERE;
760 		return B_DEV_BAD_DRIVE_NUM;
761 	}
762 	/* set the command data pointer */
763 	if(ccbio->cam_ch.cam_flags & CAM_CDB_POINTER){
764 		cmd = ccbio->cam_cdb_io.cam_cdb_ptr;
765 	}else{
766 		cmd = ccbio->cam_cdb_io.cam_cdb_bytes;
767 	}
768 	/* NOTE: using stack copy of sg_buffer sgb. It can be modified/freed in
769 					 *_handle_features() functions! Twice reallocation is also not awaited!
770 					 Note this on refactoring!!! */
771 	/*TODO returns!*/
772 	init_sg_buffer(&sgb, ccbio);
773 	do{ /* <-- will be repeated if 6-byte RW/MS commands failed */
774 		uint8 *rcmd;
775 		uint8 rcmdlen;
776 		uint32 transfer_len = 0;
777 		EDirection dir = eDirNone;
778 		/* handle various features for this device */
779 		if(!pre_handle_features(udi, ccbio, (scsi_cmd_generic *)cmd, &sgb, &status)){
780 			release_sem(udi->lock_sem);
781 			return status;
782 		}
783 		/* transform command as required by protocol */
784 		rcmd		= udi->scsi_command_buf;
785 		rcmdlen = sizeof(udi->scsi_command_buf);
786 		if((status = (*udi->transform_m->transform)(udi, cmd, ccbio->cam_cdb_len & 0x1f,
787 													 &rcmd, &rcmdlen)) != B_OK)
788 		{
789 			TRACE_ALWAYS("xpt_scsi_io: transform failed: %08x\n", status);
790 			ccbio->cam_ch.cam_status = CAM_REQ_INVALID;
791 			release_sem(udi->lock_sem);
792 			return B_BAD_VALUE;
793 		}
794 		/* set correct direction flag */
795 		switch(CAM_DIR_MASK & ccbio->cam_ch.cam_flags){
796 		case CAM_DIR_IN:	dir = eDirIn;	break;
797 		case CAM_DIR_OUT:	dir = eDirOut;	break;
798 		default:			dir = eDirNone;	break;
799 		}
800 
801 		TRACE_DATA_IO_SG(sgb.piov, sgb.count);
802 
803 		/*TODO: return!*/
804 		sg_buffer_len(&sgb, &transfer_len);
805 		/* transfer command to device. SCSI status will be handled in callback */
806 		(*udi->protocol_m->transfer)(udi, rcmd, rcmdlen, sgb.piov, sgb.count,
807 									 transfer_len/*ccbio->cam_dxfer_len*/,
808 										dir, ccbio, transfer_callback);
809 		/* perform some post-tranfer features handling
810 			 and automatic 6-10 bytes command support detection */
811 	} while(post_handle_features(udi, ccbio, (scsi_cmd_generic *)cmd, &sgb, &status));
812 	release_sem(udi->lock_sem);
813 	return status;
814 }
815 /**
816 	\fn:xpt_path_inquiry
817 	\param ccbp:
818 	\return:
819 
820 	xpt_path_inquiry - handle XPT_PATH_INQ sim action
821 */
822 status_t xpt_path_inquiry(CCB_PATHINQ *ccbp)
823 {
824 	status_t status			= B_OK;
825 
826 	ccbp->cam_version_num	= SIM_VERSION;
827 	ccbp->cam_target_sprt	= 0;
828 	ccbp->cam_hba_eng_cnt	= 0;
829 	memset (ccbp->cam_vuhba_flags, 0, VUHBA);
830 	ccbp->cam_sim_priv		= SIM_PRIV;
831 	ccbp->cam_async_flags	= 0;
832 	ccbp->cam_initiator_id	= CONTROLLER_SCSI_ID;
833 	ccbp->cam_hba_inquiry	= CONTROLLER_SCSI_BUS; /* Narrow SCSI bus */
834 	ccbp->cam_hba_misc		= PIM_NOINQUIRY;
835 	ccbp->cam_osd_usage		= 0;
836 	/* There should be special handling of path_id == 0xff
837 		 but looks like it's not used by BeOS now */
838 	/*ccbp->cam_hpath_id = path_id;*/
839 	strncpy(ccbp->cam_sim_vid, sim_vendor_name, SIM_ID);
840 	strncpy(ccbp->cam_hba_vid, hba_vendor_name, HBA_ID);
841 	ccbp->cam_ch.cam_status = CAM_REQ_CMP;
842 	return status;
843 }
844 /**
845 	\fn:xpt_extended_path_inquiry
846 	\param ccbep: ???
847 	\return:???
848 
849 	xpt_extended_path_inquiry - handle XPT_EXTENDED_PATH_INQ sim action
850 */
851 status_t xpt_extended_path_inquiry(CCB_EXTENDED_PATHINQ *ccbep)
852 {
853 	status_t status = B_OK;
854 	xpt_path_inquiry((CCB_PATHINQ *)ccbep);
855 	sprintf(ccbep->cam_sim_version, "%d.0", SIM_VERSION);
856 	sprintf(ccbep->cam_hba_version, "%d.0", HBA_VERSION);
857 	strncpy(ccbep->cam_controller_family, controller_family, FAM_ID);
858 	strncpy(ccbep->cam_controller_type, "USB-SCSI", TYPE_ID);
859 	return status;
860 }
861 /**
862 	\fn:sim_action
863 	\param ccbh: ????
864 	\return: ????
865 
866 	This fucntion performs SCSI interface module actions -
867 	calls corresponding xpt_* - functions.
868 */
869 static long sim_action(CCB_HEADER *ccbh)
870 {
871 	status_t status = B_ERROR;
872 	if(path_id != ccbh->cam_path_id){
873 		TRACE_ALWAYS("sim_action:path_id mismatch of func:%d:our:%d,requested:%d\n",
874 					 ccbh->cam_func_code, path_id, ccbh->cam_path_id);
875 		ccbh->cam_status = CAM_PATH_INVALID;
876 	} else {
877 		ccbh->cam_status = CAM_REQ_INPROG;
878 		switch(ccbh->cam_func_code){
879 			case XPT_SCSI_IO:
880 				status = xpt_scsi_io((CCB_SCSIIO *)ccbh);
881 				break;
882 			case XPT_PATH_INQ:
883 				status = xpt_path_inquiry((CCB_PATHINQ *)ccbh);
884 				break;
885 			case XPT_EXTENDED_PATH_INQ:
886 				status = xpt_extended_path_inquiry((CCB_EXTENDED_PATHINQ *)ccbh);
887 				break;
888 			default:
889 				TRACE_ALWAYS("sim_action: unsupported function: %x\n", ccbh->cam_func_code);
890 				ccbh->cam_status = CAM_REQ_INVALID;
891 				break;
892 		}
893 	}
894 	return status;
895 }
896 /**
897 	\fn:std_ops
898 	\param op: operation to be performed on this module
899 	\param ...: possible additional arguments
900 	\return: B_OK on success, error status on failure
901 
902 	This function deals with standard module operations. Currently, the only
903 	two things that entails are initialization and uninitialization.
904 	- get the SCSI Bus Manager Module and USB Manager Module
905 	- put them when we're finished
906 */
907 static status_t std_ops(int32 op, ...)
908 {
909 	int i;
910 	status_t status = B_ERROR;
911 	CAM_SIM_ENTRY entry;
912 	switch(op) {
913 	case B_MODULE_INIT:
914 		if(0 == atomic_add(&load_count, 1)){
915 			thread_info tinfo = {0};
916 			load_module_settings();
917 			get_thread_info(find_thread(0), &tinfo);
918 			if(!b_ignore_sysinit2 || (0 != strcmp(tinfo.name, "sysinit2"))){
919 				create_log();
920 				if(get_module(B_USB_MODULE_NAME, (module_info **)&usb) == B_OK){
921 					if(get_module(B_CAM_FOR_SIM_MODULE_NAME, (module_info **)&cam) == B_OK){
922 						for(i = 0; i < MAX_DEVICES_COUNT; i++)
923 							usb_devices[i] = NULL;
924 
925 						if((*usb->register_driver)(MODULE_NAME, supported_devices, SIZEOF(supported_devices), "usb_dsk") == B_OK){
926 							if((*usb->install_notify)(MODULE_NAME, &notify_hooks) == B_OK){
927 								entry.sim_init = sim_init;
928 								entry.sim_action = sim_action;
929 								path_id =(*cam->xpt_bus_register)(&entry);
930 								usb_serial_lock = create_sem(1, MODULE_NAME"_devices_table_lock");
931 								status = B_OK;
932 								break;
933 							}
934 						}
935 						put_module(B_CAM_FOR_SIM_MODULE_NAME);
936 					}
937 					put_module(B_USB_MODULE_NAME);
938 				}
939 			} else {
940 				TRACE_ALWAYS("std_ops INIT call was ignored for thread:%s\n", tinfo.name);
941 			}
942 		} else {
943 			atomic_add(&load_count, -1);
944 		}
945 		break;
946 	case B_MODULE_UNINIT:
947 		if(1 == atomic_add(&load_count, -1)){
948 			(*usb->uninstall_notify)(MODULE_NAME);
949 			status = B_OK;
950 			if(path_id != -1){
951 				(*cam->xpt_bus_deregister)(path_id);
952 				path_id = -1;
953 			}
954 			delete_sem(usb_serial_lock);
955 			put_module(B_USB_MODULE_NAME);
956 			put_module(B_CAM_FOR_SIM_MODULE_NAME);
957 		} else {
958 			atomic_add(&load_count, 1);
959 		}
960 		break;
961 	}
962 	return status;
963 }
964 
965 /**
966 	Declare our module_info so we can be loaded as a kernel module
967 */
968 static sim_module_info sim_usb_module = {
969 	{ "busses/scsi/usb/v1", 0, &std_ops }
970 };
971 /**
972 	Export module_info-s list
973 */
974 _EXPORT module_info *modules[] = {
975 	(module_info *) &sim_usb_module,
976 	NULL
977 };
978 
979