1 /* 2 * \file: freecom.c 3 * \brief: USB SCSI module extention for Freecom USB/IDE adaptor 4 * 5 * This file is a part of BeOS USB SCSI interface module project. 6 * Copyright (c) 2003-2004 by Siarzhuk Zharski <imker@gmx.li> 7 * 8 * This program is free software; you can redistribute it and/or modify it 9 * under the terms of the GNU General Public License as published by the 10 * Free Software Foundation; either version 2, or (at your option) any 11 * later version. 12 * 13 * This program is distributed in the hope that it will be useful, but 14 * WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License along 19 * with this program; if not, write to the Free Software Foundation, Inc., 20 * 675 Mass Ave, Cambridge, MA 02139, USA. 21 * 22 * This protocol extension module was developed using information from 23 * "Driver for Freecom USB/IDE adaptor" in Linux usb storage driver and 24 * "Mac OS X driver for the Freecom Traveller CD writer". 25 * 26 * $Source: /cvsroot/sis4be/usb_scsi/freecom/freecom.c,v $ 27 * $Author: zharik $ 28 * $Revision: 1.11 $ 29 * $Date: 2005/04/01 22:39:23 $ 30 * 31 */ 32 #include "usb_scsi.h" 33 34 #include <malloc.h> 35 #include "device_info.h" 36 #include "proto_module.h" 37 38 #define FREECOM_MODULE_NAME "freecom" 39 #define FREECOM_PROTOCOL_MODULE_NAME \ 40 MODULE_PREFIX FREECOM_MODULE_NAME PROTOCOL_SUFFIX 41 42 /** 43 \struct:fcm_command 44 */ 45 typedef struct { 46 uint8 type; /* block type. */ 47 #define FCMT_ATAPI 0x21 /* send command. low bit indicates waiting for interrupt */ 48 #define FCMT_STATUS 0x20 /* request for status */ 49 #define FCMT_IN 0x81 /* request read data transfer. */ 50 #define FCMT_OUT 0x01 /* request write data transfer */ 51 #define FCMT_REG_IN 0xC0 /* read value from IDE regsiter */ 52 #define FCMT_REG_OUT 0x40 /* write value to IDE register */ 53 /* to use FCMT_REG_ combine it with register */ 54 #define FCMR_DATA 0x10 /* data register */ 55 /* ... */ 56 #define FCMR_CMD 0x16 /* status-command register */ 57 #define FCMR_INT 0x17 /* interrupt-error register */ 58 uint8 timeout; /* timeout (seconds?) */ 59 #define FCMTO_DEF 0xfe /* default timeout */ 60 #define FCMTO_TU 0x14 /* timeout for TEST UNIT READY command */ 61 union{ 62 uint8 cmd[12]; /* An ATAPI command. */ 63 uint32 count; /* number of bytes to transfer. */ 64 uint16 reg_val; /* Value to write to IDE register. */ 65 uint8 pad[62]; /* Padding Data. All FREECOM commands are 64 bytes long */ 66 }_PACKED data; 67 }_PACKED fcm_command; 68 69 /** 70 \struct:fcm_status 71 */ 72 typedef struct { 73 uint8 status; 74 #define FCMS_BSY 0x80 /* device is busy */ 75 #define FCMS_DRDY 0x40 /* */ 76 #define FCMS_DMA 0x20 /* */ 77 #define FCMS_SEEK 0x10 /* */ 78 #define FCMS_DRQ 0x08 /* */ 79 #define FCMS_CORR 0x04 /* */ 80 #define FCMS_INTR 0x02 /*FREECOM specific: use obsolete bit*/ 81 #define FCMS_CHECK 0x01 /* device is in error condition */ 82 uint8 reason; 83 #define FCMR_REL 0x04 /* */ 84 #define FCMR_IO 0x02 /* */ 85 #define FCMR_COD 0x01 /* */ 86 uint16 count; 87 }_PACKED fcm_status; 88 89 // Timeout value (in us) for the freecom packet USB BulkRead and BulkWrite functions 90 #define FREECOM_USB_TIMEOUT 30000000 91 92 /** 93 \fn: 94 \param udi: 95 \param st: 96 \return: 97 98 */ 99 void trace_status(usb_device_info *udi, const fcm_status *st) 100 { 101 char ch_status[] = "BDFSRCIE"; 102 char ch_reason[] = ".....RIC"; 103 int i = 0; 104 for(; i < 8; i++){ 105 if(!(st->status & (1 << i))) 106 ch_status[7 - i] = '.'; 107 if(!(st->reason & (1 << i))) 108 ch_reason[7 - i] = '.'; 109 } 110 PTRACE(udi, "FCM:Status:{%s; Reason:%s; Count:%d}\n", 111 ch_status, ch_reason, st->count); 112 } 113 114 /** 115 \fn:freecom_initialize 116 \param udi: device on wich we should perform initialization 117 \return:error code if initialization failed or B_OK if it passed 118 119 initialize procedure. 120 */ 121 status_t 122 freecom_initialize(usb_device_info *udi) 123 { 124 status_t status = B_OK; 125 #define INIT_BUFFER_SIZE 0x20 126 char buffer[INIT_BUFFER_SIZE + 1]; 127 size_t len = 0; 128 129 if(B_OK != (status = (*udi->usb_m->send_request)(udi->device, 130 USB_REQTYPE_DEVICE_IN | USB_REQTYPE_VENDOR, 131 0x4c, 0x4346, 0x0, INIT_BUFFER_SIZE, 132 buffer, &len))) 133 { 134 PTRACE_ALWAYS(udi, "FCM:init[%d]: init failed: %08x\n", udi->dev_num, status); 135 } else { 136 buffer[len] = 0; 137 PTRACE(udi, "FCM:init[%d]: init '%s' OK\n", udi->dev_num, buffer); 138 } 139 140 if(B_OK != (status = (*udi->usb_m->send_request)(udi->device, 141 USB_REQTYPE_DEVICE_OUT | USB_REQTYPE_VENDOR, 142 0x4d, 0x24d8, 0x0, 0, 0, &len))) 143 { 144 PTRACE_ALWAYS(udi, "FCM:init[%d]: reset on failed:%08x\n", udi->dev_num, status); 145 } 146 147 snooze(250000); 148 149 if(B_OK != (status = (*udi->usb_m->send_request)(udi->device, 150 USB_REQTYPE_DEVICE_OUT | USB_REQTYPE_VENDOR, 151 0x4d, 0x24f8, 0x0, 0, 0, &len))) 152 { 153 PTRACE_ALWAYS(udi, "FCM:init[%d]: reset off failed:%08x\n", udi->dev_num, status); 154 } 155 156 snooze(3 * 1000000); 157 158 return status; 159 } 160 /** 161 \fn:freecom_reset 162 \param udi: device on wich we should perform reset 163 \return:error code if reset failed or B_OK if it passed 164 165 reset procedure. 166 */ 167 status_t 168 freecom_reset(usb_device_info *udi) 169 { 170 status_t status = B_OK; 171 /* not required ? */ 172 return status; 173 } 174 175 /** 176 \fn:usb_callback 177 \param cookie:??? 178 \param status:??? 179 \param data:??? 180 \param actual_len:??? 181 \return:??? 182 183 ??? 184 */ 185 static void usb_callback(void *cookie, 186 uint32 status, 187 void *data, 188 uint32 actual_len) 189 { 190 if(cookie){ 191 usb_device_info *udi = (usb_device_info *)cookie; 192 // PTRACE(udi, "FCM:usb_callback:status:0x%08x,len:%d\n", status, actual_len); 193 udi->status = status; 194 udi->data = data; 195 udi->actual_len = actual_len; 196 if(udi->status != B_CANCELED) 197 release_sem(udi->trans_sem); 198 } 199 } 200 201 /** 202 \fn:queue_bulk 203 \param udi: device for which que_bulk request is performed 204 \param buffer: data buffer, used in bulk i/o operation 205 \param len: length of data buffer 206 \param b_in: is "true" if input (device->host) data transfer, "false" otherwise 207 \return: status of operation. 208 209 performs queue_bulk USB request for corresponding pipe and handle timeout of this 210 operation. 211 */ 212 static status_t 213 queue_bulk(usb_device_info *udi, 214 void *buffer, 215 size_t len, 216 bool b_in) 217 { 218 status_t status = B_OK; 219 usb_pipe pipe = b_in ? udi->pipe_in : udi->pipe_out; 220 status = (*udi->usb_m->queue_bulk)(pipe, buffer, len, usb_callback, udi); 221 if(status != B_OK){ 222 PTRACE_ALWAYS(udi, "FCM:queue_bulk:failed:%08x\n", status); 223 } else { 224 status = acquire_sem_etc(udi->trans_sem, 1, B_RELATIVE_TIMEOUT, FREECOM_USB_TIMEOUT/*udi->trans_timeout*/); 225 if(status != B_OK){ 226 PTRACE_ALWAYS(udi, "FCM:queue_bulk:acquire_sem_etc failed:%08x\n", status); 227 (*udi->usb_m->cancel_queued_transfers)(pipe); 228 } 229 } 230 return status; 231 } 232 233 /** 234 \fn:write_command 235 */ 236 static status_t 237 write_command(usb_device_info *udi, uint8 type, uint8 *cmd, uint8 timeout) 238 { 239 status_t status = B_OK; 240 /* initialize and fill in command block*/ 241 fcm_command fc = { 242 .type = type, 243 .timeout = FCMTO_DEF, 244 }; 245 if(0 != cmd){ 246 memcpy(fc.data.cmd, cmd, sizeof(fc.data.cmd)); 247 if(cmd[0] == 0x00){ 248 fc.timeout = FCMTO_TU; 249 } 250 } 251 PTRACE(udi, "FCM:FC:{Type:0x%02x; TO:%d;}\n", fc.type, fc.timeout); 252 udi->trace_bytes("FCM:FC::Cmd:\n", fc.data.cmd, sizeof(fc.data.cmd)); 253 udi->trace_bytes("FCM:FC::Pad:\n", fc.data.pad, sizeof(fc.data.pad)); 254 PTRACE(udi,"sizeof:%d\n",sizeof(fc)); 255 /* send command block to device */ 256 status = queue_bulk(udi, &fc, sizeof(fc), false); 257 if(status != B_OK || udi->status != B_OK){ 258 PTRACE_ALWAYS(udi, "FCM:write_command failed:status:%08x usb status:%08x\n", 259 status, udi->status); 260 //(*udi->protocol_m->reset)(udi); 261 status = B_CMD_WIRE_FAILED; 262 } 263 return status; 264 } 265 266 /** 267 \fn:read_status 268 */ 269 static status_t 270 read_status(usb_device_info *udi, fcm_status *fst) 271 { 272 status_t status = B_OK; 273 do{ 274 /* cleanup structure */ 275 memset(fst, 0, sizeof(fcm_status)); 276 /* read status */ 277 status = queue_bulk(udi, fst, sizeof(fcm_status), true); 278 if(status != B_OK || udi->status != B_OK){ 279 PTRACE_ALWAYS(udi, "FCM:read_status failed:" 280 "status:%08x usb status:%08x\n", status, udi->status); 281 //(*udi->protocol_m->reset)(udi); 282 status = B_CMD_WIRE_FAILED; 283 break; 284 } 285 if(udi->actual_len != sizeof(fcm_status)){ 286 PTRACE_ALWAYS(udi, "FCM:read_status failed:requested:%d, readed %d bytes\n", 287 sizeof(fcm_status), udi->actual_len); 288 status = B_CMD_WIRE_FAILED; 289 break; 290 } 291 /* trace readed status info. if required. */ 292 trace_status(udi, fst); 293 if(fst->status & FCMS_BSY){ 294 PTRACE(udi, "FCM:read_status:Timeout. Poll device with another status request.\n"); 295 if(B_OK != (status = write_command(udi, FCMT_STATUS, 0, 0))){ 296 PTRACE_ALWAYS(udi, "FCM:read_status failed:write_command_block failed %08x\n", status); 297 break; 298 } 299 } 300 }while(fst->status & FCMS_BSY); /* repeat until device is busy */ 301 return status; 302 } 303 304 /** 305 \fn:request_transfer 306 */ 307 static status_t 308 request_transfer(usb_device_info *udi, uint8 type, uint32 length, uint8 timeout) 309 { 310 status_t status = B_OK; 311 /* initialize and fill in Command Block */ 312 fcm_command fc = { 313 .type = type, 314 .timeout = timeout, 315 }; 316 fc.data.count = length; 317 318 PTRACE(udi, "FCM:FC:{Type:0x%02x; TO:%d; Count:%d}\n", fc.type, fc.timeout, fc.data.count); 319 udi->trace_bytes("FCM:FC::Pad:\n", fc.data.pad, sizeof(fc.data.pad)); 320 PTRACE(udi,"sizeof:%d\n",sizeof(fc)); 321 /* send command block to device */ 322 status = queue_bulk(udi, &fc, sizeof(fc), false); 323 if(status != B_OK || udi->status != B_OK){ 324 PTRACE_ALWAYS(udi, "FCM:request_transfer:fc send failed:" 325 "status:%08x usb status:%08x\n", status, udi->status); 326 status = B_CMD_WIRE_FAILED; 327 } 328 return status; 329 } 330 331 332 /** 333 \fn:transfer_sg 334 \param udi: corresponding device 335 \param data_sg: io vectors array with data to transfer 336 \param sglist_count: count of entries in io vector array 337 \param offset: 338 \param block_len: 339 \param b_in: direction of data transfer) 340 341 342 */ 343 static status_t 344 transfer_sg(usb_device_info *udi, 345 iovec *data_sg, 346 int32 sglist_count, 347 int32 offset, 348 int32 *block_len, 349 bool b_in) 350 { 351 status_t status = B_OK; 352 usb_pipe pipe = (b_in) ? udi->pipe_in : udi->pipe_out; 353 int32 off = offset; 354 int32 to_xfer = *block_len; 355 /* iterate through SG list */ 356 int i = 0; 357 for(; i < sglist_count && to_xfer > 0; i++) { 358 if(off < data_sg[i].iov_len) { 359 int len = min(to_xfer, (data_sg[i].iov_len - off)); 360 char *buf = ((char *)data_sg[i].iov_base) + off; 361 /* transfer linear block of data to/from device */ 362 if(B_OK == (status = (*udi->usb_m->queue_bulk)(pipe, buf, len, usb_callback, udi))) { 363 status = acquire_sem_etc(udi->trans_sem, 1, B_RELATIVE_TIMEOUT, FREECOM_USB_TIMEOUT); 364 if(status == B_OK){ 365 status = udi->status; 366 if(B_OK != status){ 367 PTRACE_ALWAYS(udi, "FCM:transfer_sg:usb transfer status is not OK:%08x\n", status); 368 } 369 } else { 370 PTRACE_ALWAYS(udi, "FCM:transfer_sg:acquire_sem_etc failed:%08x\n", status); 371 goto finalize; 372 } 373 } else { 374 PTRACE_ALWAYS(udi, "FCM:transfer_sg:queue_bulk failed:%08x\n", status); 375 goto finalize; 376 } 377 /*check amount of transferred data*/ 378 if(len != udi->actual_len){ 379 PTRACE_ALWAYS(udi, "FCM:transfer_sg:WARNING!!!Length reported:%d required:%d\n", 380 udi->actual_len, len); 381 } 382 /* handle offset logic */ 383 to_xfer -= len; 384 off = 0; 385 } else { 386 off -= data_sg[i].iov_len; 387 } 388 } 389 finalize: 390 *block_len -= to_xfer; 391 return (B_OK != status) ? B_CMD_WIRE_FAILED : status; 392 } 393 394 static status_t 395 transfer_data(usb_device_info *udi, 396 iovec *data_sg, 397 int32 sglist_count, 398 int32 transfer_length, 399 int32 *residue, 400 fcm_status *fst, 401 bool b_in) 402 { 403 status_t status = B_OK; 404 int32 readed_len = 0; 405 uint8 xfer_type = b_in ? FCMT_IN : FCMT_OUT; 406 int32 block_len = (FCMR_COD == (fst->reason & FCMR_COD)) ? 407 transfer_length : fst->count; 408 /* Strange situation with SCSIProbe - device say about 6 bytes available for 5-bytes 409 request. Read 5 and all is ok. Hmm... */ 410 if(block_len > transfer_length){ 411 PTRACE_ALWAYS(udi, "FCM:transfer_data:TRUNCATED! " 412 "requested:%d available:%d.\n", transfer_length, block_len); 413 block_len = transfer_length; 414 } 415 /* iterate until data will be transferred */ 416 while(readed_len < transfer_length && block_len > 0){ 417 /* send transfer block */ 418 if(B_OK != (status = request_transfer(udi, xfer_type, block_len, 0))){ 419 goto finalize; 420 } 421 /* check if data available/awaited */ 422 if(FCMS_DRQ != (fst->status & FCMS_DRQ)){ 423 PTRACE_ALWAYS(udi, "FCM:transfer_data:device doesn't want to transfer anything!!!\n"); 424 status = B_CMD_FAILED; 425 goto finalize; 426 } 427 /* check the device state */ 428 if(!((fst->reason & FCMR_REL) == 0 && 429 (fst->reason & FCMR_IO) == ((b_in) ? FCMR_IO : 0) && 430 (fst->reason & FCMR_COD) == 0)) 431 { 432 PTRACE_ALWAYS(udi, "FCM:transfer_data:device doesn't ready to transfer?\n"); 433 status = B_CMD_FAILED; 434 goto finalize; 435 } 436 /* transfer scatter/gather safely only for length bytes at specified offset */ 437 if(B_OK != (status = transfer_sg(udi, data_sg, 438 sglist_count, readed_len, &block_len, b_in))) 439 { 440 goto finalize; 441 } 442 /* read status */ 443 if(B_OK != (status = read_status(udi, fst))){ 444 goto finalize; 445 } 446 /* check device error state */ 447 if(fst->status & FCMS_CHECK){ 448 PTRACE(udi, "FCM:transfer_data:data transfer failed?\n", fst->status); 449 status = B_CMD_FAILED; 450 goto finalize; 451 } 452 /* we have read block successfully */ 453 readed_len += block_len; 454 /* check device state and amount of data */ 455 if((fst->reason & FCMR_COD) == FCMR_COD){ 456 #if 0 /* too frequently reported - disabled to prevent log trashing */ 457 if(readed_len < transfer_length){ 458 PTRACE_ALWAYS(udi, "FCM:transfer_data:Device had LESS data that we wanted.\n"); 459 } 460 #endif 461 break; /* exit iterations - device has finished the talking */ 462 } else { 463 if(readed_len >= transfer_length){ 464 PTRACE_ALWAYS(udi, "FCM:transfer_data:Device had MORE data that we wanted.\n"); 465 break; 466 } 467 block_len = fst->count; /* still have something to read */ 468 } 469 } 470 /* calculate residue */ 471 *residue -= readed_len; 472 if(*residue < 0) 473 *residue = 0; /* TODO: consistency between SG length and transfer length - 474 in MODE_SENSE_10 converted commands f.example ... */ 475 PTRACE(udi,"FCM:transfer_data:was requested:%d, residue:%d\n", transfer_length, *residue); 476 finalize: 477 return status; /* TODO: MUST return B_CMD_*** error codes !!!!! */ 478 } 479 480 /** 481 \fn:freecom_transfer 482 \param udi: corresponding device 483 \param cmd: SCSI command to be performed on USB device 484 \param cmdlen: length of SCSI command 485 \param data_sg: io vectors array with data to transfer 486 \param sglist_count: count of entries in io vector array 487 \param transfer_len: overall length of data to be transferred 488 \param dir: direction of data transfer 489 \param ccbio: CCB_SCSIIO struct for original SCSI command 490 \param cb: callback to handle of final stage of command performing (autosense \ 491 request etc.) 492 493 transfer procedure for bulk-only protocol. Performs SCSI command on USB device 494 [2] 495 */ 496 void 497 freecom_transfer(usb_device_info *udi, 498 uint8 *cmd, 499 uint8 cmdlen, 500 iovec*sg_data, 501 int32 sg_count, 502 int32 transfer_len, 503 EDirection dir, 504 CCB_SCSIIO *ccbio, 505 ud_transfer_callback cb) 506 { 507 status_t command_status = B_CMD_WIRE_FAILED; 508 int32 residue = transfer_len; 509 fcm_status fst = {0}; 510 511 /* Current BeOS scsi_cd driver bombs us with lot of 512 MODE_SENSE/MODE_SELECT commands. Unfortunately some of them 513 hangs the Freecom firmware. Try to ignore translated ones */ 514 #if 1 515 if((MODE_SENSE_10 == cmd[0] && 0x0e == cmd[2]) || 516 (MODE_SELECT_10 == cmd[0] && 0x10 == cmd[1])) 517 { 518 uint8 *cmd_org = 0; 519 /* set the command data pointer */ 520 if(ccbio->cam_ch.cam_flags & CAM_CDB_POINTER){ 521 cmd_org = ccbio->cam_cdb_io.cam_cdb_ptr; 522 }else{ 523 cmd_org = ccbio->cam_cdb_io.cam_cdb_bytes; 524 } 525 if(cmd_org[0] != cmd[0]){ 526 if(MODE_SENSE_10 == cmd[0]){ /* emulate answer - return empty pages */ 527 scsi_mode_param_header_10 *hdr = (scsi_mode_param_header_10*)sg_data[0].iov_base; 528 memset(hdr, 0, sizeof(scsi_mode_param_header_10)); 529 hdr->mode_data_len[1] = 6; 530 PTRACE(udi, "FCM:freecom_transfer:MODE_SENSE_10 ignored %02x->02x\n", cmd_org[0], cmd[0]); 531 } else { 532 PTRACE(udi, "FCM:freecom_transfer:MODE_SELECT_10 ignored %02x->%02x\n", cmd_org[0], cmd[0]); 533 } 534 command_status = B_OK; /* just say that command processed OK */ 535 goto finalize; 536 } 537 } 538 #endif 539 /* write command to device */ 540 if(B_OK != (command_status = write_command(udi, FCMT_ATAPI, cmd, 0))){ 541 PTRACE_ALWAYS(udi, "FCM:freecom_transfer:" 542 "send command failed. Status:%08x\n", command_status); 543 goto finalize; 544 } 545 /* obtain status */ 546 if(B_OK != (command_status = read_status(udi, &fst))){ 547 PTRACE_ALWAYS(udi, "FCM:freecom_transfer:" 548 "read status failed. Status:%08x\n", command_status); 549 goto finalize; 550 } 551 /* check device error state */ 552 if(fst.status & FCMS_CHECK){ 553 PTRACE_ALWAYS(udi, "FCM:freecom_transfer:FST.Status is not OK\n"); 554 command_status = B_CMD_FAILED; 555 goto finalize; 556 } 557 /* transfer data */ 558 if(transfer_len != 0x0 && dir != eDirNone){ 559 command_status = transfer_data(udi, sg_data, sg_count, transfer_len, 560 &residue, &fst, (eDirIn == dir)); 561 } 562 finalize: 563 /* finalize transfer */ 564 cb(udi, ccbio, residue, command_status); 565 } 566 567 static status_t 568 std_ops(int32 op, ...) 569 { 570 switch(op) { 571 case B_MODULE_INIT: 572 return B_OK; 573 case B_MODULE_UNINIT: 574 return B_OK; 575 default: 576 return B_ERROR; 577 } 578 } 579 580 static protocol_module_info freecom_protocol_module = { 581 { FREECOM_PROTOCOL_MODULE_NAME, 0, std_ops }, 582 freecom_initialize, 583 freecom_reset, 584 freecom_transfer, 585 }; 586 587 _EXPORT protocol_module_info *modules[] = { 588 &freecom_protocol_module, 589 NULL 590 }; 591