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