18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for Freecom USB/IDE adaptor 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Freecom v0.1: 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * First release 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Current development and maintenance by: 108c2ecf20Sopenharmony_ci * (C) 2000 David Brown <usb-storage@davidb.org> 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * This driver was developed with information provided in FREECOM's USB 138c2ecf20Sopenharmony_ci * Programmers Reference Guide. For further information contact Freecom 148c2ecf20Sopenharmony_ci * (https://www.freecom.de/) 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <linux/module.h> 188c2ecf20Sopenharmony_ci#include <scsi/scsi.h> 198c2ecf20Sopenharmony_ci#include <scsi/scsi_cmnd.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include "usb.h" 228c2ecf20Sopenharmony_ci#include "transport.h" 238c2ecf20Sopenharmony_ci#include "protocol.h" 248c2ecf20Sopenharmony_ci#include "debug.h" 258c2ecf20Sopenharmony_ci#include "scsiglue.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define DRV_NAME "ums-freecom" 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Driver for Freecom USB/IDE adaptor"); 308c2ecf20Sopenharmony_ciMODULE_AUTHOR("David Brown <usb-storage@davidb.org>"); 318c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 328c2ecf20Sopenharmony_ciMODULE_IMPORT_NS(USB_STORAGE); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_STORAGE_DEBUG 358c2ecf20Sopenharmony_cistatic void pdump(struct us_data *us, void *ibuffer, int length); 368c2ecf20Sopenharmony_ci#endif 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/* Bits of HD_STATUS */ 398c2ecf20Sopenharmony_ci#define ERR_STAT 0x01 408c2ecf20Sopenharmony_ci#define DRQ_STAT 0x08 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/* All of the outgoing packets are 64 bytes long. */ 438c2ecf20Sopenharmony_cistruct freecom_cb_wrap { 448c2ecf20Sopenharmony_ci u8 Type; /* Command type. */ 458c2ecf20Sopenharmony_ci u8 Timeout; /* Timeout in seconds. */ 468c2ecf20Sopenharmony_ci u8 Atapi[12]; /* An ATAPI packet. */ 478c2ecf20Sopenharmony_ci u8 Filler[50]; /* Padding Data. */ 488c2ecf20Sopenharmony_ci}; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistruct freecom_xfer_wrap { 518c2ecf20Sopenharmony_ci u8 Type; /* Command type. */ 528c2ecf20Sopenharmony_ci u8 Timeout; /* Timeout in seconds. */ 538c2ecf20Sopenharmony_ci __le32 Count; /* Number of bytes to transfer. */ 548c2ecf20Sopenharmony_ci u8 Pad[58]; 558c2ecf20Sopenharmony_ci} __attribute__ ((packed)); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistruct freecom_ide_out { 588c2ecf20Sopenharmony_ci u8 Type; /* Type + IDE register. */ 598c2ecf20Sopenharmony_ci u8 Pad; 608c2ecf20Sopenharmony_ci __le16 Value; /* Value to write. */ 618c2ecf20Sopenharmony_ci u8 Pad2[60]; 628c2ecf20Sopenharmony_ci}; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistruct freecom_ide_in { 658c2ecf20Sopenharmony_ci u8 Type; /* Type | IDE register. */ 668c2ecf20Sopenharmony_ci u8 Pad[63]; 678c2ecf20Sopenharmony_ci}; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistruct freecom_status { 708c2ecf20Sopenharmony_ci u8 Status; 718c2ecf20Sopenharmony_ci u8 Reason; 728c2ecf20Sopenharmony_ci __le16 Count; 738c2ecf20Sopenharmony_ci u8 Pad[60]; 748c2ecf20Sopenharmony_ci}; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci/* 778c2ecf20Sopenharmony_ci * Freecom stuffs the interrupt status in the INDEX_STAT bit of the ide 788c2ecf20Sopenharmony_ci * register. 798c2ecf20Sopenharmony_ci */ 808c2ecf20Sopenharmony_ci#define FCM_INT_STATUS 0x02 /* INDEX_STAT */ 818c2ecf20Sopenharmony_ci#define FCM_STATUS_BUSY 0x80 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci/* 848c2ecf20Sopenharmony_ci * These are the packet types. The low bit indicates that this command 858c2ecf20Sopenharmony_ci * should wait for an interrupt. 868c2ecf20Sopenharmony_ci */ 878c2ecf20Sopenharmony_ci#define FCM_PACKET_ATAPI 0x21 888c2ecf20Sopenharmony_ci#define FCM_PACKET_STATUS 0x20 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci/* 918c2ecf20Sopenharmony_ci * Receive data from the IDE interface. The ATAPI packet has already 928c2ecf20Sopenharmony_ci * waited, so the data should be immediately available. 938c2ecf20Sopenharmony_ci */ 948c2ecf20Sopenharmony_ci#define FCM_PACKET_INPUT 0x81 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci/* Send data to the IDE interface. */ 978c2ecf20Sopenharmony_ci#define FCM_PACKET_OUTPUT 0x01 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci/* 1008c2ecf20Sopenharmony_ci * Write a value to an ide register. Or the ide register to write after 1018c2ecf20Sopenharmony_ci * munging the address a bit. 1028c2ecf20Sopenharmony_ci */ 1038c2ecf20Sopenharmony_ci#define FCM_PACKET_IDE_WRITE 0x40 1048c2ecf20Sopenharmony_ci#define FCM_PACKET_IDE_READ 0xC0 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci/* All packets (except for status) are 64 bytes long. */ 1078c2ecf20Sopenharmony_ci#define FCM_PACKET_LENGTH 64 1088c2ecf20Sopenharmony_ci#define FCM_STATUS_PACKET_LENGTH 4 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic int init_freecom(struct us_data *us); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci/* 1148c2ecf20Sopenharmony_ci * The table of devices 1158c2ecf20Sopenharmony_ci */ 1168c2ecf20Sopenharmony_ci#define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \ 1178c2ecf20Sopenharmony_ci vendorName, productName, useProtocol, useTransport, \ 1188c2ecf20Sopenharmony_ci initFunction, flags) \ 1198c2ecf20Sopenharmony_ci{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ 1208c2ecf20Sopenharmony_ci .driver_info = (flags) } 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic struct usb_device_id freecom_usb_ids[] = { 1238c2ecf20Sopenharmony_ci# include "unusual_freecom.h" 1248c2ecf20Sopenharmony_ci { } /* Terminating entry */ 1258c2ecf20Sopenharmony_ci}; 1268c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, freecom_usb_ids); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci#undef UNUSUAL_DEV 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci/* 1318c2ecf20Sopenharmony_ci * The flags table 1328c2ecf20Sopenharmony_ci */ 1338c2ecf20Sopenharmony_ci#define UNUSUAL_DEV(idVendor, idProduct, bcdDeviceMin, bcdDeviceMax, \ 1348c2ecf20Sopenharmony_ci vendor_name, product_name, use_protocol, use_transport, \ 1358c2ecf20Sopenharmony_ci init_function, Flags) \ 1368c2ecf20Sopenharmony_ci{ \ 1378c2ecf20Sopenharmony_ci .vendorName = vendor_name, \ 1388c2ecf20Sopenharmony_ci .productName = product_name, \ 1398c2ecf20Sopenharmony_ci .useProtocol = use_protocol, \ 1408c2ecf20Sopenharmony_ci .useTransport = use_transport, \ 1418c2ecf20Sopenharmony_ci .initFunction = init_function, \ 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic struct us_unusual_dev freecom_unusual_dev_list[] = { 1458c2ecf20Sopenharmony_ci# include "unusual_freecom.h" 1468c2ecf20Sopenharmony_ci { } /* Terminating entry */ 1478c2ecf20Sopenharmony_ci}; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci#undef UNUSUAL_DEV 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic int 1528c2ecf20Sopenharmony_cifreecom_readdata (struct scsi_cmnd *srb, struct us_data *us, 1538c2ecf20Sopenharmony_ci unsigned int ipipe, unsigned int opipe, int count) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci struct freecom_xfer_wrap *fxfr = 1568c2ecf20Sopenharmony_ci (struct freecom_xfer_wrap *) us->iobuf; 1578c2ecf20Sopenharmony_ci int result; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci fxfr->Type = FCM_PACKET_INPUT | 0x00; 1608c2ecf20Sopenharmony_ci fxfr->Timeout = 0; /* Short timeout for debugging. */ 1618c2ecf20Sopenharmony_ci fxfr->Count = cpu_to_le32 (count); 1628c2ecf20Sopenharmony_ci memset (fxfr->Pad, 0, sizeof (fxfr->Pad)); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci usb_stor_dbg(us, "Read data Freecom! (c=%d)\n", count); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci /* Issue the transfer command. */ 1678c2ecf20Sopenharmony_ci result = usb_stor_bulk_transfer_buf (us, opipe, fxfr, 1688c2ecf20Sopenharmony_ci FCM_PACKET_LENGTH, NULL); 1698c2ecf20Sopenharmony_ci if (result != USB_STOR_XFER_GOOD) { 1708c2ecf20Sopenharmony_ci usb_stor_dbg(us, "Freecom readdata transport error\n"); 1718c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_ERROR; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci /* Now transfer all of our blocks. */ 1758c2ecf20Sopenharmony_ci usb_stor_dbg(us, "Start of read\n"); 1768c2ecf20Sopenharmony_ci result = usb_stor_bulk_srb(us, ipipe, srb); 1778c2ecf20Sopenharmony_ci usb_stor_dbg(us, "freecom_readdata done!\n"); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci if (result > USB_STOR_XFER_SHORT) 1808c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_ERROR; 1818c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_GOOD; 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic int 1858c2ecf20Sopenharmony_cifreecom_writedata (struct scsi_cmnd *srb, struct us_data *us, 1868c2ecf20Sopenharmony_ci int unsigned ipipe, unsigned int opipe, int count) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci struct freecom_xfer_wrap *fxfr = 1898c2ecf20Sopenharmony_ci (struct freecom_xfer_wrap *) us->iobuf; 1908c2ecf20Sopenharmony_ci int result; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci fxfr->Type = FCM_PACKET_OUTPUT | 0x00; 1938c2ecf20Sopenharmony_ci fxfr->Timeout = 0; /* Short timeout for debugging. */ 1948c2ecf20Sopenharmony_ci fxfr->Count = cpu_to_le32 (count); 1958c2ecf20Sopenharmony_ci memset (fxfr->Pad, 0, sizeof (fxfr->Pad)); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci usb_stor_dbg(us, "Write data Freecom! (c=%d)\n", count); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci /* Issue the transfer command. */ 2008c2ecf20Sopenharmony_ci result = usb_stor_bulk_transfer_buf (us, opipe, fxfr, 2018c2ecf20Sopenharmony_ci FCM_PACKET_LENGTH, NULL); 2028c2ecf20Sopenharmony_ci if (result != USB_STOR_XFER_GOOD) { 2038c2ecf20Sopenharmony_ci usb_stor_dbg(us, "Freecom writedata transport error\n"); 2048c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_ERROR; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci /* Now transfer all of our blocks. */ 2088c2ecf20Sopenharmony_ci usb_stor_dbg(us, "Start of write\n"); 2098c2ecf20Sopenharmony_ci result = usb_stor_bulk_srb(us, opipe, srb); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci usb_stor_dbg(us, "freecom_writedata done!\n"); 2128c2ecf20Sopenharmony_ci if (result > USB_STOR_XFER_SHORT) 2138c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_ERROR; 2148c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_GOOD; 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci/* 2188c2ecf20Sopenharmony_ci * Transport for the Freecom USB/IDE adaptor. 2198c2ecf20Sopenharmony_ci * 2208c2ecf20Sopenharmony_ci */ 2218c2ecf20Sopenharmony_cistatic int freecom_transport(struct scsi_cmnd *srb, struct us_data *us) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci struct freecom_cb_wrap *fcb; 2248c2ecf20Sopenharmony_ci struct freecom_status *fst; 2258c2ecf20Sopenharmony_ci unsigned int ipipe, opipe; /* We need both pipes. */ 2268c2ecf20Sopenharmony_ci int result; 2278c2ecf20Sopenharmony_ci unsigned int partial; 2288c2ecf20Sopenharmony_ci int length; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci fcb = (struct freecom_cb_wrap *) us->iobuf; 2318c2ecf20Sopenharmony_ci fst = (struct freecom_status *) us->iobuf; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci usb_stor_dbg(us, "Freecom TRANSPORT STARTED\n"); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci /* Get handles for both transports. */ 2368c2ecf20Sopenharmony_ci opipe = us->send_bulk_pipe; 2378c2ecf20Sopenharmony_ci ipipe = us->recv_bulk_pipe; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci /* The ATAPI Command always goes out first. */ 2408c2ecf20Sopenharmony_ci fcb->Type = FCM_PACKET_ATAPI | 0x00; 2418c2ecf20Sopenharmony_ci fcb->Timeout = 0; 2428c2ecf20Sopenharmony_ci memcpy (fcb->Atapi, srb->cmnd, 12); 2438c2ecf20Sopenharmony_ci memset (fcb->Filler, 0, sizeof (fcb->Filler)); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci US_DEBUG(pdump(us, srb->cmnd, 12)); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci /* Send it out. */ 2488c2ecf20Sopenharmony_ci result = usb_stor_bulk_transfer_buf (us, opipe, fcb, 2498c2ecf20Sopenharmony_ci FCM_PACKET_LENGTH, NULL); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci /* 2528c2ecf20Sopenharmony_ci * The Freecom device will only fail if there is something wrong in 2538c2ecf20Sopenharmony_ci * USB land. It returns the status in its own registers, which 2548c2ecf20Sopenharmony_ci * come back in the bulk pipe. 2558c2ecf20Sopenharmony_ci */ 2568c2ecf20Sopenharmony_ci if (result != USB_STOR_XFER_GOOD) { 2578c2ecf20Sopenharmony_ci usb_stor_dbg(us, "freecom transport error\n"); 2588c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_ERROR; 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci /* 2628c2ecf20Sopenharmony_ci * There are times we can optimize out this status read, but it 2638c2ecf20Sopenharmony_ci * doesn't hurt us to always do it now. 2648c2ecf20Sopenharmony_ci */ 2658c2ecf20Sopenharmony_ci result = usb_stor_bulk_transfer_buf (us, ipipe, fst, 2668c2ecf20Sopenharmony_ci FCM_STATUS_PACKET_LENGTH, &partial); 2678c2ecf20Sopenharmony_ci usb_stor_dbg(us, "foo Status result %d %u\n", result, partial); 2688c2ecf20Sopenharmony_ci if (result != USB_STOR_XFER_GOOD) 2698c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_ERROR; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci US_DEBUG(pdump(us, (void *)fst, partial)); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci /* 2748c2ecf20Sopenharmony_ci * The firmware will time-out commands after 20 seconds. Some commands 2758c2ecf20Sopenharmony_ci * can legitimately take longer than this, so we use a different 2768c2ecf20Sopenharmony_ci * command that only waits for the interrupt and then sends status, 2778c2ecf20Sopenharmony_ci * without having to send a new ATAPI command to the device. 2788c2ecf20Sopenharmony_ci * 2798c2ecf20Sopenharmony_ci * NOTE: There is some indication that a data transfer after a timeout 2808c2ecf20Sopenharmony_ci * may not work, but that is a condition that should never happen. 2818c2ecf20Sopenharmony_ci */ 2828c2ecf20Sopenharmony_ci while (fst->Status & FCM_STATUS_BUSY) { 2838c2ecf20Sopenharmony_ci usb_stor_dbg(us, "20 second USB/ATAPI bridge TIMEOUT occurred!\n"); 2848c2ecf20Sopenharmony_ci usb_stor_dbg(us, "fst->Status is %x\n", fst->Status); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci /* Get the status again */ 2878c2ecf20Sopenharmony_ci fcb->Type = FCM_PACKET_STATUS; 2888c2ecf20Sopenharmony_ci fcb->Timeout = 0; 2898c2ecf20Sopenharmony_ci memset (fcb->Atapi, 0, sizeof(fcb->Atapi)); 2908c2ecf20Sopenharmony_ci memset (fcb->Filler, 0, sizeof (fcb->Filler)); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci /* Send it out. */ 2938c2ecf20Sopenharmony_ci result = usb_stor_bulk_transfer_buf (us, opipe, fcb, 2948c2ecf20Sopenharmony_ci FCM_PACKET_LENGTH, NULL); 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci /* 2978c2ecf20Sopenharmony_ci * The Freecom device will only fail if there is something 2988c2ecf20Sopenharmony_ci * wrong in USB land. It returns the status in its own 2998c2ecf20Sopenharmony_ci * registers, which come back in the bulk pipe. 3008c2ecf20Sopenharmony_ci */ 3018c2ecf20Sopenharmony_ci if (result != USB_STOR_XFER_GOOD) { 3028c2ecf20Sopenharmony_ci usb_stor_dbg(us, "freecom transport error\n"); 3038c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_ERROR; 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci /* get the data */ 3078c2ecf20Sopenharmony_ci result = usb_stor_bulk_transfer_buf (us, ipipe, fst, 3088c2ecf20Sopenharmony_ci FCM_STATUS_PACKET_LENGTH, &partial); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci usb_stor_dbg(us, "bar Status result %d %u\n", result, partial); 3118c2ecf20Sopenharmony_ci if (result != USB_STOR_XFER_GOOD) 3128c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_ERROR; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci US_DEBUG(pdump(us, (void *)fst, partial)); 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci if (partial != 4) 3188c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_ERROR; 3198c2ecf20Sopenharmony_ci if ((fst->Status & 1) != 0) { 3208c2ecf20Sopenharmony_ci usb_stor_dbg(us, "operation failed\n"); 3218c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_FAILED; 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci /* 3258c2ecf20Sopenharmony_ci * The device might not have as much data available as we 3268c2ecf20Sopenharmony_ci * requested. If you ask for more than the device has, this reads 3278c2ecf20Sopenharmony_ci * and such will hang. 3288c2ecf20Sopenharmony_ci */ 3298c2ecf20Sopenharmony_ci usb_stor_dbg(us, "Device indicates that it has %d bytes available\n", 3308c2ecf20Sopenharmony_ci le16_to_cpu(fst->Count)); 3318c2ecf20Sopenharmony_ci usb_stor_dbg(us, "SCSI requested %d\n", scsi_bufflen(srb)); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci /* Find the length we desire to read. */ 3348c2ecf20Sopenharmony_ci switch (srb->cmnd[0]) { 3358c2ecf20Sopenharmony_ci case INQUIRY: 3368c2ecf20Sopenharmony_ci case REQUEST_SENSE: /* 16 or 18 bytes? spec says 18, lots of devices only have 16 */ 3378c2ecf20Sopenharmony_ci case MODE_SENSE: 3388c2ecf20Sopenharmony_ci case MODE_SENSE_10: 3398c2ecf20Sopenharmony_ci length = le16_to_cpu(fst->Count); 3408c2ecf20Sopenharmony_ci break; 3418c2ecf20Sopenharmony_ci default: 3428c2ecf20Sopenharmony_ci length = scsi_bufflen(srb); 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci /* verify that this amount is legal */ 3468c2ecf20Sopenharmony_ci if (length > scsi_bufflen(srb)) { 3478c2ecf20Sopenharmony_ci length = scsi_bufflen(srb); 3488c2ecf20Sopenharmony_ci usb_stor_dbg(us, "Truncating request to match buffer length: %d\n", 3498c2ecf20Sopenharmony_ci length); 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci /* 3538c2ecf20Sopenharmony_ci * What we do now depends on what direction the data is supposed to 3548c2ecf20Sopenharmony_ci * move in. 3558c2ecf20Sopenharmony_ci */ 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci switch (us->srb->sc_data_direction) { 3588c2ecf20Sopenharmony_ci case DMA_FROM_DEVICE: 3598c2ecf20Sopenharmony_ci /* catch bogus "read 0 length" case */ 3608c2ecf20Sopenharmony_ci if (!length) 3618c2ecf20Sopenharmony_ci break; 3628c2ecf20Sopenharmony_ci /* 3638c2ecf20Sopenharmony_ci * Make sure that the status indicates that the device 3648c2ecf20Sopenharmony_ci * wants data as well. 3658c2ecf20Sopenharmony_ci */ 3668c2ecf20Sopenharmony_ci if ((fst->Status & DRQ_STAT) == 0 || (fst->Reason & 3) != 2) { 3678c2ecf20Sopenharmony_ci usb_stor_dbg(us, "SCSI wants data, drive doesn't have any\n"); 3688c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_FAILED; 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci result = freecom_readdata (srb, us, ipipe, opipe, length); 3718c2ecf20Sopenharmony_ci if (result != USB_STOR_TRANSPORT_GOOD) 3728c2ecf20Sopenharmony_ci return result; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci usb_stor_dbg(us, "Waiting for status\n"); 3758c2ecf20Sopenharmony_ci result = usb_stor_bulk_transfer_buf (us, ipipe, fst, 3768c2ecf20Sopenharmony_ci FCM_PACKET_LENGTH, &partial); 3778c2ecf20Sopenharmony_ci US_DEBUG(pdump(us, (void *)fst, partial)); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci if (partial != 4 || result > USB_STOR_XFER_SHORT) 3808c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_ERROR; 3818c2ecf20Sopenharmony_ci if ((fst->Status & ERR_STAT) != 0) { 3828c2ecf20Sopenharmony_ci usb_stor_dbg(us, "operation failed\n"); 3838c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_FAILED; 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci if ((fst->Reason & 3) != 3) { 3868c2ecf20Sopenharmony_ci usb_stor_dbg(us, "Drive seems still hungry\n"); 3878c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_FAILED; 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci usb_stor_dbg(us, "Transfer happy\n"); 3908c2ecf20Sopenharmony_ci break; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci case DMA_TO_DEVICE: 3938c2ecf20Sopenharmony_ci /* catch bogus "write 0 length" case */ 3948c2ecf20Sopenharmony_ci if (!length) 3958c2ecf20Sopenharmony_ci break; 3968c2ecf20Sopenharmony_ci /* 3978c2ecf20Sopenharmony_ci * Make sure the status indicates that the device wants to 3988c2ecf20Sopenharmony_ci * send us data. 3998c2ecf20Sopenharmony_ci */ 4008c2ecf20Sopenharmony_ci /* !!IMPLEMENT!! */ 4018c2ecf20Sopenharmony_ci result = freecom_writedata (srb, us, ipipe, opipe, length); 4028c2ecf20Sopenharmony_ci if (result != USB_STOR_TRANSPORT_GOOD) 4038c2ecf20Sopenharmony_ci return result; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci usb_stor_dbg(us, "Waiting for status\n"); 4068c2ecf20Sopenharmony_ci result = usb_stor_bulk_transfer_buf (us, ipipe, fst, 4078c2ecf20Sopenharmony_ci FCM_PACKET_LENGTH, &partial); 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci if (partial != 4 || result > USB_STOR_XFER_SHORT) 4108c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_ERROR; 4118c2ecf20Sopenharmony_ci if ((fst->Status & ERR_STAT) != 0) { 4128c2ecf20Sopenharmony_ci usb_stor_dbg(us, "operation failed\n"); 4138c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_FAILED; 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci if ((fst->Reason & 3) != 3) { 4168c2ecf20Sopenharmony_ci usb_stor_dbg(us, "Drive seems still hungry\n"); 4178c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_FAILED; 4188c2ecf20Sopenharmony_ci } 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci usb_stor_dbg(us, "Transfer happy\n"); 4218c2ecf20Sopenharmony_ci break; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci case DMA_NONE: 4258c2ecf20Sopenharmony_ci /* Easy, do nothing. */ 4268c2ecf20Sopenharmony_ci break; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci default: 4298c2ecf20Sopenharmony_ci /* should never hit here -- filtered in usb.c */ 4308c2ecf20Sopenharmony_ci usb_stor_dbg(us, "freecom unimplemented direction: %d\n", 4318c2ecf20Sopenharmony_ci us->srb->sc_data_direction); 4328c2ecf20Sopenharmony_ci /* Return fail, SCSI seems to handle this better. */ 4338c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_FAILED; 4348c2ecf20Sopenharmony_ci break; 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_GOOD; 4388c2ecf20Sopenharmony_ci} 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_cistatic int init_freecom(struct us_data *us) 4418c2ecf20Sopenharmony_ci{ 4428c2ecf20Sopenharmony_ci int result; 4438c2ecf20Sopenharmony_ci char *buffer = us->iobuf; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci /* 4468c2ecf20Sopenharmony_ci * The DMA-mapped I/O buffer is 64 bytes long, just right for 4478c2ecf20Sopenharmony_ci * all our packets. No need to allocate any extra buffer space. 4488c2ecf20Sopenharmony_ci */ 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci result = usb_stor_control_msg(us, us->recv_ctrl_pipe, 4518c2ecf20Sopenharmony_ci 0x4c, 0xc0, 0x4346, 0x0, buffer, 0x20, 3*HZ); 4528c2ecf20Sopenharmony_ci buffer[32] = '\0'; 4538c2ecf20Sopenharmony_ci usb_stor_dbg(us, "String returned from FC init is: %s\n", buffer); 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci /* 4568c2ecf20Sopenharmony_ci * Special thanks to the people at Freecom for providing me with 4578c2ecf20Sopenharmony_ci * this "magic sequence", which they use in their Windows and MacOS 4588c2ecf20Sopenharmony_ci * drivers to make sure that all the attached perhiperals are 4598c2ecf20Sopenharmony_ci * properly reset. 4608c2ecf20Sopenharmony_ci */ 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci /* send reset */ 4638c2ecf20Sopenharmony_ci result = usb_stor_control_msg(us, us->send_ctrl_pipe, 4648c2ecf20Sopenharmony_ci 0x4d, 0x40, 0x24d8, 0x0, NULL, 0x0, 3*HZ); 4658c2ecf20Sopenharmony_ci usb_stor_dbg(us, "result from activate reset is %d\n", result); 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci /* wait 250ms */ 4688c2ecf20Sopenharmony_ci msleep(250); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci /* clear reset */ 4718c2ecf20Sopenharmony_ci result = usb_stor_control_msg(us, us->send_ctrl_pipe, 4728c2ecf20Sopenharmony_ci 0x4d, 0x40, 0x24f8, 0x0, NULL, 0x0, 3*HZ); 4738c2ecf20Sopenharmony_ci usb_stor_dbg(us, "result from clear reset is %d\n", result); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci /* wait 3 seconds */ 4768c2ecf20Sopenharmony_ci msleep(3 * 1000); 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_GOOD; 4798c2ecf20Sopenharmony_ci} 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_cistatic int usb_stor_freecom_reset(struct us_data *us) 4828c2ecf20Sopenharmony_ci{ 4838c2ecf20Sopenharmony_ci printk (KERN_CRIT "freecom reset called\n"); 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci /* We don't really have this feature. */ 4868c2ecf20Sopenharmony_ci return FAILED; 4878c2ecf20Sopenharmony_ci} 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_STORAGE_DEBUG 4908c2ecf20Sopenharmony_cistatic void pdump(struct us_data *us, void *ibuffer, int length) 4918c2ecf20Sopenharmony_ci{ 4928c2ecf20Sopenharmony_ci static char line[80]; 4938c2ecf20Sopenharmony_ci int offset = 0; 4948c2ecf20Sopenharmony_ci unsigned char *buffer = (unsigned char *) ibuffer; 4958c2ecf20Sopenharmony_ci int i, j; 4968c2ecf20Sopenharmony_ci int from, base; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci offset = 0; 4998c2ecf20Sopenharmony_ci for (i = 0; i < length; i++) { 5008c2ecf20Sopenharmony_ci if ((i & 15) == 0) { 5018c2ecf20Sopenharmony_ci if (i > 0) { 5028c2ecf20Sopenharmony_ci offset += sprintf (line+offset, " - "); 5038c2ecf20Sopenharmony_ci for (j = i - 16; j < i; j++) { 5048c2ecf20Sopenharmony_ci if (buffer[j] >= 32 && buffer[j] <= 126) 5058c2ecf20Sopenharmony_ci line[offset++] = buffer[j]; 5068c2ecf20Sopenharmony_ci else 5078c2ecf20Sopenharmony_ci line[offset++] = '.'; 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci line[offset] = 0; 5108c2ecf20Sopenharmony_ci usb_stor_dbg(us, "%s\n", line); 5118c2ecf20Sopenharmony_ci offset = 0; 5128c2ecf20Sopenharmony_ci } 5138c2ecf20Sopenharmony_ci offset += sprintf (line+offset, "%08x:", i); 5148c2ecf20Sopenharmony_ci } else if ((i & 7) == 0) { 5158c2ecf20Sopenharmony_ci offset += sprintf (line+offset, " -"); 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci offset += sprintf (line+offset, " %02x", buffer[i] & 0xff); 5188c2ecf20Sopenharmony_ci } 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci /* Add the last "chunk" of data. */ 5218c2ecf20Sopenharmony_ci from = (length - 1) % 16; 5228c2ecf20Sopenharmony_ci base = ((length - 1) / 16) * 16; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci for (i = from + 1; i < 16; i++) 5258c2ecf20Sopenharmony_ci offset += sprintf (line+offset, " "); 5268c2ecf20Sopenharmony_ci if (from < 8) 5278c2ecf20Sopenharmony_ci offset += sprintf (line+offset, " "); 5288c2ecf20Sopenharmony_ci offset += sprintf (line+offset, " - "); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci for (i = 0; i <= from; i++) { 5318c2ecf20Sopenharmony_ci if (buffer[base+i] >= 32 && buffer[base+i] <= 126) 5328c2ecf20Sopenharmony_ci line[offset++] = buffer[base+i]; 5338c2ecf20Sopenharmony_ci else 5348c2ecf20Sopenharmony_ci line[offset++] = '.'; 5358c2ecf20Sopenharmony_ci } 5368c2ecf20Sopenharmony_ci line[offset] = 0; 5378c2ecf20Sopenharmony_ci usb_stor_dbg(us, "%s\n", line); 5388c2ecf20Sopenharmony_ci offset = 0; 5398c2ecf20Sopenharmony_ci} 5408c2ecf20Sopenharmony_ci#endif 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_cistatic struct scsi_host_template freecom_host_template; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_cistatic int freecom_probe(struct usb_interface *intf, 5458c2ecf20Sopenharmony_ci const struct usb_device_id *id) 5468c2ecf20Sopenharmony_ci{ 5478c2ecf20Sopenharmony_ci struct us_data *us; 5488c2ecf20Sopenharmony_ci int result; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci result = usb_stor_probe1(&us, intf, id, 5518c2ecf20Sopenharmony_ci (id - freecom_usb_ids) + freecom_unusual_dev_list, 5528c2ecf20Sopenharmony_ci &freecom_host_template); 5538c2ecf20Sopenharmony_ci if (result) 5548c2ecf20Sopenharmony_ci return result; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci us->transport_name = "Freecom"; 5578c2ecf20Sopenharmony_ci us->transport = freecom_transport; 5588c2ecf20Sopenharmony_ci us->transport_reset = usb_stor_freecom_reset; 5598c2ecf20Sopenharmony_ci us->max_lun = 0; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci result = usb_stor_probe2(us); 5628c2ecf20Sopenharmony_ci return result; 5638c2ecf20Sopenharmony_ci} 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_cistatic struct usb_driver freecom_driver = { 5668c2ecf20Sopenharmony_ci .name = DRV_NAME, 5678c2ecf20Sopenharmony_ci .probe = freecom_probe, 5688c2ecf20Sopenharmony_ci .disconnect = usb_stor_disconnect, 5698c2ecf20Sopenharmony_ci .suspend = usb_stor_suspend, 5708c2ecf20Sopenharmony_ci .resume = usb_stor_resume, 5718c2ecf20Sopenharmony_ci .reset_resume = usb_stor_reset_resume, 5728c2ecf20Sopenharmony_ci .pre_reset = usb_stor_pre_reset, 5738c2ecf20Sopenharmony_ci .post_reset = usb_stor_post_reset, 5748c2ecf20Sopenharmony_ci .id_table = freecom_usb_ids, 5758c2ecf20Sopenharmony_ci .soft_unbind = 1, 5768c2ecf20Sopenharmony_ci .no_dynamic_id = 1, 5778c2ecf20Sopenharmony_ci}; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_cimodule_usb_stor_driver(freecom_driver, freecom_host_template, DRV_NAME); 580