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