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
scsi_free_autosense_request(scsi_device_info * device)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
scsi_free_device(scsi_device_info * device)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
58b4f4368bSAugustin Cavalier mutex_destroy(&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
beautify_string(char * dst,char * src,int dst_size)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
scsi_register_device(scsi_bus_info * bus,uchar target_id,uchar target_lun,scsi_res_inquiry * inquiry_data)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
117*215b685fSX512 { SCSI_DEVICE_TARGET_ID_ITEM, B_UINT8_TYPE, { .ui8 = target_id }},
118*215b685fSX512 { 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,
122*215b685fSX512 { .raw = { inquiry_data, sizeof( *inquiry_data ) }}},
1234535495dSIngo Weinhold
1244535495dSIngo Weinhold // some more info for driver loading
125*215b685fSX512 { SCSI_DEVICE_TYPE_ITEM, B_UINT8_TYPE, { .ui8 = inquiry_data->device_type }},
126*215b685fSX512 { SCSI_DEVICE_VENDOR_ITEM, B_STRING_TYPE, { .string = vendor_ident }},
127*215b685fSX512 { SCSI_DEVICE_PRODUCT_ITEM, B_STRING_TYPE, { .string = product_ident }},
128*215b685fSX512 { SCSI_DEVICE_REVISION_ITEM, B_STRING_TYPE, { .string = product_rev }},
1294535495dSIngo Weinhold
1304535495dSIngo Weinhold // description of peripheral drivers
131*215b685fSX512 { B_DEVICE_BUS, B_STRING_TYPE, { .string = "scsi" }},
1324535495dSIngo Weinhold
1334535495dSIngo Weinhold // extra restriction of maximum number of blocks per transfer
134*215b685fSX512 { B_DMA_MAX_TRANSFER_BLOCKS, B_UINT32_TYPE, { .ui32 = max_blocks }},
1354535495dSIngo Weinhold
1364535495dSIngo Weinhold // atapi emulation
137*215b685fSX512 { SCSI_DEVICE_IS_ATAPI_ITEM, B_UINT8_TYPE, { .ui8 = is_atapi }},
1384535495dSIngo Weinhold // manual autosense
139*215b685fSX512 { 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 *
scsi_create_device(device_node * node,scsi_bus_info * bus,int target_id,int target_lun)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
181b4f4368bSAugustin Cavalier mutex_init(&device->dma_buffer_lock, "dma_buffer");
1824535495dSIngo Weinhold
1834535495dSIngo Weinhold device->dma_buffer_owner = create_sem(1, "dma_buffer");
1844535495dSIngo Weinhold if (device->dma_buffer_owner < 0)
185b4f4368bSAugustin Cavalier goto err;
1864535495dSIngo Weinhold
1874535495dSIngo Weinhold register_kernel_daemon(scsi_dma_buffer_daemon, device, 5 * 10);
1884535495dSIngo Weinhold
1894535495dSIngo Weinhold return device;
1904535495dSIngo Weinhold
1914535495dSIngo Weinhold err:
192b4f4368bSAugustin Cavalier mutex_destroy(&device->dma_buffer_lock);
1934535495dSIngo Weinhold free(device);
1944535495dSIngo Weinhold return NULL;
1954535495dSIngo Weinhold }
1964535495dSIngo Weinhold
1974535495dSIngo Weinhold
1984535495dSIngo Weinhold /** prepare autosense request.
1994535495dSIngo Weinhold * this cannot be done on demand but during init as we may
2004535495dSIngo Weinhold * have run out of ccbs when we need it
2014535495dSIngo Weinhold */
2024535495dSIngo Weinhold
2034535495dSIngo Weinhold static status_t
scsi_create_autosense_request(scsi_device_info * device)2044535495dSIngo Weinhold scsi_create_autosense_request(scsi_device_info *device)
2054535495dSIngo Weinhold {
2064535495dSIngo Weinhold scsi_ccb *request;
2074535495dSIngo Weinhold unsigned char *buffer;
2084535495dSIngo Weinhold scsi_cmd_request_sense *cmd;
2094535495dSIngo Weinhold size_t total_size;
2104535495dSIngo Weinhold
2114535495dSIngo Weinhold SHOW_FLOW0( 3, "" );
2124535495dSIngo Weinhold
2134535495dSIngo Weinhold device->auto_sense_request = request = scsi_alloc_ccb(device);
2144535495dSIngo Weinhold if (device->auto_sense_request == NULL)
2154535495dSIngo Weinhold return B_NO_MEMORY;
2164535495dSIngo Weinhold
2174535495dSIngo Weinhold total_size = SCSI_MAX_SENSE_SIZE + sizeof(physical_entry);
2184535495dSIngo Weinhold total_size = (total_size + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
2194535495dSIngo Weinhold
2204535495dSIngo Weinhold // allocate buffer for space sense data and S/G list
2214535495dSIngo Weinhold device->auto_sense_area = create_area("auto_sense", (void**)&buffer,
222956f4507STrung Nguyen B_ANY_KERNEL_ADDRESS, B_PAGE_SIZE, B_32_BIT_FULL_LOCK,
223956f4507STrung Nguyen B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
2244535495dSIngo Weinhold // TODO: Use B_FULL_LOCK, if addresses >= 4 GB are supported!
2254535495dSIngo Weinhold if (device->auto_sense_area < 0)
2264535495dSIngo Weinhold goto err;
2274535495dSIngo Weinhold
2284535495dSIngo Weinhold request->data = buffer;
2294535495dSIngo Weinhold request->data_length = SCSI_MAX_SENSE_SIZE;
2304535495dSIngo Weinhold request->sg_list = (physical_entry *)(buffer + SCSI_MAX_SENSE_SIZE);
2314535495dSIngo Weinhold request->sg_count = 1;
2324535495dSIngo Weinhold
2334535495dSIngo Weinhold get_memory_map(buffer, SCSI_MAX_SENSE_SIZE,
2344535495dSIngo Weinhold (physical_entry *)request->sg_list, 1);
2354535495dSIngo Weinhold
2364535495dSIngo Weinhold // disable auto-autosense, just in case;
2374535495dSIngo Weinhold // make sure no other request overtakes sense request;
2384535495dSIngo Weinhold // buffer is/must be DMA safe as we cannot risk trouble with
2394535495dSIngo Weinhold // dynamically allocated DMA buffer
2404535495dSIngo Weinhold request->flags = SCSI_DIR_IN | SCSI_DIS_AUTOSENSE |
2414535495dSIngo Weinhold SCSI_ORDERED_QTAG | SCSI_DMA_SAFE;
2424535495dSIngo Weinhold
2434535495dSIngo Weinhold cmd = (scsi_cmd_request_sense *)request->cdb;
2444535495dSIngo Weinhold request->cdb_length = sizeof(*cmd);
2454535495dSIngo Weinhold
2464535495dSIngo Weinhold memset(cmd, 0, sizeof(*cmd));
2474535495dSIngo Weinhold cmd->opcode = SCSI_OP_REQUEST_SENSE;
2484535495dSIngo Weinhold cmd->lun = device->target_lun;
2494535495dSIngo Weinhold cmd->allocation_length = SCSI_MAX_SENSE_SIZE;
2504535495dSIngo Weinhold
2514535495dSIngo Weinhold return B_OK;
2524535495dSIngo Weinhold
2534535495dSIngo Weinhold err:
2544535495dSIngo Weinhold scsi_free_ccb(request);
2554535495dSIngo Weinhold return B_NO_MEMORY;
2564535495dSIngo Weinhold }
2574535495dSIngo Weinhold
2584535495dSIngo Weinhold
2594535495dSIngo Weinhold #define SET_BIT(field, bit) field[(bit) >> 3] |= 1 << ((bit) & 7)
2604535495dSIngo Weinhold
2614535495dSIngo Weinhold static status_t
scsi_init_device(device_node * node,void ** cookie)2624535495dSIngo Weinhold scsi_init_device(device_node *node, void **cookie)
2634535495dSIngo Weinhold {
2644535495dSIngo Weinhold const scsi_res_inquiry *inquiry_data = NULL;
2654535495dSIngo Weinhold uint8 target_id, target_lun, path_id;
2664535495dSIngo Weinhold scsi_bus_info *bus;
2674535495dSIngo Weinhold scsi_device_info *device;
2684535495dSIngo Weinhold status_t res;
2694535495dSIngo Weinhold size_t inquiry_data_len;
2704535495dSIngo Weinhold uint8 is_atapi, manual_autosense;
2714535495dSIngo Weinhold
2724535495dSIngo Weinhold SHOW_FLOW0(3, "");
2734535495dSIngo Weinhold
2744535495dSIngo Weinhold if (pnp->get_attr_uint8( node, SCSI_DEVICE_TARGET_ID_ITEM, &target_id, false) != B_OK
2754535495dSIngo Weinhold || pnp->get_attr_uint8( node, SCSI_DEVICE_TARGET_LUN_ITEM, &target_lun, false) != B_OK
2764535495dSIngo Weinhold || pnp->get_attr_uint8( node, SCSI_DEVICE_IS_ATAPI_ITEM, &is_atapi, false) != B_OK
2774535495dSIngo Weinhold || pnp->get_attr_uint8( node, SCSI_DEVICE_MANUAL_AUTOSENSE_ITEM, &manual_autosense, false) != B_OK
2784535495dSIngo Weinhold || pnp->get_attr_raw( node, SCSI_DEVICE_INQUIRY_ITEM,
2794535495dSIngo Weinhold (const void **)&inquiry_data, &inquiry_data_len, false) != B_OK
2804535495dSIngo Weinhold || inquiry_data_len != sizeof(*inquiry_data)) {
2814535495dSIngo Weinhold return B_ERROR;
2824535495dSIngo Weinhold }
2834535495dSIngo Weinhold
2844535495dSIngo Weinhold {
2854535495dSIngo Weinhold device_node *parent = pnp->get_parent_node(node);
2864535495dSIngo Weinhold pnp->get_driver(parent, NULL, (void **)&bus);
2874535495dSIngo Weinhold pnp->put_node(parent);
2884535495dSIngo Weinhold }
2894535495dSIngo Weinhold
2904535495dSIngo Weinhold device = scsi_create_device(node, bus, target_id, target_lun);
2914535495dSIngo Weinhold if (device == NULL)
2924535495dSIngo Weinhold return B_NO_MEMORY;
2934535495dSIngo Weinhold
2944535495dSIngo Weinhold // never mind if there is no path - it might be an emulated controller
2954535495dSIngo Weinhold path_id = (uint8)-1;
2964535495dSIngo Weinhold
2974535495dSIngo Weinhold pnp->get_attr_uint8(node, SCSI_BUS_PATH_ID_ITEM, &path_id, true);
2984535495dSIngo Weinhold
2994535495dSIngo Weinhold device->inquiry_data = *inquiry_data;
3004535495dSIngo Weinhold
3014535495dSIngo Weinhold // save restrictions
3024535495dSIngo Weinhold device->is_atapi = is_atapi;
3034535495dSIngo Weinhold device->manual_autosense = manual_autosense;
3044535495dSIngo Weinhold
3054535495dSIngo Weinhold // size of device queue must be detected by trial and error, so
3064535495dSIngo Weinhold // we start with a really high number and see when the device chokes
3074535495dSIngo Weinhold device->total_slots = 4096;
3084535495dSIngo Weinhold
3094535495dSIngo Weinhold // disable queuing if bus doesn't support it
3104535495dSIngo Weinhold if ((bus->inquiry_data.hba_inquiry & SCSI_PI_TAG_ABLE) == 0)
3114535495dSIngo Weinhold device->total_slots = 1;
3124535495dSIngo Weinhold
3134535495dSIngo Weinhold // if there is no autosense, disable queuing to make sure autosense is
3144535495dSIngo Weinhold // not overtaken by other requests
3154535495dSIngo Weinhold if (device->manual_autosense)
3164535495dSIngo Weinhold device->total_slots = 1;
3174535495dSIngo Weinhold
3184535495dSIngo Weinhold device->left_slots = device->total_slots;
3194535495dSIngo Weinhold
3204535495dSIngo Weinhold // get autosense request if required
3214535495dSIngo Weinhold if (device->manual_autosense) {
3224535495dSIngo Weinhold if (scsi_create_autosense_request(device) != B_OK) {
3234535495dSIngo Weinhold res = B_NO_MEMORY;
3244535495dSIngo Weinhold goto err;
3254535495dSIngo Weinhold }
3264535495dSIngo Weinhold }
3274535495dSIngo Weinhold
3284535495dSIngo Weinhold // if this is an ATAPI device, we need an emulation buffer
3294535495dSIngo Weinhold if (scsi_init_emulation_buffer(device, SCSI_ATAPI_BUFFER_SIZE) != B_OK) {
3304535495dSIngo Weinhold res = B_NO_MEMORY;
3314535495dSIngo Weinhold goto err;
3324535495dSIngo Weinhold }
3334535495dSIngo Weinhold
3344535495dSIngo Weinhold memset(device->emulation_map, 0, sizeof(device->emulation_map));
3354535495dSIngo Weinhold
3364535495dSIngo Weinhold if (device->is_atapi) {
3374535495dSIngo Weinhold SET_BIT(device->emulation_map, SCSI_OP_READ_6);
3384535495dSIngo Weinhold SET_BIT(device->emulation_map, SCSI_OP_WRITE_6);
3394535495dSIngo Weinhold SET_BIT(device->emulation_map, SCSI_OP_MODE_SENSE_6);
3404535495dSIngo Weinhold SET_BIT(device->emulation_map, SCSI_OP_MODE_SELECT_6);
3414535495dSIngo Weinhold SET_BIT(device->emulation_map, SCSI_OP_INQUIRY);
3424535495dSIngo Weinhold }
3434535495dSIngo Weinhold
3444535495dSIngo Weinhold *cookie = device;
3454535495dSIngo Weinhold return B_OK;
3464535495dSIngo Weinhold
3474535495dSIngo Weinhold err:
3484535495dSIngo Weinhold scsi_free_device(device);
3494535495dSIngo Weinhold return res;
3504535495dSIngo Weinhold }
3514535495dSIngo Weinhold
3524535495dSIngo Weinhold
3534535495dSIngo Weinhold static void
scsi_uninit_device(scsi_device_info * device)3544535495dSIngo Weinhold scsi_uninit_device(scsi_device_info *device)
3554535495dSIngo Weinhold {
3564535495dSIngo Weinhold SHOW_FLOW0(3, "");
3574535495dSIngo Weinhold
3584535495dSIngo Weinhold scsi_free_device(device);
3594535495dSIngo Weinhold }
3604535495dSIngo Weinhold
3614535495dSIngo Weinhold
3624535495dSIngo Weinhold static void
scsi_device_removed(scsi_device_info * device)3634535495dSIngo Weinhold scsi_device_removed(scsi_device_info *device)
3644535495dSIngo Weinhold {
3654535495dSIngo Weinhold SHOW_FLOW0(3, "");
3664535495dSIngo Weinhold
3674535495dSIngo Weinhold if (device == NULL)
3684535495dSIngo Weinhold return;
3694535495dSIngo Weinhold
3704535495dSIngo Weinhold // this must be atomic as no lock is used
3714535495dSIngo Weinhold device->valid = false;
3724535495dSIngo Weinhold }
3734535495dSIngo Weinhold
3744535495dSIngo Weinhold
3754535495dSIngo Weinhold /** get device info; create a temporary one if it's not registered
3764535495dSIngo Weinhold * (used during detection)
3774535495dSIngo Weinhold * on success, scan_lun_lock of bus is hold
3784535495dSIngo Weinhold */
3794535495dSIngo Weinhold
3804535495dSIngo Weinhold status_t
scsi_force_get_device(scsi_bus_info * bus,uchar target_id,uchar target_lun,scsi_device_info ** res_device)3814535495dSIngo Weinhold scsi_force_get_device(scsi_bus_info *bus, uchar target_id,
3824535495dSIngo Weinhold uchar target_lun, scsi_device_info **res_device)
3834535495dSIngo Weinhold {
3844535495dSIngo Weinhold device_attr attrs[] = {
385*215b685fSX512 { SCSI_DEVICE_TARGET_ID_ITEM, B_UINT8_TYPE, { .ui8 = target_id }},
386*215b685fSX512 { SCSI_DEVICE_TARGET_LUN_ITEM, B_UINT8_TYPE, { .ui8 = target_lun }},
3874535495dSIngo Weinhold { NULL }
3884535495dSIngo Weinhold };
3894535495dSIngo Weinhold device_node *node;
3904535495dSIngo Weinhold status_t res;
3914535495dSIngo Weinhold driver_module_info *driver_interface;
3924535495dSIngo Weinhold scsi_device device;
3934535495dSIngo Weinhold
3944535495dSIngo Weinhold SHOW_FLOW0(3, "");
3954535495dSIngo Weinhold
3964535495dSIngo Weinhold // very important: only one can use a forced device to avoid double detection
3974535495dSIngo Weinhold acquire_sem(bus->scan_lun_lock);
3984535495dSIngo Weinhold
3994535495dSIngo Weinhold // check whether device registered already
4004535495dSIngo Weinhold node = NULL;
4014535495dSIngo Weinhold pnp->get_next_child_node(bus->node, attrs, &node);
4024535495dSIngo Weinhold
4034535495dSIngo Weinhold SHOW_FLOW(3, "%p", node);
4044535495dSIngo Weinhold
4054535495dSIngo Weinhold if (node != NULL) {
4064535495dSIngo Weinhold // TODO: have a second look a this one!
4074535495dSIngo Weinhold // there is one - get it
4084535495dSIngo Weinhold res = pnp->get_driver(node, &driver_interface, (void **)&device);
4094535495dSIngo Weinhold if (res != B_OK)
4104535495dSIngo Weinhold pnp->put_node(node);
4114535495dSIngo Weinhold } else {
4124535495dSIngo Weinhold // device doesn't exist yet - create a temporary one
4134535495dSIngo Weinhold device = scsi_create_device(NULL, bus, target_id, target_lun);
4144535495dSIngo Weinhold if (device == NULL)
4154535495dSIngo Weinhold res = B_NO_MEMORY;
4164535495dSIngo Weinhold else
4174535495dSIngo Weinhold res = B_OK;
4184535495dSIngo Weinhold }
4194535495dSIngo Weinhold
4204535495dSIngo Weinhold *res_device = device;
4214535495dSIngo Weinhold
4224535495dSIngo Weinhold if (res != B_OK)
4234535495dSIngo Weinhold release_sem(bus->scan_lun_lock);
4244535495dSIngo Weinhold
4254535495dSIngo Weinhold return res;
4264535495dSIngo Weinhold }
4274535495dSIngo Weinhold
4284535495dSIngo Weinhold
4294535495dSIngo Weinhold /** cleanup device received from scsi_force_get_device
4304535495dSIngo Weinhold * on return, scan_lun_lock of bus is released
4314535495dSIngo Weinhold */
4324535495dSIngo Weinhold
4334535495dSIngo Weinhold void
scsi_put_forced_device(scsi_device_info * device)4344535495dSIngo Weinhold scsi_put_forced_device(scsi_device_info *device)
4354535495dSIngo Weinhold {
4364535495dSIngo Weinhold scsi_bus_info *bus = device->bus;
4374535495dSIngo Weinhold
4384535495dSIngo Weinhold SHOW_FLOW0(3, "");
4394535495dSIngo Weinhold
4404535495dSIngo Weinhold if (device->node != NULL) {
4414535495dSIngo Weinhold // device is registered
4424535495dSIngo Weinhold pnp->put_node(device->node);
4434535495dSIngo Weinhold } else {
4444535495dSIngo Weinhold // device is temporary
4454535495dSIngo Weinhold scsi_free_device(device);
4464535495dSIngo Weinhold }
4474535495dSIngo Weinhold
4484535495dSIngo Weinhold release_sem(bus->scan_lun_lock);
4494535495dSIngo Weinhold }
4504535495dSIngo Weinhold
4514535495dSIngo Weinhold
4524535495dSIngo Weinhold static uchar
scsi_reset_device(scsi_device_info * device)4534535495dSIngo Weinhold scsi_reset_device(scsi_device_info *device)
4544535495dSIngo Weinhold {
4554535495dSIngo Weinhold SHOW_FLOW0(3, "");
4564535495dSIngo Weinhold
4574535495dSIngo Weinhold if (device->node == NULL)
4584535495dSIngo Weinhold return SCSI_DEV_NOT_THERE;
4594535495dSIngo Weinhold
4604535495dSIngo Weinhold return device->bus->interface->reset_device(device->bus->sim_cookie,
4614535495dSIngo Weinhold device->target_id, device->target_lun);
4624535495dSIngo Weinhold }
4634535495dSIngo Weinhold
4644535495dSIngo Weinhold
4654535495dSIngo Weinhold static status_t
scsi_ioctl(scsi_device_info * device,uint32 op,void * buffer,size_t length)4664535495dSIngo Weinhold scsi_ioctl(scsi_device_info *device, uint32 op, void *buffer, size_t length)
4674535495dSIngo Weinhold {
4684535495dSIngo Weinhold if (device->bus->interface->ioctl != NULL) {
4694535495dSIngo Weinhold return device->bus->interface->ioctl(device->bus->sim_cookie,
4704535495dSIngo Weinhold device->target_id, op, buffer, length);
4714535495dSIngo Weinhold }
4724535495dSIngo Weinhold
473e523d3cfSJérôme Duval return B_DEV_INVALID_IOCTL;
4744535495dSIngo Weinhold }
4754535495dSIngo Weinhold
4764535495dSIngo Weinhold
4774535495dSIngo Weinhold static status_t
std_ops(int32 op,...)4784535495dSIngo Weinhold std_ops(int32 op, ...)
4794535495dSIngo Weinhold {
4804535495dSIngo Weinhold switch (op) {
4814535495dSIngo Weinhold case B_MODULE_INIT:
4824535495dSIngo Weinhold {
4834535495dSIngo Weinhold // Link to SCSI bus.
4844535495dSIngo Weinhold // SCSI device driver must have SCSI bus loaded, but it calls its functions
4854535495dSIngo Weinhold // directly instead via official interface, so this pointer is never read.
4864535495dSIngo Weinhold module_info *dummy;
4874535495dSIngo Weinhold return get_module(SCSI_BUS_MODULE_NAME, &dummy);
4884535495dSIngo Weinhold }
4894535495dSIngo Weinhold case B_MODULE_UNINIT:
4904535495dSIngo Weinhold return put_module(SCSI_BUS_MODULE_NAME);
4914535495dSIngo Weinhold
4924535495dSIngo Weinhold default:
4934535495dSIngo Weinhold return B_ERROR;
4944535495dSIngo Weinhold }
4954535495dSIngo Weinhold }
4964535495dSIngo Weinhold
4974535495dSIngo Weinhold
4984535495dSIngo Weinhold scsi_device_interface scsi_device_module = {
4994535495dSIngo Weinhold {
5004535495dSIngo Weinhold {
5014535495dSIngo Weinhold SCSI_DEVICE_MODULE_NAME,
5024535495dSIngo Weinhold 0,
5034535495dSIngo Weinhold std_ops
5044535495dSIngo Weinhold },
5054535495dSIngo Weinhold
5064535495dSIngo Weinhold NULL, // supported devices
5074535495dSIngo Weinhold NULL, // register node
5084535495dSIngo Weinhold scsi_init_device,
5094535495dSIngo Weinhold (void (*)(void *)) scsi_uninit_device,
5104535495dSIngo Weinhold NULL, // register child devices
5114535495dSIngo Weinhold NULL, // rescan
5124535495dSIngo Weinhold (void (*)(void *)) scsi_device_removed
5134535495dSIngo Weinhold },
5144535495dSIngo Weinhold
5154535495dSIngo Weinhold scsi_alloc_ccb,
5164535495dSIngo Weinhold scsi_free_ccb,
5174535495dSIngo Weinhold
5184535495dSIngo Weinhold scsi_async_io,
5194535495dSIngo Weinhold scsi_sync_io,
5204535495dSIngo Weinhold scsi_abort,
5214535495dSIngo Weinhold scsi_reset_device,
5224535495dSIngo Weinhold scsi_term_io,
5234535495dSIngo Weinhold scsi_ioctl,
5244535495dSIngo Weinhold };
525