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