1/**
2 *
3 * TODO: description
4 *
5 * This file is a part of USB SCSI CAM for Haiku.
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
38status_t device_added(const usb_device device, void **cookie);
39
40status_t device_removed(void *cookie);
41
42static long sim_action(CCB_HEADER *ccbh);
43static 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
56static long path_id		= -1;
57static int32 load_count	= 0;
58
59static char sim_vendor_name[]	= "Haiku";		/* who wrote this driver */
60static char hba_vendor_name[]	= "USB";		/* who made the hardware */
61static char controller_family[]	= "USB SCSI";	/* what family of products */
62
63struct usb_support_descriptor supported_devices[] = {
64	{0, 0, 0, 0, 0}
65};
66
67#define SIZEOF(array) (sizeof(array)/sizeof(array[0])) //?????
68
69usb_device_info *usb_devices[MAX_DEVICES_COUNT];
70/* main devices table locking semaphore */
71sem_id usb_serial_lock = -1;
72
73usb_module_info *usb;
74static cam_for_sim_module_info *cam;
75
76struct usb_notify_hooks notify_hooks = {
77	device_added,
78	device_removed
79};
80
81/* function prototupes */
82static status_t get_interface_properties(usb_interface_info *uii, uint32 *pproperties);
83static status_t load_vendor_module(char **path, const char *path_mask, const char *prop, module_info **mi);
84static status_t setup_transport_modules(usb_device_info *udi, usb_device_settings *uds);
85static status_t setup_endpoints(usb_interface_info *uii, usb_device_info *udi);
86static status_t allocate_resources(usb_device_info *udi);
87static void 	release_resources(usb_device_info *udi);
88static status_t xpt_scsi_io(CCB_SCSIIO *ccbio);
89static status_t xpt_path_inquiry(CCB_PATHINQ *ccbp);
90static 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*/
101status_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*/
139status_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*/
159status_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
217static void
218release_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*/
235status_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*/
292status_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*/
338void 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*/
358status_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	}
461Failed:
462	return status;
463}
464/**
465	\fn:device_removed
466	\param cookie:???
467	\return:???
468
469	???
470*/
471status_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*/
492static long sim_init(void)
493{
494	status_t status = B_OK;
495	TRACE("sim_init\n");
496	return status;
497}
498
499/**
500*/
501static bool
502pre_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*/
543static bool
544pre_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
609set_REQ_CMP_and_return:
610	ccbio->cam_ch.cam_status = CAM_REQ_CMP;
611	return false;//*ret_status = B_OK;
612
613set_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*/
620static bool
621post_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
719set_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*/
731status_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*/
826status_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*/
855status_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*/
873static 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*/
913static 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 */
979static float
980supports_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 */
992static status_t
993register_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 */
1005static status_t
1006init_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 */
1018static status_t
1019uninit_module(void *cookie)
1020{
1021	TRACE_ALWAYS("uninit_driver\n");
1022	return B_OK;
1023}
1024
1025/**
1026 * \fn:
1027 * \param :
1028 *  TODO
1029 */
1030static void
1031device_removed(device_node_handle node, void *cookie)
1032{
1033	TRACE_ALWAYS("device_removed\n");
1034}
1035
1036/**
1037 * \fn:
1038 * \param :
1039 *  TODO
1040 */
1041static void
1042device_cleanup(device_node_handle node)
1043{
1044	TRACE_ALWAYS("device_cleanup\n");
1045}
1046
1047/**
1048 * \fn:
1049 * \param :
1050 *  TODO
1051 */
1052static void
1053get_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 */
1063static void
1064scsi_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 */
1075static uchar
1076abort( 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 */
1088static uchar
1089reset_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 */
1101static uchar
1102term_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 */
1114static uchar
1115path_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 */
1127static uchar
1128scan_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 */
1140static uchar
1141reset_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 */
1152static void
1153get_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 */
1164static status_t
1165module_ioctl(scsi_sim_cookie cookie, uint8 targetID, uint32 op, void *buffer, size_t length)
1166{
1167	TRACE_ALWAYS("ioctl\n");
1168	return B_DEV_INVALID_IOCTL;
1169}
1170
1171
1172/**
1173	Declare our module_info so we can be loaded as a kernel module
1174*/
1175static 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