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 /* References: 14 * USB Mass Storage Class specifications: 15 * http://www.usb.org/developers/data/devclass/usbmassover_11.pdf 16 * http://www.usb.org/developers/data/devclass/usbmass-ufi10.pdf 17 * http://www.usb.org/developers/data/devclass/usbmass-cbi10.pdf 18 */ 19 20 #include <string.h> 21 22 #include "usb_scsi.h" 23 24 #include "device_info.h" 25 26 #include "proto_module.h" 27 #include "proto_common.h" 28 #include "proto_cbi.h" 29 30 #include "usb_defs.h" 31 32 #define USB_REQ_CBI_ADSC 0x00 33 34 35 typedef struct _usb_mass_CBI_CB{ 36 uint8 op; 37 uint8 op2; 38 uint8 padding[14]; 39 } usb_mass_CBI_CB; 40 41 #define CDB_LEN 16 42 #define CDB_RESET_LEN 12 43 44 //typedef uint8 usb_mass_CDB[CDB_LEN]; 45 46 typedef union _usb_mass_CBI_IDB{ 47 struct { 48 uint8 type; 49 uint8 value; 50 } common; 51 struct { 52 uint8 asc; 53 uint8 ascq; 54 } ufi; 55 } usb_mass_CBI_IDB; 56 57 #define CBI_IDB_TYPE_CCI 0x00 58 #define CBI_IDB_VALUE_PASS 0x00 59 #define CBI_IDB_VALUE_FAIL 0x01 60 #define CBI_IDB_VALUE_PHASE 0x02 61 #define CBI_IDB_VALUE_PERSISTENT 0x03 62 #define CBI_IDB_VALUE_STATUS_MASK 0x03 63 64 static void trace_CDB(usb_device_info *udi, const usb_mass_CBI_CB *cb, int len); 65 static status_t cbi_reset(usb_device_info *udi); 66 static status_t cbi_initialize(usb_device_info *udi); 67 static void cbi_transfer(usb_device_info *udi, uint8 *cmd, uint8 cmdlen, //sg_buffer *sgb, 68 iovec *sg_data, int32 sg_count, int32 transfer_len, 69 EDirection dir, CCB_SCSIIO *ccbio, ud_transfer_callback cb); 70 71 /** returns size of private protocol buffer */ 72 //int cbi_buffer_length(){ return sizeof(usb_mass_CBI_CB) + sizeof(usb_mass_CBI_IDB); } 73 /** casts private protocol buffer to CBI_IDB */ 74 /*#define IDB_BUFFER(__buff)\ 75 ((usb_mass_CBI_IDB*)(((uint8*)__buff) + sizeof(usb_mass_CBI_CB))) 76 */ 77 /** casts private protocol buffer to CBI_CB */ 78 /*#define CB_BUFFER(__buff)\ 79 ((usb_mass_CBI_CB*)(__buff)) 80 */ 81 void trace_CDB(usb_device_info *udi, const usb_mass_CBI_CB *cb, int len) 82 { 83 PTRACE(udi, "CB:{op:0x%02x; op2:0x%02x;}\n", cb->op, cb->op2); 84 udi->trace_bytes(" padding:", cb->padding, len - 2); 85 } 86 87 static status_t 88 send_request_adsc(usb_device_info *udi, void *cb, int length) 89 { 90 status_t status = B_ERROR; 91 size_t len = 0; 92 trace_CDB(udi, cb, length); 93 status = (*udi->usb_m->send_request)(udi->device, 94 USB_REQTYPE_CLASS | USB_REQTYPE_INTERFACE_OUT, 95 USB_REQ_CBI_ADSC, 0, 96 udi->interface, length, 97 cb, &len); 98 return status; 99 } 100 101 static status_t 102 request_interrupt(usb_device_info *udi, usb_mass_CBI_IDB *idb) 103 { 104 status_t status = B_ERROR; 105 status = (*udi->usb_m->queue_interrupt)(udi->pipe_intr, idb, 106 sizeof(usb_mass_CBI_IDB), bulk_callback, udi); 107 if(status != B_OK){ 108 PTRACE(udi, "request_interrupt:failed:%08x\n", status); 109 } else { 110 status = acquire_sem_etc(udi->trans_sem, 1, B_RELATIVE_TIMEOUT, udi->trans_timeout/*LOCK_TIMEOUT*/); 111 if(status != B_OK){ 112 PTRACE(udi, "request_interrupt:acquire_sem_etc failed:%08x\n", status); 113 (*udi->usb_m->cancel_queued_transfers)(udi->pipe_intr); 114 } 115 } 116 udi->trace_bytes("Intr status:", (uint8*)idb, sizeof(usb_mass_CBI_IDB)); 117 return status; 118 } 119 120 static status_t 121 parse_status(usb_device_info *udi, usb_mass_CBI_IDB *idb) 122 { 123 status_t command_status = B_OK; 124 if(CMDSET(udi->properties) == CMDSET_UFI){ 125 if(idb->ufi.asc == 0 && idb->ufi.ascq == 0){ 126 command_status = B_OK; 127 }else{ 128 command_status = B_CMD_FAILED; 129 } 130 } else { 131 if(idb->common.type == CBI_IDB_TYPE_CCI){ 132 switch(idb->common.value & CBI_IDB_VALUE_STATUS_MASK){ 133 case CBI_IDB_VALUE_PASS: 134 command_status = B_OK; 135 break; 136 case CBI_IDB_VALUE_FAIL: 137 case CBI_IDB_VALUE_PERSISTENT: 138 command_status = B_CMD_FAILED; 139 break; 140 case CBI_IDB_VALUE_PHASE: 141 default: 142 command_status = B_CMD_WIRE_FAILED; 143 break; 144 } 145 } /* else ?? */ 146 } 147 return command_status; 148 } 149 150 /*================= "standard" protocol procedures ==============================*/ 151 152 /** 153 \fn: 154 \param udi: ??? 155 \return:?? 156 157 ?? 158 */ 159 status_t 160 cbi_reset(usb_device_info *udi) 161 { 162 status_t status = B_ERROR; 163 //usb_mass_CBI_CB *cb = CB_BUFFER(udi->proto_buf); 164 //usb_mass_CDB cdb = {0x1d, 0x04, 0x00}; 165 //memset(cdb + 2, 0xff, CDB_RESET_LEN - 2); 166 usb_mass_CBI_CB cb = { 167 .op = 0x1D, 168 .op2 = 0x04, 169 }; 170 memset(cb.padding , 0xff, sizeof(cb.padding)); 171 status = send_request_adsc(udi, &cb, CDB_RESET_LEN); 172 if(status != B_OK) 173 PTRACE_ALWAYS(udi, "command_block_reset: reset request failed: %08x\n", status); 174 175 if(B_OK != (status = (*udi->usb_m->clear_feature)(udi->pipe_in, 176 USB_FEATURE_ENDPOINT_HALT))) 177 PTRACE_ALWAYS(udi, "command_block_reset: clear_feature on pipe_in failed: %08x\n", status); 178 179 if(B_OK != (status = (*udi->usb_m->clear_feature)(udi->pipe_out, 180 USB_FEATURE_ENDPOINT_HALT))) 181 PTRACE_ALWAYS(udi, "command_block_reset: clear_feature on pipe_out failed: %08x\n", status); 182 PTRACE(udi, "command_block_reset:%08x\n", status); 183 184 return status; 185 } 186 187 /** 188 \fn: 189 */ 190 status_t 191 cbi_initialize(usb_device_info *udi) 192 { 193 status_t status = B_OK; 194 udi->max_lun = 0; 195 return status; 196 } 197 198 /** 199 \fn:bulk_only_transfer 200 \param udi: ??? 201 \param cmd: ??? 202 \param cmdlen: ??? 203 \return:??? 204 205 ??? 206 */ 207 void 208 cbi_transfer(usb_device_info *udi, uint8 *cmd, uint8 cmdlen, 209 //sg_buffer *sgb, 210 iovec *sg_data, int32 sg_count, int32 transfer_len, 211 EDirection dir,CCB_SCSIIO *ccbio, ud_transfer_callback cb) 212 { 213 status_t status = B_OK; 214 status_t command_status = B_OK; 215 int32 residue = transfer_len; 216 usb_mass_CBI_IDB idb = {{0}}; 217 do{ 218 status = send_request_adsc(udi, cmd, cmdlen); 219 if(status != B_OK){ 220 PTRACE(udi, "cbi_transfer:send command block failed: %08x\n", status); 221 if(status == B_DEV_STALLED){ 222 command_status = B_CMD_FAILED; 223 } else { 224 command_status = B_CMD_WIRE_FAILED; 225 (*udi->protocol_m->reset)(udi); 226 } 227 break; 228 } 229 if(transfer_len != 0){ 230 status = process_data_io(udi, sg_data, sg_count, dir); 231 if(status != B_OK){ 232 command_status = B_CMD_WIRE_FAILED; 233 break; 234 } 235 } 236 237 if(PROTO(udi->properties) == PROTO_CBI){ 238 status = request_interrupt(udi, &idb); 239 PTRACE(udi, "cbi_transfer:request interrupt: %08x(%08x)\n", status, udi->status); 240 if(status != B_OK){ 241 command_status = B_CMD_WIRE_FAILED; 242 break; 243 } 244 if(udi->status == B_DEV_STALLED){ 245 } 246 command_status = parse_status(udi, &idb); 247 } else { /* PROP_CB ???*/ 248 command_status = B_CMD_UNKNOWN; 249 } 250 residue = 0; /* DEBUG!!!!!!!!!*/ 251 }while(false); 252 cb(udi, ccbio, residue, command_status); 253 } 254 255 protocol_module_info cbi_protocol_m = { 256 {0, 0, 0}, /* this is not a real kernel module - just interface */ 257 cbi_initialize, 258 cbi_reset, 259 cbi_transfer, 260 }; 261 262