xref: /haiku/src/add-ons/kernel/bus_managers/scsi/devices.cpp (revision e523d3cfc19042fd61bc09cb10935947dfd16c71)
14535495dSIngo Weinhold /*
24535495dSIngo Weinhold  * Copyright 2004-2006, Haiku, Inc. All RightsReserved.
34535495dSIngo Weinhold  * Copyright 2002/03, Thomas Kurschel. All rights reserved.
44535495dSIngo Weinhold  *
54535495dSIngo Weinhold  * Distributed under the terms of the MIT License.
64535495dSIngo Weinhold  */
74535495dSIngo Weinhold 
84535495dSIngo Weinhold /*
94535495dSIngo Weinhold 	Device node layer.
104535495dSIngo Weinhold 
114535495dSIngo Weinhold 	When a SCSI bus is registered, this layer scans for SCSI devices
124535495dSIngo Weinhold 	and registers a node for each of them. Peripheral drivers are on
134535495dSIngo Weinhold 	top of these nodes.
144535495dSIngo Weinhold */
154535495dSIngo Weinhold 
164535495dSIngo Weinhold #include "scsi_internal.h"
174535495dSIngo Weinhold 
184535495dSIngo Weinhold #include <string.h>
194535495dSIngo Weinhold #include <stdlib.h>
204535495dSIngo Weinhold #include <stdio.h>
214535495dSIngo Weinhold 
224535495dSIngo Weinhold #include <algorithm>
234535495dSIngo Weinhold 
244535495dSIngo Weinhold 
254535495dSIngo Weinhold /** free autosense request of device */
264535495dSIngo Weinhold 
274535495dSIngo Weinhold static void
284535495dSIngo Weinhold scsi_free_autosense_request(scsi_device_info *device)
294535495dSIngo Weinhold {
304535495dSIngo Weinhold 	SHOW_FLOW0( 3, "" );
314535495dSIngo Weinhold 
324535495dSIngo Weinhold 	if (device->auto_sense_request != NULL) {
334535495dSIngo Weinhold 		scsi_free_ccb(device->auto_sense_request);
344535495dSIngo Weinhold 		device->auto_sense_request = NULL;
354535495dSIngo Weinhold 	}
364535495dSIngo Weinhold 
374535495dSIngo Weinhold 	if (device->auto_sense_area > 0) {
384535495dSIngo Weinhold 		delete_area(device->auto_sense_area);
394535495dSIngo Weinhold 		device->auto_sense_area = 0;
404535495dSIngo Weinhold 	}
414535495dSIngo Weinhold }
424535495dSIngo Weinhold 
434535495dSIngo Weinhold 
444535495dSIngo Weinhold /** free all data of device */
454535495dSIngo Weinhold 
464535495dSIngo Weinhold static void
474535495dSIngo Weinhold scsi_free_device(scsi_device_info *device)
484535495dSIngo Weinhold {
494535495dSIngo Weinhold 	SHOW_FLOW0( 3, "" );
504535495dSIngo Weinhold 
514535495dSIngo Weinhold 	scsi_free_emulation_buffer(device);
524535495dSIngo Weinhold 	scsi_free_autosense_request(device);
534535495dSIngo Weinhold 
544535495dSIngo Weinhold 	unregister_kernel_daemon(scsi_dma_buffer_daemon, device);
554535495dSIngo Weinhold 
564535495dSIngo Weinhold 	scsi_dma_buffer_free(&device->dma_buffer);
574535495dSIngo Weinhold 
584535495dSIngo Weinhold 	DELETE_BEN(&device->dma_buffer_lock);
594535495dSIngo Weinhold 	delete_sem(device->dma_buffer_owner);
604535495dSIngo Weinhold 
614535495dSIngo Weinhold 	free(device);
624535495dSIngo Weinhold }
634535495dSIngo Weinhold 
644535495dSIngo Weinhold 
654535495dSIngo Weinhold /**	copy string src without trailing zero to dst and remove trailing
664535495dSIngo Weinhold  *	spaces size of dst is dst_size, size of src is dst_size-1
674535495dSIngo Weinhold  */
684535495dSIngo Weinhold 
694535495dSIngo Weinhold static void
704535495dSIngo Weinhold beautify_string(char *dst, char *src, int dst_size)
714535495dSIngo Weinhold {
724535495dSIngo Weinhold 	int i;
734535495dSIngo Weinhold 
744535495dSIngo Weinhold 	memcpy(dst, src, dst_size - 1);
754535495dSIngo Weinhold 
764535495dSIngo Weinhold 	for (i = dst_size - 2; i >= 0; --i) {
774535495dSIngo Weinhold 		if (dst[i] != ' ')
784535495dSIngo Weinhold 			break;
794535495dSIngo Weinhold 	}
804535495dSIngo Weinhold 
814535495dSIngo Weinhold 	dst[i + 1] = 0;
824535495dSIngo Weinhold }
834535495dSIngo Weinhold 
844535495dSIngo Weinhold 
854535495dSIngo Weinhold /** register new device */
864535495dSIngo Weinhold 
874535495dSIngo Weinhold status_t
884535495dSIngo Weinhold scsi_register_device(scsi_bus_info *bus, uchar target_id,
894535495dSIngo Weinhold 	uchar target_lun, scsi_res_inquiry *inquiry_data)
904535495dSIngo Weinhold {
914535495dSIngo Weinhold 	bool is_atapi, manual_autosense;
924535495dSIngo Weinhold 	uint32 orig_max_blocks, max_blocks;
934535495dSIngo Weinhold 
944535495dSIngo Weinhold 	SHOW_FLOW0( 3, "" );
954535495dSIngo Weinhold 
964535495dSIngo Weinhold 	// ask for restrictions
974535495dSIngo Weinhold 	bus->interface->get_restrictions(bus->sim_cookie,
984535495dSIngo Weinhold 		target_id, &is_atapi, &manual_autosense, &max_blocks);
994535495dSIngo Weinhold 	if (target_lun != 0)
1004535495dSIngo Weinhold 		dprintf("WARNING: SCSI target %d lun %d getting restrictions without lun\n",
1014535495dSIngo Weinhold 			target_id, target_lun);
1024535495dSIngo Weinhold 
1034535495dSIngo Weinhold 	// find maximum transfer blocks
1044535495dSIngo Weinhold 	// set default value to max (need something like ULONG_MAX here)
1054535495dSIngo Weinhold 	orig_max_blocks = ~0;
1064535495dSIngo Weinhold 	pnp->get_attr_uint32(bus->node, B_DMA_MAX_TRANSFER_BLOCKS, &orig_max_blocks,
1074535495dSIngo Weinhold 		true);
1084535495dSIngo Weinhold 
1094535495dSIngo Weinhold 	max_blocks = std::min(max_blocks, orig_max_blocks);
1104535495dSIngo Weinhold 
1114535495dSIngo Weinhold 	{
1124535495dSIngo Weinhold 		char vendor_ident[sizeof( inquiry_data->vendor_ident ) + 1];
1134535495dSIngo Weinhold 		char product_ident[sizeof( inquiry_data->product_ident ) + 1];
1144535495dSIngo Weinhold 		char product_rev[sizeof( inquiry_data->product_rev ) + 1];
1154535495dSIngo Weinhold 		device_attr attrs[] = {
1164535495dSIngo Weinhold 			// connection
1174535495dSIngo Weinhold 			{ SCSI_DEVICE_TARGET_ID_ITEM, B_UINT8_TYPE, { ui8: target_id }},
1184535495dSIngo Weinhold 			{ SCSI_DEVICE_TARGET_LUN_ITEM, B_UINT8_TYPE, { ui8: target_lun }},
1194535495dSIngo Weinhold 
1204535495dSIngo Weinhold 			// inquiry data (used for both identification and information)
1214535495dSIngo Weinhold 			{ SCSI_DEVICE_INQUIRY_ITEM, B_RAW_TYPE,
1224535495dSIngo Weinhold 				{ raw: { inquiry_data, sizeof( *inquiry_data ) }}},
1234535495dSIngo Weinhold 
1244535495dSIngo Weinhold 			// some more info for driver loading
1254535495dSIngo Weinhold 			{ SCSI_DEVICE_TYPE_ITEM, B_UINT8_TYPE, { ui8: inquiry_data->device_type }},
1264535495dSIngo Weinhold 			{ SCSI_DEVICE_VENDOR_ITEM, B_STRING_TYPE, { string: vendor_ident }},
1274535495dSIngo Weinhold 			{ SCSI_DEVICE_PRODUCT_ITEM, B_STRING_TYPE, { string: product_ident }},
1284535495dSIngo Weinhold 			{ SCSI_DEVICE_REVISION_ITEM, B_STRING_TYPE, { string: product_rev }},
1294535495dSIngo Weinhold 
1304535495dSIngo Weinhold 			// description of peripheral drivers
1314535495dSIngo Weinhold 			{ B_DEVICE_BUS, B_STRING_TYPE, { string: "scsi" }},
1324535495dSIngo Weinhold 
1334535495dSIngo Weinhold 			// extra restriction of maximum number of blocks per transfer
1344535495dSIngo Weinhold 			{ B_DMA_MAX_TRANSFER_BLOCKS, B_UINT32_TYPE, { ui32: max_blocks }},
1354535495dSIngo Weinhold 
1364535495dSIngo Weinhold 			// atapi emulation
1374535495dSIngo Weinhold 			{ SCSI_DEVICE_IS_ATAPI_ITEM, B_UINT8_TYPE, { ui8: is_atapi }},
1384535495dSIngo Weinhold 			// manual autosense
1394535495dSIngo Weinhold 			{ SCSI_DEVICE_MANUAL_AUTOSENSE_ITEM, B_UINT8_TYPE, { ui8: manual_autosense }},
1404535495dSIngo Weinhold 			{ NULL }
1414535495dSIngo Weinhold 		};
1424535495dSIngo Weinhold 
1434535495dSIngo Weinhold 		beautify_string(vendor_ident, inquiry_data->vendor_ident, sizeof(vendor_ident));
1444535495dSIngo Weinhold 		beautify_string(product_ident, inquiry_data->product_ident, sizeof(product_ident));
1454535495dSIngo Weinhold 		beautify_string(product_rev, inquiry_data->product_rev, sizeof(product_rev));
1464535495dSIngo Weinhold 
1474535495dSIngo Weinhold 		return pnp->register_node(bus->node, SCSI_DEVICE_MODULE_NAME, attrs,
1484535495dSIngo Weinhold 			NULL, NULL);
1494535495dSIngo Weinhold 	}
1504535495dSIngo Weinhold 
1514535495dSIngo Weinhold 	return B_OK;
1524535495dSIngo Weinhold }
1534535495dSIngo Weinhold 
1544535495dSIngo Weinhold 
1554535495dSIngo Weinhold // create data structure for a device
1564535495dSIngo Weinhold static scsi_device_info *
1574535495dSIngo Weinhold scsi_create_device(device_node *node, scsi_bus_info *bus,
1584535495dSIngo Weinhold 	int target_id, int target_lun)
1594535495dSIngo Weinhold {
1604535495dSIngo Weinhold 	scsi_device_info *device;
1614535495dSIngo Weinhold 
1624535495dSIngo Weinhold 	SHOW_FLOW0( 3, "" );
1634535495dSIngo Weinhold 
1644535495dSIngo Weinhold 	device = (scsi_device_info *)malloc(sizeof(*device));
1654535495dSIngo Weinhold 	if (device == NULL)
1664535495dSIngo Weinhold 		return NULL;
1674535495dSIngo Weinhold 
1684535495dSIngo Weinhold 	memset(device, 0, sizeof(*device));
1694535495dSIngo Weinhold 
1704535495dSIngo Weinhold 	device->lock_count = device->blocked[0] = device->blocked[1] = 0;
1714535495dSIngo Weinhold 	device->sim_overflow = 0;
1724535495dSIngo Weinhold 	device->queued_reqs = NULL;
1734535495dSIngo Weinhold 	device->bus = bus;
1744535495dSIngo Weinhold 	device->target_id = target_id;
1754535495dSIngo Weinhold 	device->target_lun = target_lun;
1764535495dSIngo Weinhold 	device->valid = true;
1774535495dSIngo Weinhold 	device->node = node;
1784535495dSIngo Weinhold 
1794535495dSIngo Weinhold 	scsi_dma_buffer_init(&device->dma_buffer);
1804535495dSIngo Weinhold 
1814535495dSIngo Weinhold 	if (INIT_BEN(&device->dma_buffer_lock, "dma_buffer") < 0)
1824535495dSIngo Weinhold 		goto err;
1834535495dSIngo Weinhold 
1844535495dSIngo Weinhold 	device->dma_buffer_owner = create_sem(1, "dma_buffer");
1854535495dSIngo Weinhold 	if (device->dma_buffer_owner < 0)
1864535495dSIngo Weinhold 		goto err2;
1874535495dSIngo Weinhold 
1884535495dSIngo Weinhold 	register_kernel_daemon(scsi_dma_buffer_daemon, device, 5 * 10);
1894535495dSIngo Weinhold 
1904535495dSIngo Weinhold 	return device;
1914535495dSIngo Weinhold 
1924535495dSIngo Weinhold err2:
1934535495dSIngo Weinhold 	DELETE_BEN(&device->dma_buffer_lock);
1944535495dSIngo Weinhold err:
1954535495dSIngo Weinhold 	free(device);
1964535495dSIngo Weinhold 	return NULL;
1974535495dSIngo Weinhold }
1984535495dSIngo Weinhold 
1994535495dSIngo Weinhold 
2004535495dSIngo Weinhold /**	prepare autosense request.
2014535495dSIngo Weinhold  *	this cannot be done on demand but during init as we may
2024535495dSIngo Weinhold  *	have run out of ccbs when we need it
2034535495dSIngo Weinhold  */
2044535495dSIngo Weinhold 
2054535495dSIngo Weinhold static status_t
2064535495dSIngo Weinhold scsi_create_autosense_request(scsi_device_info *device)
2074535495dSIngo Weinhold {
2084535495dSIngo Weinhold 	scsi_ccb *request;
2094535495dSIngo Weinhold 	unsigned char *buffer;
2104535495dSIngo Weinhold 	scsi_cmd_request_sense *cmd;
2114535495dSIngo Weinhold 	size_t total_size;
2124535495dSIngo Weinhold 
2134535495dSIngo Weinhold 	SHOW_FLOW0( 3, "" );
2144535495dSIngo Weinhold 
2154535495dSIngo Weinhold 	device->auto_sense_request = request = scsi_alloc_ccb(device);
2164535495dSIngo Weinhold 	if (device->auto_sense_request == NULL)
2174535495dSIngo Weinhold 		return B_NO_MEMORY;
2184535495dSIngo Weinhold 
2194535495dSIngo Weinhold 	total_size = SCSI_MAX_SENSE_SIZE + sizeof(physical_entry);
2204535495dSIngo Weinhold 	total_size = (total_size + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
2214535495dSIngo Weinhold 
2224535495dSIngo Weinhold 	// allocate buffer for space sense data and S/G list
2234535495dSIngo Weinhold 	device->auto_sense_area = create_area("auto_sense", (void**)&buffer,
2244535495dSIngo Weinhold 		B_ANY_KERNEL_ADDRESS, B_PAGE_SIZE, B_32_BIT_FULL_LOCK, 0);
2254535495dSIngo Weinhold 		// TODO: Use B_FULL_LOCK, if addresses >= 4 GB are supported!
2264535495dSIngo Weinhold 	if (device->auto_sense_area < 0)
2274535495dSIngo Weinhold 		goto err;
2284535495dSIngo Weinhold 
2294535495dSIngo Weinhold 	request->data = buffer;
2304535495dSIngo Weinhold 	request->data_length = SCSI_MAX_SENSE_SIZE;
2314535495dSIngo Weinhold 	request->sg_list = (physical_entry *)(buffer + SCSI_MAX_SENSE_SIZE);
2324535495dSIngo Weinhold 	request->sg_count = 1;
2334535495dSIngo Weinhold 
2344535495dSIngo Weinhold 	get_memory_map(buffer, SCSI_MAX_SENSE_SIZE,
2354535495dSIngo Weinhold 		(physical_entry *)request->sg_list, 1);
2364535495dSIngo Weinhold 
2374535495dSIngo Weinhold 	// disable auto-autosense, just in case;
2384535495dSIngo Weinhold 	// make sure no other request overtakes sense request;
2394535495dSIngo Weinhold 	// buffer is/must be DMA safe as we cannot risk trouble with
2404535495dSIngo Weinhold 	// dynamically allocated DMA buffer
2414535495dSIngo Weinhold 	request->flags = SCSI_DIR_IN | SCSI_DIS_AUTOSENSE |
2424535495dSIngo Weinhold 		SCSI_ORDERED_QTAG | SCSI_DMA_SAFE;
2434535495dSIngo Weinhold 
2444535495dSIngo Weinhold 	cmd = (scsi_cmd_request_sense *)request->cdb;
2454535495dSIngo Weinhold 	request->cdb_length = sizeof(*cmd);
2464535495dSIngo Weinhold 
2474535495dSIngo Weinhold 	memset(cmd, 0, sizeof(*cmd));
2484535495dSIngo Weinhold 	cmd->opcode = SCSI_OP_REQUEST_SENSE;
2494535495dSIngo Weinhold 	cmd->lun = device->target_lun;
2504535495dSIngo Weinhold 	cmd->allocation_length = SCSI_MAX_SENSE_SIZE;
2514535495dSIngo Weinhold 
2524535495dSIngo Weinhold 	return B_OK;
2534535495dSIngo Weinhold 
2544535495dSIngo Weinhold err:
2554535495dSIngo Weinhold 	scsi_free_ccb(request);
2564535495dSIngo Weinhold 	return B_NO_MEMORY;
2574535495dSIngo Weinhold }
2584535495dSIngo Weinhold 
2594535495dSIngo Weinhold 
2604535495dSIngo Weinhold #define SET_BIT(field, bit) field[(bit) >> 3] |= 1 << ((bit) & 7)
2614535495dSIngo Weinhold 
2624535495dSIngo Weinhold static status_t
2634535495dSIngo Weinhold scsi_init_device(device_node *node, void **cookie)
2644535495dSIngo Weinhold {
2654535495dSIngo Weinhold 	const scsi_res_inquiry *inquiry_data = NULL;
2664535495dSIngo Weinhold 	uint8 target_id, target_lun, path_id;
2674535495dSIngo Weinhold 	scsi_bus_info *bus;
2684535495dSIngo Weinhold 	scsi_device_info *device;
2694535495dSIngo Weinhold 	status_t res;
2704535495dSIngo Weinhold 	size_t inquiry_data_len;
2714535495dSIngo Weinhold 	uint8 is_atapi, manual_autosense;
2724535495dSIngo Weinhold 
2734535495dSIngo Weinhold 	SHOW_FLOW0(3, "");
2744535495dSIngo Weinhold 
2754535495dSIngo Weinhold 	if (pnp->get_attr_uint8( node, SCSI_DEVICE_TARGET_ID_ITEM, &target_id, false) != B_OK
2764535495dSIngo Weinhold 		|| pnp->get_attr_uint8( node, SCSI_DEVICE_TARGET_LUN_ITEM, &target_lun, false) != B_OK
2774535495dSIngo Weinhold 		|| pnp->get_attr_uint8( node, SCSI_DEVICE_IS_ATAPI_ITEM, &is_atapi, false) != B_OK
2784535495dSIngo Weinhold 		|| pnp->get_attr_uint8( node, SCSI_DEVICE_MANUAL_AUTOSENSE_ITEM, &manual_autosense, false) != B_OK
2794535495dSIngo Weinhold 		|| pnp->get_attr_raw( node, SCSI_DEVICE_INQUIRY_ITEM,
2804535495dSIngo Weinhold 				(const void **)&inquiry_data, &inquiry_data_len, false) != B_OK
2814535495dSIngo Weinhold 		|| inquiry_data_len != sizeof(*inquiry_data)) {
2824535495dSIngo Weinhold 		return B_ERROR;
2834535495dSIngo Weinhold 	}
2844535495dSIngo Weinhold 
2854535495dSIngo Weinhold 	{
2864535495dSIngo Weinhold 		device_node *parent = pnp->get_parent_node(node);
2874535495dSIngo Weinhold 		pnp->get_driver(parent, NULL, (void **)&bus);
2884535495dSIngo Weinhold 		pnp->put_node(parent);
2894535495dSIngo Weinhold 	}
2904535495dSIngo Weinhold 
2914535495dSIngo Weinhold 	device = scsi_create_device(node, bus, target_id, target_lun);
2924535495dSIngo Weinhold 	if (device == NULL)
2934535495dSIngo Weinhold 		return B_NO_MEMORY;
2944535495dSIngo Weinhold 
2954535495dSIngo Weinhold 	// never mind if there is no path - it might be an emulated controller
2964535495dSIngo Weinhold 	path_id = (uint8)-1;
2974535495dSIngo Weinhold 
2984535495dSIngo Weinhold 	pnp->get_attr_uint8(node, SCSI_BUS_PATH_ID_ITEM, &path_id, true);
2994535495dSIngo Weinhold 
3004535495dSIngo Weinhold 	device->inquiry_data = *inquiry_data;
3014535495dSIngo Weinhold 
3024535495dSIngo Weinhold 	// save restrictions
3034535495dSIngo Weinhold 	device->is_atapi = is_atapi;
3044535495dSIngo Weinhold 	device->manual_autosense = manual_autosense;
3054535495dSIngo Weinhold 
3064535495dSIngo Weinhold 	// size of device queue must be detected by trial and error, so
3074535495dSIngo Weinhold 	// we start with a really high number and see when the device chokes
3084535495dSIngo Weinhold 	device->total_slots = 4096;
3094535495dSIngo Weinhold 
3104535495dSIngo Weinhold 	// disable queuing if bus doesn't support it
3114535495dSIngo Weinhold 	if ((bus->inquiry_data.hba_inquiry & SCSI_PI_TAG_ABLE) == 0)
3124535495dSIngo Weinhold 		device->total_slots = 1;
3134535495dSIngo Weinhold 
3144535495dSIngo Weinhold 	// if there is no autosense, disable queuing to make sure autosense is
3154535495dSIngo Weinhold 	// not overtaken by other requests
3164535495dSIngo Weinhold 	if (device->manual_autosense)
3174535495dSIngo Weinhold 		device->total_slots = 1;
3184535495dSIngo Weinhold 
3194535495dSIngo Weinhold 	device->left_slots = device->total_slots;
3204535495dSIngo Weinhold 
3214535495dSIngo Weinhold 	// get autosense request if required
3224535495dSIngo Weinhold 	if (device->manual_autosense) {
3234535495dSIngo Weinhold 		if (scsi_create_autosense_request(device) != B_OK) {
3244535495dSIngo Weinhold 			res = B_NO_MEMORY;
3254535495dSIngo Weinhold 			goto err;
3264535495dSIngo Weinhold 		}
3274535495dSIngo Weinhold 	}
3284535495dSIngo Weinhold 
3294535495dSIngo Weinhold 	// if this is an ATAPI device, we need an emulation buffer
3304535495dSIngo Weinhold 	if (scsi_init_emulation_buffer(device, SCSI_ATAPI_BUFFER_SIZE) != B_OK) {
3314535495dSIngo Weinhold 		res = B_NO_MEMORY;
3324535495dSIngo Weinhold 		goto err;
3334535495dSIngo Weinhold 	}
3344535495dSIngo Weinhold 
3354535495dSIngo Weinhold 	memset(device->emulation_map, 0, sizeof(device->emulation_map));
3364535495dSIngo Weinhold 
3374535495dSIngo Weinhold 	if (device->is_atapi) {
3384535495dSIngo Weinhold 		SET_BIT(device->emulation_map, SCSI_OP_READ_6);
3394535495dSIngo Weinhold 		SET_BIT(device->emulation_map, SCSI_OP_WRITE_6);
3404535495dSIngo Weinhold 		SET_BIT(device->emulation_map, SCSI_OP_MODE_SENSE_6);
3414535495dSIngo Weinhold 		SET_BIT(device->emulation_map, SCSI_OP_MODE_SELECT_6);
3424535495dSIngo Weinhold 		SET_BIT(device->emulation_map, SCSI_OP_INQUIRY);
3434535495dSIngo Weinhold 	}
3444535495dSIngo Weinhold 
3454535495dSIngo Weinhold 	*cookie = device;
3464535495dSIngo Weinhold 	return B_OK;
3474535495dSIngo Weinhold 
3484535495dSIngo Weinhold err:
3494535495dSIngo Weinhold 	scsi_free_device(device);
3504535495dSIngo Weinhold 	return res;
3514535495dSIngo Weinhold }
3524535495dSIngo Weinhold 
3534535495dSIngo Weinhold 
3544535495dSIngo Weinhold static void
3554535495dSIngo Weinhold scsi_uninit_device(scsi_device_info *device)
3564535495dSIngo Weinhold {
3574535495dSIngo Weinhold 	SHOW_FLOW0(3, "");
3584535495dSIngo Weinhold 
3594535495dSIngo Weinhold 	scsi_free_device(device);
3604535495dSIngo Weinhold }
3614535495dSIngo Weinhold 
3624535495dSIngo Weinhold 
3634535495dSIngo Weinhold static void
3644535495dSIngo Weinhold scsi_device_removed(scsi_device_info *device)
3654535495dSIngo Weinhold {
3664535495dSIngo Weinhold 	SHOW_FLOW0(3, "");
3674535495dSIngo Weinhold 
3684535495dSIngo Weinhold 	if (device == NULL)
3694535495dSIngo Weinhold 		return;
3704535495dSIngo Weinhold 
3714535495dSIngo Weinhold 	// this must be atomic as no lock is used
3724535495dSIngo Weinhold 	device->valid = false;
3734535495dSIngo Weinhold }
3744535495dSIngo Weinhold 
3754535495dSIngo Weinhold 
3764535495dSIngo Weinhold /**	get device info; create a temporary one if it's not registered
3774535495dSIngo Weinhold  *	(used during detection)
3784535495dSIngo Weinhold  *	on success, scan_lun_lock of bus is hold
3794535495dSIngo Weinhold  */
3804535495dSIngo Weinhold 
3814535495dSIngo Weinhold status_t
3824535495dSIngo Weinhold scsi_force_get_device(scsi_bus_info *bus, uchar target_id,
3834535495dSIngo Weinhold 	uchar target_lun, scsi_device_info **res_device)
3844535495dSIngo Weinhold {
3854535495dSIngo Weinhold 	device_attr attrs[] = {
3864535495dSIngo Weinhold 		{ SCSI_DEVICE_TARGET_ID_ITEM, B_UINT8_TYPE, { ui8: target_id }},
3874535495dSIngo Weinhold 		{ SCSI_DEVICE_TARGET_LUN_ITEM, B_UINT8_TYPE, { ui8: target_lun }},
3884535495dSIngo Weinhold 		{ NULL }
3894535495dSIngo Weinhold 	};
3904535495dSIngo Weinhold 	device_node *node;
3914535495dSIngo Weinhold 	status_t res;
3924535495dSIngo Weinhold 	driver_module_info *driver_interface;
3934535495dSIngo Weinhold 	scsi_device device;
3944535495dSIngo Weinhold 
3954535495dSIngo Weinhold 	SHOW_FLOW0(3, "");
3964535495dSIngo Weinhold 
3974535495dSIngo Weinhold 	// very important: only one can use a forced device to avoid double detection
3984535495dSIngo Weinhold 	acquire_sem(bus->scan_lun_lock);
3994535495dSIngo Weinhold 
4004535495dSIngo Weinhold 	// check whether device registered already
4014535495dSIngo Weinhold 	node = NULL;
4024535495dSIngo Weinhold 	pnp->get_next_child_node(bus->node, attrs, &node);
4034535495dSIngo Weinhold 
4044535495dSIngo Weinhold 	SHOW_FLOW(3, "%p", node);
4054535495dSIngo Weinhold 
4064535495dSIngo Weinhold 	if (node != NULL) {
4074535495dSIngo Weinhold 		// TODO: have a second look a this one!
4084535495dSIngo Weinhold 		// there is one - get it
4094535495dSIngo Weinhold 		res = pnp->get_driver(node, &driver_interface, (void **)&device);
4104535495dSIngo Weinhold 		if (res != B_OK)
4114535495dSIngo Weinhold 			pnp->put_node(node);
4124535495dSIngo Weinhold 	} else {
4134535495dSIngo Weinhold 		// device doesn't exist yet - create a temporary one
4144535495dSIngo Weinhold 		device = scsi_create_device(NULL, bus, target_id, target_lun);
4154535495dSIngo Weinhold 		if (device == NULL)
4164535495dSIngo Weinhold 			res = B_NO_MEMORY;
4174535495dSIngo Weinhold 		else
4184535495dSIngo Weinhold 			res = B_OK;
4194535495dSIngo Weinhold 	}
4204535495dSIngo Weinhold 
4214535495dSIngo Weinhold 	*res_device = device;
4224535495dSIngo Weinhold 
4234535495dSIngo Weinhold 	if (res != B_OK)
4244535495dSIngo Weinhold 		release_sem(bus->scan_lun_lock);
4254535495dSIngo Weinhold 
4264535495dSIngo Weinhold 	return res;
4274535495dSIngo Weinhold }
4284535495dSIngo Weinhold 
4294535495dSIngo Weinhold 
4304535495dSIngo Weinhold /**	cleanup device received from scsi_force_get_device
4314535495dSIngo Weinhold  *	on return, scan_lun_lock of bus is released
4324535495dSIngo Weinhold  */
4334535495dSIngo Weinhold 
4344535495dSIngo Weinhold void
4354535495dSIngo Weinhold scsi_put_forced_device(scsi_device_info *device)
4364535495dSIngo Weinhold {
4374535495dSIngo Weinhold 	scsi_bus_info *bus = device->bus;
4384535495dSIngo Weinhold 
4394535495dSIngo Weinhold 	SHOW_FLOW0(3, "");
4404535495dSIngo Weinhold 
4414535495dSIngo Weinhold 	if (device->node != NULL) {
4424535495dSIngo Weinhold 		// device is registered
4434535495dSIngo Weinhold 		pnp->put_node(device->node);
4444535495dSIngo Weinhold 	} else {
4454535495dSIngo Weinhold 		// device is temporary
4464535495dSIngo Weinhold 		scsi_free_device(device);
4474535495dSIngo Weinhold 	}
4484535495dSIngo Weinhold 
4494535495dSIngo Weinhold 	release_sem(bus->scan_lun_lock);
4504535495dSIngo Weinhold }
4514535495dSIngo Weinhold 
4524535495dSIngo Weinhold 
4534535495dSIngo Weinhold static uchar
4544535495dSIngo Weinhold scsi_reset_device(scsi_device_info *device)
4554535495dSIngo Weinhold {
4564535495dSIngo Weinhold 	SHOW_FLOW0(3, "");
4574535495dSIngo Weinhold 
4584535495dSIngo Weinhold 	if (device->node == NULL)
4594535495dSIngo Weinhold 		return SCSI_DEV_NOT_THERE;
4604535495dSIngo Weinhold 
4614535495dSIngo Weinhold 	return device->bus->interface->reset_device(device->bus->sim_cookie,
4624535495dSIngo Weinhold 		device->target_id, device->target_lun);
4634535495dSIngo Weinhold }
4644535495dSIngo Weinhold 
4654535495dSIngo Weinhold 
4664535495dSIngo Weinhold static status_t
4674535495dSIngo Weinhold scsi_ioctl(scsi_device_info *device, uint32 op, void *buffer, size_t length)
4684535495dSIngo Weinhold {
4694535495dSIngo Weinhold 	if (device->bus->interface->ioctl != NULL) {
4704535495dSIngo Weinhold 		return device->bus->interface->ioctl(device->bus->sim_cookie,
4714535495dSIngo Weinhold 			device->target_id, op, buffer, length);
4724535495dSIngo Weinhold 	}
4734535495dSIngo Weinhold 
474*e523d3cfSJérôme Duval 	return B_DEV_INVALID_IOCTL;
4754535495dSIngo Weinhold }
4764535495dSIngo Weinhold 
4774535495dSIngo Weinhold 
4784535495dSIngo Weinhold static status_t
4794535495dSIngo Weinhold std_ops(int32 op, ...)
4804535495dSIngo Weinhold {
4814535495dSIngo Weinhold 	switch (op) {
4824535495dSIngo Weinhold 		case B_MODULE_INIT:
4834535495dSIngo Weinhold 		{
4844535495dSIngo Weinhold 			// Link to SCSI bus.
4854535495dSIngo Weinhold 			// SCSI device driver must have SCSI bus loaded, but it calls its functions
4864535495dSIngo Weinhold 			// directly instead via official interface, so this pointer is never read.
4874535495dSIngo Weinhold 			module_info *dummy;
4884535495dSIngo Weinhold 			return get_module(SCSI_BUS_MODULE_NAME, &dummy);
4894535495dSIngo Weinhold 		}
4904535495dSIngo Weinhold 		case B_MODULE_UNINIT:
4914535495dSIngo Weinhold 			return put_module(SCSI_BUS_MODULE_NAME);
4924535495dSIngo Weinhold 
4934535495dSIngo Weinhold 		default:
4944535495dSIngo Weinhold 			return B_ERROR;
4954535495dSIngo Weinhold 	}
4964535495dSIngo Weinhold }
4974535495dSIngo Weinhold 
4984535495dSIngo Weinhold 
4994535495dSIngo Weinhold scsi_device_interface scsi_device_module = {
5004535495dSIngo Weinhold 	{
5014535495dSIngo Weinhold 		{
5024535495dSIngo Weinhold 			SCSI_DEVICE_MODULE_NAME,
5034535495dSIngo Weinhold 			0,
5044535495dSIngo Weinhold 			std_ops
5054535495dSIngo Weinhold 		},
5064535495dSIngo Weinhold 
5074535495dSIngo Weinhold 		NULL,	// supported devices
5084535495dSIngo Weinhold 		NULL,	// register node
5094535495dSIngo Weinhold 		scsi_init_device,
5104535495dSIngo Weinhold 		(void (*)(void *)) scsi_uninit_device,
5114535495dSIngo Weinhold 		NULL,	// register child devices
5124535495dSIngo Weinhold 		NULL,	// rescan
5134535495dSIngo Weinhold 		(void (*)(void *)) scsi_device_removed
5144535495dSIngo Weinhold 	},
5154535495dSIngo Weinhold 
5164535495dSIngo Weinhold 	scsi_alloc_ccb,
5174535495dSIngo Weinhold 	scsi_free_ccb,
5184535495dSIngo Weinhold 
5194535495dSIngo Weinhold 	scsi_async_io,
5204535495dSIngo Weinhold 	scsi_sync_io,
5214535495dSIngo Weinhold 	scsi_abort,
5224535495dSIngo Weinhold 	scsi_reset_device,
5234535495dSIngo Weinhold 	scsi_term_io,
5244535495dSIngo Weinhold 	scsi_ioctl,
5254535495dSIngo Weinhold };
526