- pre5:
[davej-history.git] / drivers / usb / storage / freecom.c
bloba9501a06a71a1aa63a42e8c67f9e6984eea1b5d5
1 /* Driver for Freecom USB/IDE adaptor
3 * $Id: freecom.c,v 1.7 2000/08/25 00:13:51 mdharm Exp $
5 * Freecom v0.1:
7 * First release
9 * Current development and maintenance by:
10 * (C) 2000 David Brown <usb-storage@davidb.org>
12 * This program is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by the
14 * Free Software Foundation; either version 2, or (at your option) any
15 * later version.
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
22 * You should have received a copy of the GNU General Public License along
23 * with this program; if not, write to the Free Software Foundation, Inc.,
24 * 675 Mass Ave, Cambridge, MA 02139, USA.
26 * This driver was developed with information provided in FREECOM's USB
27 * Programmers Reference Guide. For further information contact Freecom
28 * (http://www.freecom.de/)
31 #include "transport.h"
32 #include "protocol.h"
33 #include "usb.h"
34 #include "debug.h"
35 #include "freecom.h"
37 static void pdump (void *, int);
39 struct freecom_udata {
40 __u8 buffer[64]; /* Common command block. */
42 typedef struct freecom_udata *freecom_udata_t;
44 /* All of the outgoing packets are 64 bytes long. */
45 struct freecom_cb_wrap {
46 __u8 Type; /* Command type. */
47 __u8 Timeout; /* Timeout in seconds. */
48 __u8 Atapi[12]; /* An ATAPI packet. */
49 __u8 Filler[50]; /* Padding Data. */
52 struct freecom_xfer_wrap {
53 __u8 Type; /* Command type. */
54 __u8 Timeout; /* Timeout in seconds. */
55 __u32 Count; /* Number of bytes to transfer. */
56 __u8 Pad[58];
59 struct freecom_status {
60 __u8 Status;
61 __u8 Reason;
62 __u16 Count;
63 __u8 Pad[60];
66 /* These are the packet types. The low bit indicates that this command
67 * should wait for an interrupt. */
68 #define FCM_PACKET_ATAPI 0x21
70 /* Receive data from the IDE interface. The ATAPI packet has already
71 * waited, so the data should be immediately available. */
72 #define FCM_PACKET_INPUT 0x90
74 /* All packets (except for status) are 64 bytes long. */
75 #define FCM_PACKET_LENGTH 64
77 static int
78 freecom_readdata (Scsi_Cmnd *srb, struct us_data *us,
79 int ipipe, int opipe, int count)
81 freecom_udata_t extra = (freecom_udata_t) us->extra;
82 struct freecom_xfer_wrap *fxfr =
83 (struct freecom_xfer_wrap *) extra->buffer;
84 int result, partial;
85 int offset;
86 __u8 *buffer = extra->buffer;
88 fxfr->Type = FCM_PACKET_INPUT | 0x00;
89 fxfr->Timeout = 0; /* Short timeout for debugging. */
90 fxfr->Count = cpu_to_le32 (count);
91 memset (fxfr->Pad, 0, sizeof (fxfr->Pad));
93 printk (KERN_DEBUG "Read data Freecom! (c=%d)\n", count);
95 /* Issue the transfer command. */
96 result = usb_stor_bulk_msg (us, fxfr, opipe,
97 FCM_PACKET_LENGTH, &partial);
98 if (result != 0) {
99 US_DEBUGP ("Freecom readdata xpot failure: r=%d, p=%d\n",
100 result, partial);
102 /* -ENOENT -- we canceled this transfer */
103 if (result == -ENOENT) {
104 US_DEBUGP("us_transfer_partial(): transfer aborted\n");
105 return US_BULK_TRANSFER_ABORTED;
108 return USB_STOR_TRANSPORT_ERROR;
110 printk (KERN_DEBUG "Done issuing read request: %d %d\n",
111 result, partial);
113 /* Now transfer all of our blocks. */
114 if (srb->use_sg) {
115 US_DEBUGP ("Need to implement scatter-gather\n");
116 return USB_STOR_TRANSPORT_ERROR;
117 } else {
118 offset = 0;
120 while (offset < count) {
121 printk (KERN_DEBUG "Start of read\n");
122 /* Use the given buffer directly, but only if there
123 * is space for an entire packet. */
125 if (offset + 64 <= srb->request_bufflen) {
126 result = usb_stor_bulk_msg (
127 us, srb->request_buffer+offset,
128 ipipe, 64, &partial);
129 printk (KERN_DEBUG "Read111 = %d, %d\n",
130 result, partial);
131 pdump (srb->request_buffer+offset,
132 partial);
133 } else {
134 result = usb_stor_bulk_msg (
135 us, buffer,
136 ipipe, 64, &partial);
137 printk (KERN_DEBUG "Read112 = %d, %d\n",
138 result, partial);
139 memcpy (srb->request_buffer+offset,
140 buffer,
141 srb->request_bufflen - offset);
142 pdump (srb->request_buffer+offset,
143 srb->request_bufflen - offset);
146 if (result != 0) {
147 US_DEBUGP ("Freecom readblock r=%d, p=%d\n",
148 result, partial);
150 /* -ENOENT -- we canceled this transfer */
151 if (result == -ENOENT) {
152 US_DEBUGP("us_transfer_partial(): transfer aborted\n");
153 return US_BULK_TRANSFER_ABORTED;
156 return USB_STOR_TRANSPORT_ERROR;
159 offset += 64;
163 printk (KERN_DEBUG "freecom_readdata done!\n");
164 return USB_STOR_TRANSPORT_GOOD;
168 * Transport for the Freecom USB/IDE adaptor.
171 int freecom_transport(Scsi_Cmnd *srb, struct us_data *us)
173 struct freecom_cb_wrap *fcb;
174 struct freecom_status *fst;
175 int ipipe, opipe; /* We need both pipes. */
176 int result;
177 int partial;
178 int length;
179 freecom_udata_t extra;
181 /* Allocate a buffer for us. The upper usb transport code will
182 * free this for us when cleaning up. */
183 if (us->extra == NULL) {
184 us->extra = kmalloc (sizeof (struct freecom_udata),
185 GFP_KERNEL);
186 if (us->extra == NULL) {
187 printk (KERN_WARNING USB_STORAGE "Out of memory\n");
188 return USB_STOR_TRANSPORT_ERROR;
192 extra = (freecom_udata_t) us->extra;
194 fcb = (struct freecom_cb_wrap *) extra->buffer;
195 fst = (struct freecom_status *) extra->buffer;
197 printk (KERN_DEBUG "Freecom TRANSPORT STARTED\n");
199 /* Get handles for both transports. */
200 opipe = usb_sndbulkpipe (us->pusb_dev, us->ep_out);
201 ipipe = usb_rcvbulkpipe (us->pusb_dev, us->ep_in);
203 #if 0
204 /* Yuck, let's see if this helps us. Artificially increase the
205 * length on this. */
206 if (srb->cmnd[0] == 0x03 && srb->cmnd[4] == 0x12)
207 srb->cmnd[4] = 0x0E;
208 #endif
210 /* The ATAPI Command always goes out first. */
211 fcb->Type = FCM_PACKET_ATAPI;
212 fcb->Timeout = 0;
213 memcpy (fcb->Atapi, srb->cmnd, 12);
214 memset (fcb->Filler, 0, sizeof (fcb->Filler));
216 pdump (srb->cmnd, 12);
218 /* Send it out. */
219 result = usb_stor_bulk_msg (us, fcb, opipe,
220 FCM_PACKET_LENGTH, &partial);
222 /* The Freecom device will only fail if there is something wrong in
223 * USB land. It returns the status in its own registers, which
224 * come back in the bulk pipe. */
225 if (result != 0) {
226 US_DEBUGP ("freecom xport failure: r=%d, p=%d\n",
227 result, partial);
229 /* -ENOENT -- we canceled this transfer */
230 if (result == -ENOENT) {
231 US_DEBUGP("us_transfer_partial(): transfer aborted\n");
232 return US_BULK_TRANSFER_ABORTED;
235 return USB_STOR_TRANSPORT_ERROR;
238 /* There are times we can optimize out this status read, but it
239 * doesn't hurt us to always do it now. */
240 result = usb_stor_bulk_msg (us, fst, ipipe,
241 FCM_PACKET_LENGTH, &partial);
242 printk (KERN_DEBUG "foo Status result %d %d\n", result, partial);
243 /* -ENOENT -- we canceled this transfer */
244 if (result == -ENOENT) {
245 US_DEBUGP("us_transfer_partial(): transfer aborted\n");
246 return US_BULK_TRANSFER_ABORTED;
249 pdump ((void *) fst, partial);
250 if (partial != 4 || result != 0) {
251 return USB_STOR_TRANSPORT_ERROR;
253 if ((fst->Reason & 1) != 0) {
254 printk (KERN_DEBUG "operation failed\n");
255 return USB_STOR_TRANSPORT_FAILED;
258 /* The device might not have as much data available as we
259 * requested. If you ask for more than the device has, this reads
260 * and such will hang. */
261 printk (KERN_DEBUG "Device indicates that it has %d bytes available\n",
262 le16_to_cpu (fst->Count));
264 /* Find the length we desire to read. It is the lesser of the SCSI
265 * layer's requested length, and the length the device claims to
266 * have available. */
267 length = us_transfer_length (srb);
268 printk (KERN_DEBUG "SCSI requested %d\n", length);
269 if (length > le16_to_cpu (fst->Count))
270 length = le16_to_cpu (fst->Count);
272 /* What we do now depends on what direction the data is supposed to
273 * move in. */
275 switch (us->srb->sc_data_direction) {
276 case SCSI_DATA_READ:
277 result = freecom_readdata (srb, us, ipipe, opipe, length);
278 if (result != USB_STOR_TRANSPORT_GOOD)
279 return result;
280 break;
282 default:
283 US_DEBUGP ("freecom unimplemented direction: %d\n",
284 us->srb->sc_data_direction);
285 // Return fail, SCSI seems to handle this better.
286 return USB_STOR_TRANSPORT_FAILED;
287 break;
290 #if 0
291 /* After the transfer, we can read our status register. */
292 printk (KERN_DEBUG "Going to read status register\n");
293 result = usb_stor_bulk_msg (us, &fst, ipipe,
294 FCM_PACKET_LENGTH, &partial);
295 printk (KERN_DEBUG "Result from read %d %d\n", result, partial);
296 if (result != 0) {
297 return USB_STOR_TRANSPORT_ERROR;
299 if ((fst.Reason & 1) != 0) {
300 return USB_STOR_TRANSPORT_FAILED;
302 #endif
304 return USB_STOR_TRANSPORT_GOOD;
306 printk (KERN_DEBUG "Freecom: transfer_length = %d\n",
307 us_transfer_length (srb));
309 printk (KERN_DEBUG "Freecom: direction = %d\n",
310 srb->sc_data_direction);
312 return USB_STOR_TRANSPORT_ERROR;
315 int usb_stor_freecom_reset(struct us_data *us)
317 printk (KERN_DEBUG "freecom reset called\n");
319 /* We don't really have this feature. */
320 return USB_STOR_TRANSPORT_ERROR;
323 static void pdump (void *ibuffer, int length)
325 static char line[80];
326 int offset = 0;
327 unsigned char *buffer = (unsigned char *) ibuffer;
328 int i, j;
329 int from, base;
331 offset = 0;
332 for (i = 0; i < length; i++) {
333 if ((i & 15) == 0) {
334 if (i > 0) {
335 offset += sprintf (line+offset, " - ");
336 for (j = i - 16; j < i; j++) {
337 if (buffer[j] >= 32 && buffer[j] <= 126)
338 line[offset++] = buffer[j];
339 else
340 line[offset++] = '.';
342 line[offset] = 0;
343 printk (KERN_DEBUG "%s\n", line);
344 offset = 0;
346 offset += sprintf (line+offset, "%08x:", i);
348 else if ((i & 7) == 0) {
349 offset += sprintf (line+offset, " -");
351 offset += sprintf (line+offset, " %02x", buffer[i] & 0xff);
354 /* Add the last "chunk" of data. */
355 from = (length - 1) % 16;
356 base = ((length - 1) / 16) * 16;
358 for (i = from + 1; i < 16; i++)
359 offset += sprintf (line+offset, " ");
360 if (from < 8)
361 offset += sprintf (line+offset, " ");
362 offset += sprintf (line+offset, " - ");
364 for (i = 0; i <= from; i++) {
365 if (buffer[base+i] >= 32 && buffer[base+i] <= 126)
366 line[offset++] = buffer[base+i];
367 else
368 line[offset++] = '.';
370 line[offset] = 0;
371 printk (KERN_DEBUG "%s\n", line);
372 offset = 0;