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 */
trace_status(usb_device_info * udi,const fcm_status * st)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
freecom_initialize(usb_device_info * udi)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
freecom_reset(usb_device_info * udi)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 */
usb_callback(void * cookie,uint32 status,void * data,uint32 actual_len)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
queue_bulk(usb_device_info * udi,void * buffer,size_t len,bool b_in)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
write_command(usb_device_info * udi,uint8 type,uint8 * cmd,uint8 timeout)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
read_status(usb_device_info * udi,fcm_status * fst)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
request_transfer(usb_device_info * udi,uint8 type,uint32 length,uint8 timeout)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
transfer_sg(usb_device_info * udi,iovec * data_sg,int32 sglist_count,int32 offset,int32 * block_len,bool b_in)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
transfer_data(usb_device_info * udi,iovec * data_sg,int32 sglist_count,int32 transfer_length,int32 * residue,fcm_status * fst,bool b_in)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
freecom_transfer(usb_device_info * udi,uint8 * cmd,uint8 cmdlen,iovec * sg_data,int32 sg_count,int32 transfer_len,EDirection dir,CCB_SCSIIO * ccbio,ud_transfer_callback cb)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
std_ops(int32 op,...)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