Creation of Cybook 2416 (actually Gen4) repository
This commit is contained in:
161
drivers/usb/storage/Kconfig
Normal file
161
drivers/usb/storage/Kconfig
Normal file
@@ -0,0 +1,161 @@
|
||||
#
|
||||
# USB Storage driver configuration
|
||||
#
|
||||
|
||||
comment "NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support'"
|
||||
comment "may also be needed; see USB_STORAGE Help for more information"
|
||||
depends on USB
|
||||
|
||||
config USB_STORAGE
|
||||
tristate "USB Mass Storage support"
|
||||
depends on USB && SCSI
|
||||
---help---
|
||||
Say Y here if you want to connect USB mass storage devices to your
|
||||
computer's USB port. This is the driver you need for USB
|
||||
floppy drives, USB hard disks, USB tape drives, USB CD-ROMs,
|
||||
USB flash devices, and memory sticks, along with
|
||||
similar devices. This driver may also be used for some cameras
|
||||
and card readers.
|
||||
|
||||
This option depends on 'SCSI' support being enabled, but you
|
||||
probably also need 'SCSI device support: SCSI disk support'
|
||||
(BLK_DEV_SD) for most USB storage devices.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called usb-storage.
|
||||
|
||||
config USB_STORAGE_DEBUG
|
||||
bool "USB Mass Storage verbose debug"
|
||||
depends on USB_STORAGE
|
||||
help
|
||||
Say Y here in order to have the USB Mass Storage code generate
|
||||
verbose debugging messages.
|
||||
|
||||
config USB_STORAGE_DATAFAB
|
||||
bool "Datafab Compact Flash Reader support (EXPERIMENTAL)"
|
||||
depends on USB_STORAGE && EXPERIMENTAL
|
||||
help
|
||||
Support for certain Datafab CompactFlash readers.
|
||||
Datafab has a web page at <http://www.datafabusa.com/>.
|
||||
|
||||
config USB_STORAGE_FREECOM
|
||||
bool "Freecom USB/ATAPI Bridge support"
|
||||
depends on USB_STORAGE
|
||||
help
|
||||
Support for the Freecom USB to IDE/ATAPI adaptor.
|
||||
Freecom has a web page at <http://www.freecom.de/>.
|
||||
|
||||
config USB_STORAGE_ISD200
|
||||
bool "ISD-200 USB/ATA Bridge support"
|
||||
depends on USB_STORAGE
|
||||
depends on BLK_DEV_IDE=y || BLK_DEV_IDE=USB_STORAGE
|
||||
---help---
|
||||
Say Y here if you want to use USB Mass Store devices based
|
||||
on the In-Systems Design ISD-200 USB/ATA bridge.
|
||||
|
||||
Some of the products that use this chip are:
|
||||
|
||||
- Archos Jukebox 6000
|
||||
- ISD SmartCable for Storage
|
||||
- Taiwan Skymaster CD530U/DEL-0241 IDE bridge
|
||||
- Sony CRX10U CD-R/RW drive
|
||||
- CyQ've CQ8060A CDRW drive
|
||||
- Planex eXtreme Drive RX-25HU USB-IDE cable (not model RX-25U)
|
||||
|
||||
config USB_STORAGE_DPCM
|
||||
bool "Microtech/ZiO! CompactFlash/SmartMedia support"
|
||||
depends on USB_STORAGE
|
||||
help
|
||||
Say Y here to support the Microtech/ZiO! CompactFlash reader.
|
||||
There is a web page at <http://www.ziocorp.com/products/>.
|
||||
|
||||
config USB_STORAGE_USBAT
|
||||
bool "USBAT/USBAT02-based storage support (EXPERIMENTAL)"
|
||||
depends on USB_STORAGE && EXPERIMENTAL
|
||||
help
|
||||
Say Y here to include additional code to support storage devices
|
||||
based on the SCM/Shuttle USBAT/USBAT02 processors.
|
||||
|
||||
Devices reported to work with this driver include:
|
||||
- CompactFlash reader included with Kodak DC3800 camera
|
||||
- Dane-Elec Zmate CompactFlash reader
|
||||
- Delkin Efilm reader2
|
||||
- HP 8200e/8210e/8230e CD-Writer Plus drives
|
||||
- I-JAM JS-50U
|
||||
- Jessops CompactFlash JESDCFRU BLACK
|
||||
- Kingston Technology PCREAD-USB/CF
|
||||
- Maxell UA4 CompactFlash reader
|
||||
- Memorex UCF-100
|
||||
- Microtech ZiO! ICS-45 CF2
|
||||
- RCA LYRA MP3 portable
|
||||
- Sandisk ImageMate SDDR-05b
|
||||
|
||||
config USB_STORAGE_SDDR09
|
||||
bool "SanDisk SDDR-09 (and other SmartMedia) support (EXPERIMENTAL)"
|
||||
depends on USB_STORAGE && EXPERIMENTAL
|
||||
help
|
||||
Say Y here to include additional code to support the Sandisk SDDR-09
|
||||
SmartMedia reader in the USB Mass Storage driver.
|
||||
Also works for the Microtech Zio! SmartMedia reader.
|
||||
|
||||
config USB_STORAGE_SDDR55
|
||||
bool "SanDisk SDDR-55 SmartMedia support (EXPERIMENTAL)"
|
||||
depends on USB_STORAGE && EXPERIMENTAL
|
||||
help
|
||||
Say Y here to include additional code to support the Sandisk SDDR-55
|
||||
SmartMedia reader in the USB Mass Storage driver.
|
||||
|
||||
config USB_STORAGE_JUMPSHOT
|
||||
bool "Lexar Jumpshot Compact Flash Reader (EXPERIMENTAL)"
|
||||
depends on USB_STORAGE && EXPERIMENTAL
|
||||
help
|
||||
Say Y here to include additional code to support the Lexar Jumpshot
|
||||
USB CompactFlash reader.
|
||||
|
||||
config USB_STORAGE_ALAUDA
|
||||
bool "Olympus MAUSB-10/Fuji DPC-R1 support (EXPERIMENTAL)"
|
||||
depends on USB_STORAGE && EXPERIMENTAL
|
||||
help
|
||||
Say Y here to include additional code to support the Olympus MAUSB-10
|
||||
and Fujifilm DPC-R1 USB Card reader/writer devices.
|
||||
|
||||
These devices are based on the Alauda chip and support both
|
||||
XD and SmartMedia cards.
|
||||
|
||||
config USB_STORAGE_ONETOUCH
|
||||
bool "Support OneTouch Button on Maxtor Hard Drives (EXPERIMENTAL)"
|
||||
depends on USB_STORAGE && INPUT_EVDEV && EXPERIMENTAL && !PM
|
||||
help
|
||||
Say Y here to include additional code to support the Maxtor OneTouch
|
||||
USB hard drive's onetouch button.
|
||||
|
||||
This code registers the button on the front of Maxtor OneTouch USB
|
||||
hard drive's as an input device. An action can be associated with
|
||||
this input in any keybinding software. (e.g. gnome's keyboard short-
|
||||
cuts)
|
||||
|
||||
config USB_STORAGE_KARMA
|
||||
bool "Support for Rio Karma music player"
|
||||
depends on USB_STORAGE
|
||||
help
|
||||
Say Y here to include additional code to support the Rio Karma
|
||||
USB interface.
|
||||
|
||||
This code places the Rio Karma into mass storage mode, enabling
|
||||
it to be mounted as an ordinary filesystem. Performing an eject
|
||||
on the resulting scsi device node returns the Karma to normal
|
||||
operation.
|
||||
|
||||
config USB_LIBUSUAL
|
||||
bool "The shared table of common (or usual) storage devices"
|
||||
depends on USB
|
||||
help
|
||||
This module contains a table of common (or usual) devices
|
||||
for usb-storage and ub drivers, and allows to switch binding
|
||||
of these devices without rebuilding modules.
|
||||
|
||||
Typical syntax of /etc/modprobe.conf is:
|
||||
|
||||
options libusual bias="ub"
|
||||
|
||||
If unsure, say N.
|
||||
30
drivers/usb/storage/Makefile
Normal file
30
drivers/usb/storage/Makefile
Normal file
@@ -0,0 +1,30 @@
|
||||
#
|
||||
# Makefile for the USB Mass Storage device drivers.
|
||||
#
|
||||
# 15 Aug 2000, Christoph Hellwig <hch@infradead.org>
|
||||
# Rewritten to use lists instead of if-statements.
|
||||
#
|
||||
|
||||
EXTRA_CFLAGS := -Idrivers/scsi
|
||||
|
||||
obj-$(CONFIG_USB_STORAGE) += usb-storage.o
|
||||
|
||||
usb-storage-obj-$(CONFIG_USB_STORAGE_DEBUG) += debug.o
|
||||
usb-storage-obj-$(CONFIG_USB_STORAGE_USBAT) += shuttle_usbat.o
|
||||
usb-storage-obj-$(CONFIG_USB_STORAGE_SDDR09) += sddr09.o
|
||||
usb-storage-obj-$(CONFIG_USB_STORAGE_SDDR55) += sddr55.o
|
||||
usb-storage-obj-$(CONFIG_USB_STORAGE_FREECOM) += freecom.o
|
||||
usb-storage-obj-$(CONFIG_USB_STORAGE_DPCM) += dpcm.o
|
||||
usb-storage-obj-$(CONFIG_USB_STORAGE_ISD200) += isd200.o
|
||||
usb-storage-obj-$(CONFIG_USB_STORAGE_DATAFAB) += datafab.o
|
||||
usb-storage-obj-$(CONFIG_USB_STORAGE_JUMPSHOT) += jumpshot.o
|
||||
usb-storage-obj-$(CONFIG_USB_STORAGE_ALAUDA) += alauda.o
|
||||
usb-storage-obj-$(CONFIG_USB_STORAGE_ONETOUCH) += onetouch.o
|
||||
usb-storage-obj-$(CONFIG_USB_STORAGE_KARMA) += karma.o
|
||||
|
||||
usb-storage-objs := scsiglue.o protocol.o transport.o usb.o \
|
||||
initializers.o $(usb-storage-obj-y)
|
||||
|
||||
ifneq ($(CONFIG_USB_LIBUSUAL),)
|
||||
obj-$(CONFIG_USB) += libusual.o
|
||||
endif
|
||||
1119
drivers/usb/storage/alauda.c
Normal file
1119
drivers/usb/storage/alauda.c
Normal file
File diff suppressed because it is too large
Load Diff
100
drivers/usb/storage/alauda.h
Normal file
100
drivers/usb/storage/alauda.h
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Driver for Alauda-based card readers
|
||||
*
|
||||
* Current development and maintenance by:
|
||||
* (c) 2005 Daniel Drake <dsd@gentoo.org>
|
||||
*
|
||||
* See alauda.c for more explanation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef _USB_ALAUDA_H
|
||||
#define _USB_ALAUDA_H
|
||||
|
||||
/*
|
||||
* Status bytes
|
||||
*/
|
||||
#define ALAUDA_STATUS_ERROR 0x01
|
||||
#define ALAUDA_STATUS_READY 0x40
|
||||
|
||||
/*
|
||||
* Control opcodes (for request field)
|
||||
*/
|
||||
#define ALAUDA_GET_XD_MEDIA_STATUS 0x08
|
||||
#define ALAUDA_GET_SM_MEDIA_STATUS 0x98
|
||||
#define ALAUDA_ACK_XD_MEDIA_CHANGE 0x0a
|
||||
#define ALAUDA_ACK_SM_MEDIA_CHANGE 0x9a
|
||||
#define ALAUDA_GET_XD_MEDIA_SIG 0x86
|
||||
#define ALAUDA_GET_SM_MEDIA_SIG 0x96
|
||||
|
||||
/*
|
||||
* Bulk command identity (byte 0)
|
||||
*/
|
||||
#define ALAUDA_BULK_CMD 0x40
|
||||
|
||||
/*
|
||||
* Bulk opcodes (byte 1)
|
||||
*/
|
||||
#define ALAUDA_BULK_GET_REDU_DATA 0x85
|
||||
#define ALAUDA_BULK_READ_BLOCK 0x94
|
||||
#define ALAUDA_BULK_ERASE_BLOCK 0xa3
|
||||
#define ALAUDA_BULK_WRITE_BLOCK 0xb4
|
||||
#define ALAUDA_BULK_GET_STATUS2 0xb7
|
||||
#define ALAUDA_BULK_RESET_MEDIA 0xe0
|
||||
|
||||
/*
|
||||
* Port to operate on (byte 8)
|
||||
*/
|
||||
#define ALAUDA_PORT_XD 0x00
|
||||
#define ALAUDA_PORT_SM 0x01
|
||||
|
||||
/*
|
||||
* LBA and PBA are unsigned ints. Special values.
|
||||
*/
|
||||
#define UNDEF 0xffff
|
||||
#define SPARE 0xfffe
|
||||
#define UNUSABLE 0xfffd
|
||||
|
||||
int init_alauda(struct us_data *us);
|
||||
int alauda_transport(struct scsi_cmnd *srb, struct us_data *us);
|
||||
|
||||
struct alauda_media_info {
|
||||
unsigned long capacity; /* total media size in bytes */
|
||||
unsigned int pagesize; /* page size in bytes */
|
||||
unsigned int blocksize; /* number of pages per block */
|
||||
unsigned int uzonesize; /* number of usable blocks per zone */
|
||||
unsigned int zonesize; /* number of blocks per zone */
|
||||
unsigned int blockmask; /* mask to get page from address */
|
||||
|
||||
unsigned char pageshift;
|
||||
unsigned char blockshift;
|
||||
unsigned char zoneshift;
|
||||
|
||||
u16 **lba_to_pba; /* logical to physical block map */
|
||||
u16 **pba_to_lba; /* physical to logical block map */
|
||||
};
|
||||
|
||||
struct alauda_info {
|
||||
struct alauda_media_info port[2];
|
||||
int wr_ep; /* endpoint to write data out of */
|
||||
|
||||
unsigned char sense_key;
|
||||
unsigned long sense_asc; /* additional sense code */
|
||||
unsigned long sense_ascq; /* additional sense code qualifier */
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
667
drivers/usb/storage/datafab.c
Normal file
667
drivers/usb/storage/datafab.c
Normal file
@@ -0,0 +1,667 @@
|
||||
/* Driver for Datafab USB Compact Flash reader
|
||||
*
|
||||
* $Id: datafab.c,v 1.1.1.1 2007/06/12 07:27:09 eyryu Exp $
|
||||
*
|
||||
* datafab driver v0.1:
|
||||
*
|
||||
* First release
|
||||
*
|
||||
* Current development and maintenance by:
|
||||
* (c) 2000 Jimmie Mayfield (mayfield+datafab@sackheads.org)
|
||||
*
|
||||
* Many thanks to Robert Baruch for the SanDisk SmartMedia reader driver
|
||||
* which I used as a template for this driver.
|
||||
*
|
||||
* Some bugfixes and scatter-gather code by Gregory P. Smith
|
||||
* (greg-usb@electricrain.com)
|
||||
*
|
||||
* Fix for media change by Joerg Schneider (js@joergschneider.com)
|
||||
*
|
||||
* Other contributors:
|
||||
* (c) 2002 Alan Stern <stern@rowland.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This driver attempts to support USB CompactFlash reader/writer devices
|
||||
* based on Datafab USB-to-ATA chips. It was specifically developed for the
|
||||
* Datafab MDCFE-B USB CompactFlash reader but has since been found to work
|
||||
* with a variety of Datafab-based devices from a number of manufacturers.
|
||||
* I've received a report of this driver working with a Datafab-based
|
||||
* SmartMedia device though please be aware that I'm personally unable to
|
||||
* test SmartMedia support.
|
||||
*
|
||||
* This driver supports reading and writing. If you're truly paranoid,
|
||||
* however, you can force the driver into a write-protected state by setting
|
||||
* the WP enable bits in datafab_handle_mode_sense(). See the comments
|
||||
* in that routine.
|
||||
*/
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <scsi/scsi.h>
|
||||
#include <scsi/scsi_cmnd.h>
|
||||
|
||||
#include "usb.h"
|
||||
#include "transport.h"
|
||||
#include "protocol.h"
|
||||
#include "debug.h"
|
||||
#include "datafab.h"
|
||||
|
||||
static int datafab_determine_lun(struct us_data *us,
|
||||
struct datafab_info *info);
|
||||
|
||||
|
||||
static inline int
|
||||
datafab_bulk_read(struct us_data *us, unsigned char *data, unsigned int len) {
|
||||
if (len == 0)
|
||||
return USB_STOR_XFER_GOOD;
|
||||
|
||||
US_DEBUGP("datafab_bulk_read: len = %d\n", len);
|
||||
return usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe,
|
||||
data, len, NULL);
|
||||
}
|
||||
|
||||
|
||||
static inline int
|
||||
datafab_bulk_write(struct us_data *us, unsigned char *data, unsigned int len) {
|
||||
if (len == 0)
|
||||
return USB_STOR_XFER_GOOD;
|
||||
|
||||
US_DEBUGP("datafab_bulk_write: len = %d\n", len);
|
||||
return usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe,
|
||||
data, len, NULL);
|
||||
}
|
||||
|
||||
|
||||
static int datafab_read_data(struct us_data *us,
|
||||
struct datafab_info *info,
|
||||
u32 sector,
|
||||
u32 sectors)
|
||||
{
|
||||
unsigned char *command = us->iobuf;
|
||||
unsigned char *buffer;
|
||||
unsigned char thistime;
|
||||
unsigned int totallen, alloclen;
|
||||
int len, result;
|
||||
unsigned int sg_idx = 0, sg_offset = 0;
|
||||
|
||||
// we're working in LBA mode. according to the ATA spec,
|
||||
// we can support up to 28-bit addressing. I don't know if Datafab
|
||||
// supports beyond 24-bit addressing. It's kind of hard to test
|
||||
// since it requires > 8GB CF card.
|
||||
//
|
||||
if (sectors > 0x0FFFFFFF)
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
|
||||
if (info->lun == -1) {
|
||||
result = datafab_determine_lun(us, info);
|
||||
if (result != USB_STOR_TRANSPORT_GOOD)
|
||||
return result;
|
||||
}
|
||||
|
||||
totallen = sectors * info->ssize;
|
||||
|
||||
// Since we don't read more than 64 KB at a time, we have to create
|
||||
// a bounce buffer and move the data a piece at a time between the
|
||||
// bounce buffer and the actual transfer buffer.
|
||||
|
||||
alloclen = min(totallen, 65536u);
|
||||
buffer = kmalloc(alloclen, GFP_NOIO);
|
||||
if (buffer == NULL)
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
|
||||
do {
|
||||
// loop, never allocate or transfer more than 64k at once
|
||||
// (min(128k, 255*info->ssize) is the real limit)
|
||||
|
||||
len = min(totallen, alloclen);
|
||||
thistime = (len / info->ssize) & 0xff;
|
||||
|
||||
command[0] = 0;
|
||||
command[1] = thistime;
|
||||
command[2] = sector & 0xFF;
|
||||
command[3] = (sector >> 8) & 0xFF;
|
||||
command[4] = (sector >> 16) & 0xFF;
|
||||
|
||||
command[5] = 0xE0 + (info->lun << 4);
|
||||
command[5] |= (sector >> 24) & 0x0F;
|
||||
command[6] = 0x20;
|
||||
command[7] = 0x01;
|
||||
|
||||
// send the read command
|
||||
result = datafab_bulk_write(us, command, 8);
|
||||
if (result != USB_STOR_XFER_GOOD)
|
||||
goto leave;
|
||||
|
||||
// read the result
|
||||
result = datafab_bulk_read(us, buffer, len);
|
||||
if (result != USB_STOR_XFER_GOOD)
|
||||
goto leave;
|
||||
|
||||
// Store the data in the transfer buffer
|
||||
usb_stor_access_xfer_buf(buffer, len, us->srb,
|
||||
&sg_idx, &sg_offset, TO_XFER_BUF);
|
||||
|
||||
sector += thistime;
|
||||
totallen -= len;
|
||||
} while (totallen > 0);
|
||||
|
||||
kfree(buffer);
|
||||
return USB_STOR_TRANSPORT_GOOD;
|
||||
|
||||
leave:
|
||||
kfree(buffer);
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
}
|
||||
|
||||
|
||||
static int datafab_write_data(struct us_data *us,
|
||||
struct datafab_info *info,
|
||||
u32 sector,
|
||||
u32 sectors)
|
||||
{
|
||||
unsigned char *command = us->iobuf;
|
||||
unsigned char *reply = us->iobuf;
|
||||
unsigned char *buffer;
|
||||
unsigned char thistime;
|
||||
unsigned int totallen, alloclen;
|
||||
int len, result;
|
||||
unsigned int sg_idx = 0, sg_offset = 0;
|
||||
|
||||
// we're working in LBA mode. according to the ATA spec,
|
||||
// we can support up to 28-bit addressing. I don't know if Datafab
|
||||
// supports beyond 24-bit addressing. It's kind of hard to test
|
||||
// since it requires > 8GB CF card.
|
||||
//
|
||||
if (sectors > 0x0FFFFFFF)
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
|
||||
if (info->lun == -1) {
|
||||
result = datafab_determine_lun(us, info);
|
||||
if (result != USB_STOR_TRANSPORT_GOOD)
|
||||
return result;
|
||||
}
|
||||
|
||||
totallen = sectors * info->ssize;
|
||||
|
||||
// Since we don't write more than 64 KB at a time, we have to create
|
||||
// a bounce buffer and move the data a piece at a time between the
|
||||
// bounce buffer and the actual transfer buffer.
|
||||
|
||||
alloclen = min(totallen, 65536u);
|
||||
buffer = kmalloc(alloclen, GFP_NOIO);
|
||||
if (buffer == NULL)
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
|
||||
do {
|
||||
// loop, never allocate or transfer more than 64k at once
|
||||
// (min(128k, 255*info->ssize) is the real limit)
|
||||
|
||||
len = min(totallen, alloclen);
|
||||
thistime = (len / info->ssize) & 0xff;
|
||||
|
||||
// Get the data from the transfer buffer
|
||||
usb_stor_access_xfer_buf(buffer, len, us->srb,
|
||||
&sg_idx, &sg_offset, FROM_XFER_BUF);
|
||||
|
||||
command[0] = 0;
|
||||
command[1] = thistime;
|
||||
command[2] = sector & 0xFF;
|
||||
command[3] = (sector >> 8) & 0xFF;
|
||||
command[4] = (sector >> 16) & 0xFF;
|
||||
|
||||
command[5] = 0xE0 + (info->lun << 4);
|
||||
command[5] |= (sector >> 24) & 0x0F;
|
||||
command[6] = 0x30;
|
||||
command[7] = 0x02;
|
||||
|
||||
// send the command
|
||||
result = datafab_bulk_write(us, command, 8);
|
||||
if (result != USB_STOR_XFER_GOOD)
|
||||
goto leave;
|
||||
|
||||
// send the data
|
||||
result = datafab_bulk_write(us, buffer, len);
|
||||
if (result != USB_STOR_XFER_GOOD)
|
||||
goto leave;
|
||||
|
||||
// read the result
|
||||
result = datafab_bulk_read(us, reply, 2);
|
||||
if (result != USB_STOR_XFER_GOOD)
|
||||
goto leave;
|
||||
|
||||
if (reply[0] != 0x50 && reply[1] != 0) {
|
||||
US_DEBUGP("datafab_write_data: Gah! "
|
||||
"write return code: %02x %02x\n",
|
||||
reply[0], reply[1]);
|
||||
result = USB_STOR_TRANSPORT_ERROR;
|
||||
goto leave;
|
||||
}
|
||||
|
||||
sector += thistime;
|
||||
totallen -= len;
|
||||
} while (totallen > 0);
|
||||
|
||||
kfree(buffer);
|
||||
return USB_STOR_TRANSPORT_GOOD;
|
||||
|
||||
leave:
|
||||
kfree(buffer);
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
}
|
||||
|
||||
|
||||
static int datafab_determine_lun(struct us_data *us,
|
||||
struct datafab_info *info)
|
||||
{
|
||||
// Dual-slot readers can be thought of as dual-LUN devices.
|
||||
// We need to determine which card slot is being used.
|
||||
// We'll send an IDENTIFY DEVICE command and see which LUN responds...
|
||||
//
|
||||
// There might be a better way of doing this?
|
||||
|
||||
static unsigned char scommand[8] = { 0, 1, 0, 0, 0, 0xa0, 0xec, 1 };
|
||||
unsigned char *command = us->iobuf;
|
||||
unsigned char *buf;
|
||||
int count = 0, rc;
|
||||
|
||||
if (!us || !info)
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
|
||||
memcpy(command, scommand, 8);
|
||||
buf = kmalloc(512, GFP_NOIO);
|
||||
if (!buf)
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
|
||||
US_DEBUGP("datafab_determine_lun: locating...\n");
|
||||
|
||||
// we'll try 3 times before giving up...
|
||||
//
|
||||
while (count++ < 3) {
|
||||
command[5] = 0xa0;
|
||||
|
||||
rc = datafab_bulk_write(us, command, 8);
|
||||
if (rc != USB_STOR_XFER_GOOD) {
|
||||
rc = USB_STOR_TRANSPORT_ERROR;
|
||||
goto leave;
|
||||
}
|
||||
|
||||
rc = datafab_bulk_read(us, buf, 512);
|
||||
if (rc == USB_STOR_XFER_GOOD) {
|
||||
info->lun = 0;
|
||||
rc = USB_STOR_TRANSPORT_GOOD;
|
||||
goto leave;
|
||||
}
|
||||
|
||||
command[5] = 0xb0;
|
||||
|
||||
rc = datafab_bulk_write(us, command, 8);
|
||||
if (rc != USB_STOR_XFER_GOOD) {
|
||||
rc = USB_STOR_TRANSPORT_ERROR;
|
||||
goto leave;
|
||||
}
|
||||
|
||||
rc = datafab_bulk_read(us, buf, 512);
|
||||
if (rc == USB_STOR_XFER_GOOD) {
|
||||
info->lun = 1;
|
||||
rc = USB_STOR_TRANSPORT_GOOD;
|
||||
goto leave;
|
||||
}
|
||||
|
||||
msleep(20);
|
||||
}
|
||||
|
||||
rc = USB_STOR_TRANSPORT_ERROR;
|
||||
|
||||
leave:
|
||||
kfree(buf);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int datafab_id_device(struct us_data *us,
|
||||
struct datafab_info *info)
|
||||
{
|
||||
// this is a variation of the ATA "IDENTIFY DEVICE" command...according
|
||||
// to the ATA spec, 'Sector Count' isn't used but the Windows driver
|
||||
// sets this bit so we do too...
|
||||
//
|
||||
static unsigned char scommand[8] = { 0, 1, 0, 0, 0, 0xa0, 0xec, 1 };
|
||||
unsigned char *command = us->iobuf;
|
||||
unsigned char *reply;
|
||||
int rc;
|
||||
|
||||
if (!us || !info)
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
|
||||
if (info->lun == -1) {
|
||||
rc = datafab_determine_lun(us, info);
|
||||
if (rc != USB_STOR_TRANSPORT_GOOD)
|
||||
return rc;
|
||||
}
|
||||
|
||||
memcpy(command, scommand, 8);
|
||||
reply = kmalloc(512, GFP_NOIO);
|
||||
if (!reply)
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
|
||||
command[5] += (info->lun << 4);
|
||||
|
||||
rc = datafab_bulk_write(us, command, 8);
|
||||
if (rc != USB_STOR_XFER_GOOD) {
|
||||
rc = USB_STOR_TRANSPORT_ERROR;
|
||||
goto leave;
|
||||
}
|
||||
|
||||
// we'll go ahead and extract the media capacity while we're here...
|
||||
//
|
||||
rc = datafab_bulk_read(us, reply, 512);
|
||||
if (rc == USB_STOR_XFER_GOOD) {
|
||||
// capacity is at word offset 57-58
|
||||
//
|
||||
info->sectors = ((u32)(reply[117]) << 24) |
|
||||
((u32)(reply[116]) << 16) |
|
||||
((u32)(reply[115]) << 8) |
|
||||
((u32)(reply[114]) );
|
||||
rc = USB_STOR_TRANSPORT_GOOD;
|
||||
goto leave;
|
||||
}
|
||||
|
||||
rc = USB_STOR_TRANSPORT_ERROR;
|
||||
|
||||
leave:
|
||||
kfree(reply);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static int datafab_handle_mode_sense(struct us_data *us,
|
||||
struct scsi_cmnd * srb,
|
||||
int sense_6)
|
||||
{
|
||||
static unsigned char rw_err_page[12] = {
|
||||
0x1, 0xA, 0x21, 1, 0, 0, 0, 0, 1, 0, 0, 0
|
||||
};
|
||||
static unsigned char cache_page[12] = {
|
||||
0x8, 0xA, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
static unsigned char rbac_page[12] = {
|
||||
0x1B, 0xA, 0, 0x81, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
static unsigned char timer_page[8] = {
|
||||
0x1C, 0x6, 0, 0, 0, 0
|
||||
};
|
||||
unsigned char pc, page_code;
|
||||
unsigned int i = 0;
|
||||
struct datafab_info *info = (struct datafab_info *) (us->extra);
|
||||
unsigned char *ptr = us->iobuf;
|
||||
|
||||
// most of this stuff is just a hack to get things working. the
|
||||
// datafab reader doesn't present a SCSI interface so we
|
||||
// fudge the SCSI commands...
|
||||
//
|
||||
|
||||
pc = srb->cmnd[2] >> 6;
|
||||
page_code = srb->cmnd[2] & 0x3F;
|
||||
|
||||
switch (pc) {
|
||||
case 0x0:
|
||||
US_DEBUGP("datafab_handle_mode_sense: Current values\n");
|
||||
break;
|
||||
case 0x1:
|
||||
US_DEBUGP("datafab_handle_mode_sense: Changeable values\n");
|
||||
break;
|
||||
case 0x2:
|
||||
US_DEBUGP("datafab_handle_mode_sense: Default values\n");
|
||||
break;
|
||||
case 0x3:
|
||||
US_DEBUGP("datafab_handle_mode_sense: Saves values\n");
|
||||
break;
|
||||
}
|
||||
|
||||
memset(ptr, 0, 8);
|
||||
if (sense_6) {
|
||||
ptr[2] = 0x00; // WP enable: 0x80
|
||||
i = 4;
|
||||
} else {
|
||||
ptr[3] = 0x00; // WP enable: 0x80
|
||||
i = 8;
|
||||
}
|
||||
|
||||
switch (page_code) {
|
||||
default:
|
||||
// vendor-specific mode
|
||||
info->sense_key = 0x05;
|
||||
info->sense_asc = 0x24;
|
||||
info->sense_ascq = 0x00;
|
||||
return USB_STOR_TRANSPORT_FAILED;
|
||||
|
||||
case 0x1:
|
||||
memcpy(ptr + i, rw_err_page, sizeof(rw_err_page));
|
||||
i += sizeof(rw_err_page);
|
||||
break;
|
||||
|
||||
case 0x8:
|
||||
memcpy(ptr + i, cache_page, sizeof(cache_page));
|
||||
i += sizeof(cache_page);
|
||||
break;
|
||||
|
||||
case 0x1B:
|
||||
memcpy(ptr + i, rbac_page, sizeof(rbac_page));
|
||||
i += sizeof(rbac_page);
|
||||
break;
|
||||
|
||||
case 0x1C:
|
||||
memcpy(ptr + i, timer_page, sizeof(timer_page));
|
||||
i += sizeof(timer_page);
|
||||
break;
|
||||
|
||||
case 0x3F: // retrieve all pages
|
||||
memcpy(ptr + i, timer_page, sizeof(timer_page));
|
||||
i += sizeof(timer_page);
|
||||
memcpy(ptr + i, rbac_page, sizeof(rbac_page));
|
||||
i += sizeof(rbac_page);
|
||||
memcpy(ptr + i, cache_page, sizeof(cache_page));
|
||||
i += sizeof(cache_page);
|
||||
memcpy(ptr + i, rw_err_page, sizeof(rw_err_page));
|
||||
i += sizeof(rw_err_page);
|
||||
break;
|
||||
}
|
||||
|
||||
if (sense_6)
|
||||
ptr[0] = i - 1;
|
||||
else
|
||||
((__be16 *) ptr)[0] = cpu_to_be16(i - 2);
|
||||
usb_stor_set_xfer_buf(ptr, i, srb);
|
||||
|
||||
return USB_STOR_TRANSPORT_GOOD;
|
||||
}
|
||||
|
||||
static void datafab_info_destructor(void *extra)
|
||||
{
|
||||
// this routine is a placeholder...
|
||||
// currently, we don't allocate any extra memory so we're okay
|
||||
}
|
||||
|
||||
|
||||
// Transport for the Datafab MDCFE-B
|
||||
//
|
||||
int datafab_transport(struct scsi_cmnd * srb, struct us_data *us)
|
||||
{
|
||||
struct datafab_info *info;
|
||||
int rc;
|
||||
unsigned long block, blocks;
|
||||
unsigned char *ptr = us->iobuf;
|
||||
static unsigned char inquiry_reply[8] = {
|
||||
0x00, 0x80, 0x00, 0x01, 0x1F, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
if (!us->extra) {
|
||||
us->extra = kzalloc(sizeof(struct datafab_info), GFP_NOIO);
|
||||
if (!us->extra) {
|
||||
US_DEBUGP("datafab_transport: Gah! "
|
||||
"Can't allocate storage for Datafab info struct!\n");
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
}
|
||||
us->extra_destructor = datafab_info_destructor;
|
||||
((struct datafab_info *)us->extra)->lun = -1;
|
||||
}
|
||||
|
||||
info = (struct datafab_info *) (us->extra);
|
||||
|
||||
if (srb->cmnd[0] == INQUIRY) {
|
||||
US_DEBUGP("datafab_transport: INQUIRY. Returning bogus response");
|
||||
memcpy(ptr, inquiry_reply, sizeof(inquiry_reply));
|
||||
fill_inquiry_response(us, ptr, 36);
|
||||
return USB_STOR_TRANSPORT_GOOD;
|
||||
}
|
||||
|
||||
if (srb->cmnd[0] == READ_CAPACITY) {
|
||||
info->ssize = 0x200; // hard coded 512 byte sectors as per ATA spec
|
||||
rc = datafab_id_device(us, info);
|
||||
if (rc != USB_STOR_TRANSPORT_GOOD)
|
||||
return rc;
|
||||
|
||||
US_DEBUGP("datafab_transport: READ_CAPACITY: %ld sectors, %ld bytes per sector\n",
|
||||
info->sectors, info->ssize);
|
||||
|
||||
// build the reply
|
||||
// we need the last sector, not the number of sectors
|
||||
((__be32 *) ptr)[0] = cpu_to_be32(info->sectors - 1);
|
||||
((__be32 *) ptr)[1] = cpu_to_be32(info->ssize);
|
||||
usb_stor_set_xfer_buf(ptr, 8, srb);
|
||||
|
||||
return USB_STOR_TRANSPORT_GOOD;
|
||||
}
|
||||
|
||||
if (srb->cmnd[0] == MODE_SELECT_10) {
|
||||
US_DEBUGP("datafab_transport: Gah! MODE_SELECT_10.\n");
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
}
|
||||
|
||||
// don't bother implementing READ_6 or WRITE_6.
|
||||
//
|
||||
if (srb->cmnd[0] == READ_10) {
|
||||
block = ((u32)(srb->cmnd[2]) << 24) | ((u32)(srb->cmnd[3]) << 16) |
|
||||
((u32)(srb->cmnd[4]) << 8) | ((u32)(srb->cmnd[5]));
|
||||
|
||||
blocks = ((u32)(srb->cmnd[7]) << 8) | ((u32)(srb->cmnd[8]));
|
||||
|
||||
US_DEBUGP("datafab_transport: READ_10: read block 0x%04lx count %ld\n", block, blocks);
|
||||
return datafab_read_data(us, info, block, blocks);
|
||||
}
|
||||
|
||||
if (srb->cmnd[0] == READ_12) {
|
||||
// we'll probably never see a READ_12 but we'll do it anyway...
|
||||
//
|
||||
block = ((u32)(srb->cmnd[2]) << 24) | ((u32)(srb->cmnd[3]) << 16) |
|
||||
((u32)(srb->cmnd[4]) << 8) | ((u32)(srb->cmnd[5]));
|
||||
|
||||
blocks = ((u32)(srb->cmnd[6]) << 24) | ((u32)(srb->cmnd[7]) << 16) |
|
||||
((u32)(srb->cmnd[8]) << 8) | ((u32)(srb->cmnd[9]));
|
||||
|
||||
US_DEBUGP("datafab_transport: READ_12: read block 0x%04lx count %ld\n", block, blocks);
|
||||
return datafab_read_data(us, info, block, blocks);
|
||||
}
|
||||
|
||||
if (srb->cmnd[0] == WRITE_10) {
|
||||
block = ((u32)(srb->cmnd[2]) << 24) | ((u32)(srb->cmnd[3]) << 16) |
|
||||
((u32)(srb->cmnd[4]) << 8) | ((u32)(srb->cmnd[5]));
|
||||
|
||||
blocks = ((u32)(srb->cmnd[7]) << 8) | ((u32)(srb->cmnd[8]));
|
||||
|
||||
US_DEBUGP("datafab_transport: WRITE_10: write block 0x%04lx count %ld\n", block, blocks);
|
||||
return datafab_write_data(us, info, block, blocks);
|
||||
}
|
||||
|
||||
if (srb->cmnd[0] == WRITE_12) {
|
||||
// we'll probably never see a WRITE_12 but we'll do it anyway...
|
||||
//
|
||||
block = ((u32)(srb->cmnd[2]) << 24) | ((u32)(srb->cmnd[3]) << 16) |
|
||||
((u32)(srb->cmnd[4]) << 8) | ((u32)(srb->cmnd[5]));
|
||||
|
||||
blocks = ((u32)(srb->cmnd[6]) << 24) | ((u32)(srb->cmnd[7]) << 16) |
|
||||
((u32)(srb->cmnd[8]) << 8) | ((u32)(srb->cmnd[9]));
|
||||
|
||||
US_DEBUGP("datafab_transport: WRITE_12: write block 0x%04lx count %ld\n", block, blocks);
|
||||
return datafab_write_data(us, info, block, blocks);
|
||||
}
|
||||
|
||||
if (srb->cmnd[0] == TEST_UNIT_READY) {
|
||||
US_DEBUGP("datafab_transport: TEST_UNIT_READY.\n");
|
||||
return datafab_id_device(us, info);
|
||||
}
|
||||
|
||||
if (srb->cmnd[0] == REQUEST_SENSE) {
|
||||
US_DEBUGP("datafab_transport: REQUEST_SENSE. Returning faked response\n");
|
||||
|
||||
// this response is pretty bogus right now. eventually if necessary
|
||||
// we can set the correct sense data. so far though it hasn't been
|
||||
// necessary
|
||||
//
|
||||
memset(ptr, 0, 18);
|
||||
ptr[0] = 0xF0;
|
||||
ptr[2] = info->sense_key;
|
||||
ptr[7] = 11;
|
||||
ptr[12] = info->sense_asc;
|
||||
ptr[13] = info->sense_ascq;
|
||||
usb_stor_set_xfer_buf(ptr, 18, srb);
|
||||
|
||||
return USB_STOR_TRANSPORT_GOOD;
|
||||
}
|
||||
|
||||
if (srb->cmnd[0] == MODE_SENSE) {
|
||||
US_DEBUGP("datafab_transport: MODE_SENSE_6 detected\n");
|
||||
return datafab_handle_mode_sense(us, srb, 1);
|
||||
}
|
||||
|
||||
if (srb->cmnd[0] == MODE_SENSE_10) {
|
||||
US_DEBUGP("datafab_transport: MODE_SENSE_10 detected\n");
|
||||
return datafab_handle_mode_sense(us, srb, 0);
|
||||
}
|
||||
|
||||
if (srb->cmnd[0] == ALLOW_MEDIUM_REMOVAL) {
|
||||
// sure. whatever. not like we can stop the user from
|
||||
// popping the media out of the device (no locking doors, etc)
|
||||
//
|
||||
return USB_STOR_TRANSPORT_GOOD;
|
||||
}
|
||||
|
||||
if (srb->cmnd[0] == START_STOP) {
|
||||
/* this is used by sd.c'check_scsidisk_media_change to detect
|
||||
media change */
|
||||
US_DEBUGP("datafab_transport: START_STOP.\n");
|
||||
/* the first datafab_id_device after a media change returns
|
||||
an error (determined experimentally) */
|
||||
rc = datafab_id_device(us, info);
|
||||
if (rc == USB_STOR_TRANSPORT_GOOD) {
|
||||
info->sense_key = NO_SENSE;
|
||||
srb->result = SUCCESS;
|
||||
} else {
|
||||
info->sense_key = UNIT_ATTENTION;
|
||||
srb->result = SAM_STAT_CHECK_CONDITION;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
US_DEBUGP("datafab_transport: Gah! Unknown command: %d (0x%x)\n",
|
||||
srb->cmnd[0], srb->cmnd[0]);
|
||||
info->sense_key = 0x05;
|
||||
info->sense_asc = 0x20;
|
||||
info->sense_ascq = 0x00;
|
||||
return USB_STOR_TRANSPORT_FAILED;
|
||||
}
|
||||
40
drivers/usb/storage/datafab.h
Normal file
40
drivers/usb/storage/datafab.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/* Driver for Datafab MDCFE-B USB Compact Flash reader
|
||||
* Header File
|
||||
*
|
||||
* Current development and maintenance by:
|
||||
* (c) 2000 Jimmie Mayfield (mayfield+datafab@sackheads.org)
|
||||
*
|
||||
* See datafab.c for more explanation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef _USB_DATAFAB_MDCFE_B_H
|
||||
#define _USB_DATAFAB_MDCFE_B_H
|
||||
|
||||
extern int datafab_transport(struct scsi_cmnd *srb, struct us_data *us);
|
||||
|
||||
struct datafab_info {
|
||||
unsigned long sectors; // total sector count
|
||||
unsigned long ssize; // sector size in bytes
|
||||
signed char lun; // used for dual-slot readers
|
||||
|
||||
// the following aren't used yet
|
||||
unsigned char sense_key;
|
||||
unsigned long sense_asc; // additional sense code
|
||||
unsigned long sense_ascq; // additional sense code qualifier
|
||||
};
|
||||
|
||||
#endif
|
||||
179
drivers/usb/storage/debug.c
Normal file
179
drivers/usb/storage/debug.c
Normal file
@@ -0,0 +1,179 @@
|
||||
/* Driver for USB Mass Storage compliant devices
|
||||
* Debugging Functions Source Code File
|
||||
*
|
||||
* $Id: debug.c,v 1.1.1.1 2007/06/12 07:27:09 eyryu Exp $
|
||||
*
|
||||
* Current development and maintenance by:
|
||||
* (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
|
||||
*
|
||||
* Developed with the assistance of:
|
||||
* (c) 2002 Alan Stern <stern@rowland.org>
|
||||
*
|
||||
* Initial work by:
|
||||
* (c) 1999 Michael Gee (michael@linuxspecific.com)
|
||||
*
|
||||
* This driver is based on the 'USB Mass Storage Class' document. This
|
||||
* describes in detail the protocol used to communicate with such
|
||||
* devices. Clearly, the designers had SCSI and ATAPI commands in
|
||||
* mind when they created this document. The commands are all very
|
||||
* similar to commands in the SCSI-II and ATAPI specifications.
|
||||
*
|
||||
* It is important to note that in a number of cases this class
|
||||
* exhibits class-specific exemptions from the USB specification.
|
||||
* Notably the usage of NAK, STALL and ACK differs from the norm, in
|
||||
* that they are used to communicate wait, failed and OK on commands.
|
||||
*
|
||||
* Also, for certain devices, the interrupt endpoint is used to convey
|
||||
* status of a command.
|
||||
*
|
||||
* Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more
|
||||
* information about this driver.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/cdrom.h>
|
||||
#include <scsi/scsi.h>
|
||||
#include <scsi/scsi_cmnd.h>
|
||||
#include <scsi/scsi_dbg.h>
|
||||
|
||||
#include "debug.h"
|
||||
#include "scsi.h"
|
||||
|
||||
|
||||
void usb_stor_show_command(struct scsi_cmnd *srb)
|
||||
{
|
||||
char *what = NULL;
|
||||
int i;
|
||||
|
||||
switch (srb->cmnd[0]) {
|
||||
case TEST_UNIT_READY: what = "TEST_UNIT_READY"; break;
|
||||
case REZERO_UNIT: what = "REZERO_UNIT"; break;
|
||||
case REQUEST_SENSE: what = "REQUEST_SENSE"; break;
|
||||
case FORMAT_UNIT: what = "FORMAT_UNIT"; break;
|
||||
case READ_BLOCK_LIMITS: what = "READ_BLOCK_LIMITS"; break;
|
||||
case REASSIGN_BLOCKS: what = "REASSIGN_BLOCKS"; break;
|
||||
case READ_6: what = "READ_6"; break;
|
||||
case WRITE_6: what = "WRITE_6"; break;
|
||||
case SEEK_6: what = "SEEK_6"; break;
|
||||
case READ_REVERSE: what = "READ_REVERSE"; break;
|
||||
case WRITE_FILEMARKS: what = "WRITE_FILEMARKS"; break;
|
||||
case SPACE: what = "SPACE"; break;
|
||||
case INQUIRY: what = "INQUIRY"; break;
|
||||
case RECOVER_BUFFERED_DATA: what = "RECOVER_BUFFERED_DATA"; break;
|
||||
case MODE_SELECT: what = "MODE_SELECT"; break;
|
||||
case RESERVE: what = "RESERVE"; break;
|
||||
case RELEASE: what = "RELEASE"; break;
|
||||
case COPY: what = "COPY"; break;
|
||||
case ERASE: what = "ERASE"; break;
|
||||
case MODE_SENSE: what = "MODE_SENSE"; break;
|
||||
case START_STOP: what = "START_STOP"; break;
|
||||
case RECEIVE_DIAGNOSTIC: what = "RECEIVE_DIAGNOSTIC"; break;
|
||||
case SEND_DIAGNOSTIC: what = "SEND_DIAGNOSTIC"; break;
|
||||
case ALLOW_MEDIUM_REMOVAL: what = "ALLOW_MEDIUM_REMOVAL"; break;
|
||||
case SET_WINDOW: what = "SET_WINDOW"; break;
|
||||
case READ_CAPACITY: what = "READ_CAPACITY"; break;
|
||||
case READ_10: what = "READ_10"; break;
|
||||
case WRITE_10: what = "WRITE_10"; break;
|
||||
case SEEK_10: what = "SEEK_10"; break;
|
||||
case WRITE_VERIFY: what = "WRITE_VERIFY"; break;
|
||||
case VERIFY: what = "VERIFY"; break;
|
||||
case SEARCH_HIGH: what = "SEARCH_HIGH"; break;
|
||||
case SEARCH_EQUAL: what = "SEARCH_EQUAL"; break;
|
||||
case SEARCH_LOW: what = "SEARCH_LOW"; break;
|
||||
case SET_LIMITS: what = "SET_LIMITS"; break;
|
||||
case READ_POSITION: what = "READ_POSITION"; break;
|
||||
case SYNCHRONIZE_CACHE: what = "SYNCHRONIZE_CACHE"; break;
|
||||
case LOCK_UNLOCK_CACHE: what = "LOCK_UNLOCK_CACHE"; break;
|
||||
case READ_DEFECT_DATA: what = "READ_DEFECT_DATA"; break;
|
||||
case MEDIUM_SCAN: what = "MEDIUM_SCAN"; break;
|
||||
case COMPARE: what = "COMPARE"; break;
|
||||
case COPY_VERIFY: what = "COPY_VERIFY"; break;
|
||||
case WRITE_BUFFER: what = "WRITE_BUFFER"; break;
|
||||
case READ_BUFFER: what = "READ_BUFFER"; break;
|
||||
case UPDATE_BLOCK: what = "UPDATE_BLOCK"; break;
|
||||
case READ_LONG: what = "READ_LONG"; break;
|
||||
case WRITE_LONG: what = "WRITE_LONG"; break;
|
||||
case CHANGE_DEFINITION: what = "CHANGE_DEFINITION"; break;
|
||||
case WRITE_SAME: what = "WRITE_SAME"; break;
|
||||
case GPCMD_READ_SUBCHANNEL: what = "READ SUBCHANNEL"; break;
|
||||
case READ_TOC: what = "READ_TOC"; break;
|
||||
case GPCMD_READ_HEADER: what = "READ HEADER"; break;
|
||||
case GPCMD_PLAY_AUDIO_10: what = "PLAY AUDIO (10)"; break;
|
||||
case GPCMD_PLAY_AUDIO_MSF: what = "PLAY AUDIO MSF"; break;
|
||||
case GPCMD_GET_EVENT_STATUS_NOTIFICATION:
|
||||
what = "GET EVENT/STATUS NOTIFICATION"; break;
|
||||
case GPCMD_PAUSE_RESUME: what = "PAUSE/RESUME"; break;
|
||||
case LOG_SELECT: what = "LOG_SELECT"; break;
|
||||
case LOG_SENSE: what = "LOG_SENSE"; break;
|
||||
case GPCMD_STOP_PLAY_SCAN: what = "STOP PLAY/SCAN"; break;
|
||||
case GPCMD_READ_DISC_INFO: what = "READ DISC INFORMATION"; break;
|
||||
case GPCMD_READ_TRACK_RZONE_INFO:
|
||||
what = "READ TRACK INFORMATION"; break;
|
||||
case GPCMD_RESERVE_RZONE_TRACK: what = "RESERVE TRACK"; break;
|
||||
case GPCMD_SEND_OPC: what = "SEND OPC"; break;
|
||||
case MODE_SELECT_10: what = "MODE_SELECT_10"; break;
|
||||
case GPCMD_REPAIR_RZONE_TRACK: what = "REPAIR TRACK"; break;
|
||||
case 0x59: what = "READ MASTER CUE"; break;
|
||||
case MODE_SENSE_10: what = "MODE_SENSE_10"; break;
|
||||
case GPCMD_CLOSE_TRACK: what = "CLOSE TRACK/SESSION"; break;
|
||||
case 0x5C: what = "READ BUFFER CAPACITY"; break;
|
||||
case 0x5D: what = "SEND CUE SHEET"; break;
|
||||
case GPCMD_BLANK: what = "BLANK"; break;
|
||||
case REPORT_LUNS: what = "REPORT LUNS"; break;
|
||||
case MOVE_MEDIUM: what = "MOVE_MEDIUM or PLAY AUDIO (12)"; break;
|
||||
case READ_12: what = "READ_12"; break;
|
||||
case WRITE_12: what = "WRITE_12"; break;
|
||||
case WRITE_VERIFY_12: what = "WRITE_VERIFY_12"; break;
|
||||
case SEARCH_HIGH_12: what = "SEARCH_HIGH_12"; break;
|
||||
case SEARCH_EQUAL_12: what = "SEARCH_EQUAL_12"; break;
|
||||
case SEARCH_LOW_12: what = "SEARCH_LOW_12"; break;
|
||||
case SEND_VOLUME_TAG: what = "SEND_VOLUME_TAG"; break;
|
||||
case READ_ELEMENT_STATUS: what = "READ_ELEMENT_STATUS"; break;
|
||||
case GPCMD_READ_CD_MSF: what = "READ CD MSF"; break;
|
||||
case GPCMD_SCAN: what = "SCAN"; break;
|
||||
case GPCMD_SET_SPEED: what = "SET CD SPEED"; break;
|
||||
case GPCMD_MECHANISM_STATUS: what = "MECHANISM STATUS"; break;
|
||||
case GPCMD_READ_CD: what = "READ CD"; break;
|
||||
case 0xE1: what = "WRITE CONTINUE"; break;
|
||||
case WRITE_LONG_2: what = "WRITE_LONG_2"; break;
|
||||
default: what = "(unknown command)"; break;
|
||||
}
|
||||
US_DEBUGP("Command %s (%d bytes)\n", what, srb->cmd_len);
|
||||
US_DEBUGP("");
|
||||
for (i = 0; i < srb->cmd_len && i < 16; i++)
|
||||
US_DEBUGPX(" %02x", srb->cmnd[i]);
|
||||
US_DEBUGPX("\n");
|
||||
}
|
||||
|
||||
void usb_stor_show_sense(
|
||||
unsigned char key,
|
||||
unsigned char asc,
|
||||
unsigned char ascq) {
|
||||
|
||||
const char *what, *keystr;
|
||||
|
||||
keystr = scsi_sense_key_string(key);
|
||||
what = scsi_extd_sense_format(asc, ascq);
|
||||
|
||||
if (keystr == NULL)
|
||||
keystr = "(Unknown Key)";
|
||||
if (what == NULL)
|
||||
what = "(unknown ASC/ASCQ)";
|
||||
|
||||
US_DEBUGP("%s: ", keystr);
|
||||
US_DEBUGPX(what, ascq);
|
||||
US_DEBUGPX("\n");
|
||||
}
|
||||
64
drivers/usb/storage/debug.h
Normal file
64
drivers/usb/storage/debug.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/* Driver for USB Mass Storage compliant devices
|
||||
* Debugging Functions Header File
|
||||
*
|
||||
* $Id: debug.h,v 1.1.1.1 2007/06/12 07:27:09 eyryu Exp $
|
||||
*
|
||||
* Current development and maintenance by:
|
||||
* (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
|
||||
*
|
||||
* Initial work by:
|
||||
* (c) 1999 Michael Gee (michael@linuxspecific.com)
|
||||
*
|
||||
* This driver is based on the 'USB Mass Storage Class' document. This
|
||||
* describes in detail the protocol used to communicate with such
|
||||
* devices. Clearly, the designers had SCSI and ATAPI commands in
|
||||
* mind when they created this document. The commands are all very
|
||||
* similar to commands in the SCSI-II and ATAPI specifications.
|
||||
*
|
||||
* It is important to note that in a number of cases this class
|
||||
* exhibits class-specific exemptions from the USB specification.
|
||||
* Notably the usage of NAK, STALL and ACK differs from the norm, in
|
||||
* that they are used to communicate wait, failed and OK on commands.
|
||||
*
|
||||
* Also, for certain devices, the interrupt endpoint is used to convey
|
||||
* status of a command.
|
||||
*
|
||||
* Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more
|
||||
* information about this driver.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef _DEBUG_H_
|
||||
#define _DEBUG_H_
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#define USB_STORAGE "usb-storage: "
|
||||
|
||||
#ifdef CONFIG_USB_STORAGE_DEBUG
|
||||
void usb_stor_show_command(struct scsi_cmnd *srb);
|
||||
void usb_stor_show_sense( unsigned char key,
|
||||
unsigned char asc, unsigned char ascq );
|
||||
#define US_DEBUGP(x...) printk( KERN_DEBUG USB_STORAGE x )
|
||||
#define US_DEBUGPX(x...) printk( x )
|
||||
#define US_DEBUG(x) x
|
||||
#else
|
||||
#define US_DEBUGP(x...)
|
||||
#define US_DEBUGPX(x...)
|
||||
#define US_DEBUG(x)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
88
drivers/usb/storage/dpcm.c
Normal file
88
drivers/usb/storage/dpcm.c
Normal file
@@ -0,0 +1,88 @@
|
||||
/* Driver for Microtech DPCM-USB CompactFlash/SmartMedia reader
|
||||
*
|
||||
* $Id: dpcm.c,v 1.1.1.1 2007/06/12 07:27:09 eyryu Exp $
|
||||
*
|
||||
* DPCM driver v0.1:
|
||||
*
|
||||
* First release
|
||||
*
|
||||
* Current development and maintenance by:
|
||||
* (c) 2000 Brian Webb (webbb@earthlink.net)
|
||||
*
|
||||
* This device contains both a CompactFlash card reader, which
|
||||
* uses the Control/Bulk w/o Interrupt protocol and
|
||||
* a SmartMedia card reader that uses the same protocol
|
||||
* as the SDDR09.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <scsi/scsi.h>
|
||||
#include <scsi/scsi_cmnd.h>
|
||||
#include <scsi/scsi_device.h>
|
||||
|
||||
#include "usb.h"
|
||||
#include "transport.h"
|
||||
#include "protocol.h"
|
||||
#include "debug.h"
|
||||
#include "dpcm.h"
|
||||
#include "sddr09.h"
|
||||
|
||||
/*
|
||||
* Transport for the Microtech DPCM-USB
|
||||
*
|
||||
*/
|
||||
int dpcm_transport(struct scsi_cmnd *srb, struct us_data *us)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if(srb == NULL)
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
|
||||
US_DEBUGP("dpcm_transport: LUN=%d\n", srb->device->lun);
|
||||
|
||||
switch(srb->device->lun) {
|
||||
case 0:
|
||||
|
||||
/*
|
||||
* LUN 0 corresponds to the CompactFlash card reader.
|
||||
*/
|
||||
ret = usb_stor_CB_transport(srb, us);
|
||||
break;
|
||||
|
||||
#ifdef CONFIG_USB_STORAGE_SDDR09
|
||||
case 1:
|
||||
|
||||
/*
|
||||
* LUN 1 corresponds to the SmartMedia card reader.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Set the LUN to 0 (just in case).
|
||||
*/
|
||||
srb->device->lun = 0; us->srb->device->lun = 0;
|
||||
ret = sddr09_transport(srb, us);
|
||||
srb->device->lun = 1; us->srb->device->lun = 1;
|
||||
break;
|
||||
|
||||
#endif
|
||||
|
||||
default:
|
||||
US_DEBUGP("dpcm_transport: Invalid LUN %d\n", srb->device->lun);
|
||||
ret = USB_STOR_TRANSPORT_ERROR;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
34
drivers/usb/storage/dpcm.h
Normal file
34
drivers/usb/storage/dpcm.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/* Driver for Microtech DPCM-USB CompactFlash/SmartMedia reader
|
||||
*
|
||||
* $Id: dpcm.h,v 1.1.1.1 2007/06/12 07:27:09 eyryu Exp $
|
||||
*
|
||||
* DPCM driver v0.1:
|
||||
*
|
||||
* First release
|
||||
*
|
||||
* Current development and maintenance by:
|
||||
* (c) 2000 Brian Webb (webbb@earthlink.net)
|
||||
*
|
||||
* See dpcm.c for more explanation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef _MICROTECH_DPCM_USB_H
|
||||
#define _MICROTECH_DPCM_USB_H
|
||||
|
||||
extern int dpcm_transport(struct scsi_cmnd *srb, struct us_data *us);
|
||||
|
||||
#endif
|
||||
487
drivers/usb/storage/freecom.c
Normal file
487
drivers/usb/storage/freecom.c
Normal file
@@ -0,0 +1,487 @@
|
||||
/* Driver for Freecom USB/IDE adaptor
|
||||
*
|
||||
* $Id: freecom.c,v 1.1.1.1 2007/06/12 07:27:09 eyryu Exp $
|
||||
*
|
||||
* Freecom v0.1:
|
||||
*
|
||||
* First release
|
||||
*
|
||||
* Current development and maintenance by:
|
||||
* (C) 2000 David Brown <usb-storage@davidb.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* This driver was developed with information provided in FREECOM's USB
|
||||
* Programmers Reference Guide. For further information contact Freecom
|
||||
* (http://www.freecom.de/)
|
||||
*/
|
||||
|
||||
#include <linux/hdreg.h>
|
||||
|
||||
#include <scsi/scsi.h>
|
||||
#include <scsi/scsi_cmnd.h>
|
||||
|
||||
#include "usb.h"
|
||||
#include "transport.h"
|
||||
#include "protocol.h"
|
||||
#include "debug.h"
|
||||
#include "freecom.h"
|
||||
|
||||
#ifdef CONFIG_USB_STORAGE_DEBUG
|
||||
static void pdump (void *, int);
|
||||
#endif
|
||||
|
||||
/* Bits of HD_STATUS */
|
||||
#define ERR_STAT 0x01
|
||||
#define DRQ_STAT 0x08
|
||||
|
||||
/* All of the outgoing packets are 64 bytes long. */
|
||||
struct freecom_cb_wrap {
|
||||
u8 Type; /* Command type. */
|
||||
u8 Timeout; /* Timeout in seconds. */
|
||||
u8 Atapi[12]; /* An ATAPI packet. */
|
||||
u8 Filler[50]; /* Padding Data. */
|
||||
};
|
||||
|
||||
struct freecom_xfer_wrap {
|
||||
u8 Type; /* Command type. */
|
||||
u8 Timeout; /* Timeout in seconds. */
|
||||
__le32 Count; /* Number of bytes to transfer. */
|
||||
u8 Pad[58];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct freecom_ide_out {
|
||||
u8 Type; /* Type + IDE register. */
|
||||
u8 Pad;
|
||||
__le16 Value; /* Value to write. */
|
||||
u8 Pad2[60];
|
||||
};
|
||||
|
||||
struct freecom_ide_in {
|
||||
u8 Type; /* Type | IDE register. */
|
||||
u8 Pad[63];
|
||||
};
|
||||
|
||||
struct freecom_status {
|
||||
u8 Status;
|
||||
u8 Reason;
|
||||
__le16 Count;
|
||||
u8 Pad[60];
|
||||
};
|
||||
|
||||
/* Freecom stuffs the interrupt status in the INDEX_STAT bit of the ide
|
||||
* register. */
|
||||
#define FCM_INT_STATUS 0x02 /* INDEX_STAT */
|
||||
#define FCM_STATUS_BUSY 0x80
|
||||
|
||||
/* These are the packet types. The low bit indicates that this command
|
||||
* should wait for an interrupt. */
|
||||
#define FCM_PACKET_ATAPI 0x21
|
||||
#define FCM_PACKET_STATUS 0x20
|
||||
|
||||
/* Receive data from the IDE interface. The ATAPI packet has already
|
||||
* waited, so the data should be immediately available. */
|
||||
#define FCM_PACKET_INPUT 0x81
|
||||
|
||||
/* Send data to the IDE interface. */
|
||||
#define FCM_PACKET_OUTPUT 0x01
|
||||
|
||||
/* Write a value to an ide register. Or the ide register to write after
|
||||
* munging the address a bit. */
|
||||
#define FCM_PACKET_IDE_WRITE 0x40
|
||||
#define FCM_PACKET_IDE_READ 0xC0
|
||||
|
||||
/* All packets (except for status) are 64 bytes long. */
|
||||
#define FCM_PACKET_LENGTH 64
|
||||
#define FCM_STATUS_PACKET_LENGTH 4
|
||||
|
||||
static int
|
||||
freecom_readdata (struct scsi_cmnd *srb, struct us_data *us,
|
||||
unsigned int ipipe, unsigned int opipe, int count)
|
||||
{
|
||||
struct freecom_xfer_wrap *fxfr =
|
||||
(struct freecom_xfer_wrap *) us->iobuf;
|
||||
int result;
|
||||
|
||||
fxfr->Type = FCM_PACKET_INPUT | 0x00;
|
||||
fxfr->Timeout = 0; /* Short timeout for debugging. */
|
||||
fxfr->Count = cpu_to_le32 (count);
|
||||
memset (fxfr->Pad, 0, sizeof (fxfr->Pad));
|
||||
|
||||
US_DEBUGP("Read data Freecom! (c=%d)\n", count);
|
||||
|
||||
/* Issue the transfer command. */
|
||||
result = usb_stor_bulk_transfer_buf (us, opipe, fxfr,
|
||||
FCM_PACKET_LENGTH, NULL);
|
||||
if (result != USB_STOR_XFER_GOOD) {
|
||||
US_DEBUGP ("Freecom readdata transport error\n");
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
}
|
||||
|
||||
/* Now transfer all of our blocks. */
|
||||
US_DEBUGP("Start of read\n");
|
||||
result = usb_stor_bulk_transfer_sg(us, ipipe, srb->request_buffer,
|
||||
count, srb->use_sg, &srb->resid);
|
||||
US_DEBUGP("freecom_readdata done!\n");
|
||||
|
||||
if (result > USB_STOR_XFER_SHORT)
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
return USB_STOR_TRANSPORT_GOOD;
|
||||
}
|
||||
|
||||
static int
|
||||
freecom_writedata (struct scsi_cmnd *srb, struct us_data *us,
|
||||
int unsigned ipipe, unsigned int opipe, int count)
|
||||
{
|
||||
struct freecom_xfer_wrap *fxfr =
|
||||
(struct freecom_xfer_wrap *) us->iobuf;
|
||||
int result;
|
||||
|
||||
fxfr->Type = FCM_PACKET_OUTPUT | 0x00;
|
||||
fxfr->Timeout = 0; /* Short timeout for debugging. */
|
||||
fxfr->Count = cpu_to_le32 (count);
|
||||
memset (fxfr->Pad, 0, sizeof (fxfr->Pad));
|
||||
|
||||
US_DEBUGP("Write data Freecom! (c=%d)\n", count);
|
||||
|
||||
/* Issue the transfer command. */
|
||||
result = usb_stor_bulk_transfer_buf (us, opipe, fxfr,
|
||||
FCM_PACKET_LENGTH, NULL);
|
||||
if (result != USB_STOR_XFER_GOOD) {
|
||||
US_DEBUGP ("Freecom writedata transport error\n");
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
}
|
||||
|
||||
/* Now transfer all of our blocks. */
|
||||
US_DEBUGP("Start of write\n");
|
||||
result = usb_stor_bulk_transfer_sg(us, opipe, srb->request_buffer,
|
||||
count, srb->use_sg, &srb->resid);
|
||||
|
||||
US_DEBUGP("freecom_writedata done!\n");
|
||||
if (result > USB_STOR_XFER_SHORT)
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
return USB_STOR_TRANSPORT_GOOD;
|
||||
}
|
||||
|
||||
/*
|
||||
* Transport for the Freecom USB/IDE adaptor.
|
||||
*
|
||||
*/
|
||||
int freecom_transport(struct scsi_cmnd *srb, struct us_data *us)
|
||||
{
|
||||
struct freecom_cb_wrap *fcb;
|
||||
struct freecom_status *fst;
|
||||
unsigned int ipipe, opipe; /* We need both pipes. */
|
||||
int result;
|
||||
unsigned int partial;
|
||||
int length;
|
||||
|
||||
fcb = (struct freecom_cb_wrap *) us->iobuf;
|
||||
fst = (struct freecom_status *) us->iobuf;
|
||||
|
||||
US_DEBUGP("Freecom TRANSPORT STARTED\n");
|
||||
|
||||
/* Get handles for both transports. */
|
||||
opipe = us->send_bulk_pipe;
|
||||
ipipe = us->recv_bulk_pipe;
|
||||
|
||||
/* The ATAPI Command always goes out first. */
|
||||
fcb->Type = FCM_PACKET_ATAPI | 0x00;
|
||||
fcb->Timeout = 0;
|
||||
memcpy (fcb->Atapi, srb->cmnd, 12);
|
||||
memset (fcb->Filler, 0, sizeof (fcb->Filler));
|
||||
|
||||
US_DEBUG(pdump (srb->cmnd, 12));
|
||||
|
||||
/* Send it out. */
|
||||
result = usb_stor_bulk_transfer_buf (us, opipe, fcb,
|
||||
FCM_PACKET_LENGTH, NULL);
|
||||
|
||||
/* The Freecom device will only fail if there is something wrong in
|
||||
* USB land. It returns the status in its own registers, which
|
||||
* come back in the bulk pipe. */
|
||||
if (result != USB_STOR_XFER_GOOD) {
|
||||
US_DEBUGP ("freecom transport error\n");
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
}
|
||||
|
||||
/* There are times we can optimize out this status read, but it
|
||||
* doesn't hurt us to always do it now. */
|
||||
result = usb_stor_bulk_transfer_buf (us, ipipe, fst,
|
||||
FCM_STATUS_PACKET_LENGTH, &partial);
|
||||
US_DEBUGP("foo Status result %d %u\n", result, partial);
|
||||
if (result != USB_STOR_XFER_GOOD)
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
|
||||
US_DEBUG(pdump ((void *) fst, partial));
|
||||
|
||||
/* The firmware will time-out commands after 20 seconds. Some commands
|
||||
* can legitimately take longer than this, so we use a different
|
||||
* command that only waits for the interrupt and then sends status,
|
||||
* without having to send a new ATAPI command to the device.
|
||||
*
|
||||
* NOTE: There is some indication that a data transfer after a timeout
|
||||
* may not work, but that is a condition that should never happen.
|
||||
*/
|
||||
while (fst->Status & FCM_STATUS_BUSY) {
|
||||
US_DEBUGP("20 second USB/ATAPI bridge TIMEOUT occurred!\n");
|
||||
US_DEBUGP("fst->Status is %x\n", fst->Status);
|
||||
|
||||
/* Get the status again */
|
||||
fcb->Type = FCM_PACKET_STATUS;
|
||||
fcb->Timeout = 0;
|
||||
memset (fcb->Atapi, 0, sizeof(fcb->Atapi));
|
||||
memset (fcb->Filler, 0, sizeof (fcb->Filler));
|
||||
|
||||
/* Send it out. */
|
||||
result = usb_stor_bulk_transfer_buf (us, opipe, fcb,
|
||||
FCM_PACKET_LENGTH, NULL);
|
||||
|
||||
/* The Freecom device will only fail if there is something
|
||||
* wrong in USB land. It returns the status in its own
|
||||
* registers, which come back in the bulk pipe.
|
||||
*/
|
||||
if (result != USB_STOR_XFER_GOOD) {
|
||||
US_DEBUGP ("freecom transport error\n");
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
}
|
||||
|
||||
/* get the data */
|
||||
result = usb_stor_bulk_transfer_buf (us, ipipe, fst,
|
||||
FCM_STATUS_PACKET_LENGTH, &partial);
|
||||
|
||||
US_DEBUGP("bar Status result %d %u\n", result, partial);
|
||||
if (result != USB_STOR_XFER_GOOD)
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
|
||||
US_DEBUG(pdump ((void *) fst, partial));
|
||||
}
|
||||
|
||||
if (partial != 4)
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
if ((fst->Status & 1) != 0) {
|
||||
US_DEBUGP("operation failed\n");
|
||||
return USB_STOR_TRANSPORT_FAILED;
|
||||
}
|
||||
|
||||
/* The device might not have as much data available as we
|
||||
* requested. If you ask for more than the device has, this reads
|
||||
* and such will hang. */
|
||||
US_DEBUGP("Device indicates that it has %d bytes available\n",
|
||||
le16_to_cpu (fst->Count));
|
||||
US_DEBUGP("SCSI requested %d\n", srb->request_bufflen);
|
||||
|
||||
/* Find the length we desire to read. */
|
||||
switch (srb->cmnd[0]) {
|
||||
case INQUIRY:
|
||||
case REQUEST_SENSE: /* 16 or 18 bytes? spec says 18, lots of devices only have 16 */
|
||||
case MODE_SENSE:
|
||||
case MODE_SENSE_10:
|
||||
length = le16_to_cpu(fst->Count);
|
||||
break;
|
||||
default:
|
||||
length = srb->request_bufflen;
|
||||
}
|
||||
|
||||
/* verify that this amount is legal */
|
||||
if (length > srb->request_bufflen) {
|
||||
length = srb->request_bufflen;
|
||||
US_DEBUGP("Truncating request to match buffer length: %d\n", length);
|
||||
}
|
||||
|
||||
/* What we do now depends on what direction the data is supposed to
|
||||
* move in. */
|
||||
|
||||
switch (us->srb->sc_data_direction) {
|
||||
case DMA_FROM_DEVICE:
|
||||
/* catch bogus "read 0 length" case */
|
||||
if (!length)
|
||||
break;
|
||||
/* Make sure that the status indicates that the device
|
||||
* wants data as well. */
|
||||
if ((fst->Status & DRQ_STAT) == 0 || (fst->Reason & 3) != 2) {
|
||||
US_DEBUGP("SCSI wants data, drive doesn't have any\n");
|
||||
return USB_STOR_TRANSPORT_FAILED;
|
||||
}
|
||||
result = freecom_readdata (srb, us, ipipe, opipe, length);
|
||||
if (result != USB_STOR_TRANSPORT_GOOD)
|
||||
return result;
|
||||
|
||||
US_DEBUGP("FCM: Waiting for status\n");
|
||||
result = usb_stor_bulk_transfer_buf (us, ipipe, fst,
|
||||
FCM_PACKET_LENGTH, &partial);
|
||||
US_DEBUG(pdump ((void *) fst, partial));
|
||||
|
||||
if (partial != 4 || result > USB_STOR_XFER_SHORT)
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
if ((fst->Status & ERR_STAT) != 0) {
|
||||
US_DEBUGP("operation failed\n");
|
||||
return USB_STOR_TRANSPORT_FAILED;
|
||||
}
|
||||
if ((fst->Reason & 3) != 3) {
|
||||
US_DEBUGP("Drive seems still hungry\n");
|
||||
return USB_STOR_TRANSPORT_FAILED;
|
||||
}
|
||||
US_DEBUGP("Transfer happy\n");
|
||||
break;
|
||||
|
||||
case DMA_TO_DEVICE:
|
||||
/* catch bogus "write 0 length" case */
|
||||
if (!length)
|
||||
break;
|
||||
/* Make sure the status indicates that the device wants to
|
||||
* send us data. */
|
||||
/* !!IMPLEMENT!! */
|
||||
result = freecom_writedata (srb, us, ipipe, opipe, length);
|
||||
if (result != USB_STOR_TRANSPORT_GOOD)
|
||||
return result;
|
||||
|
||||
US_DEBUGP("FCM: Waiting for status\n");
|
||||
result = usb_stor_bulk_transfer_buf (us, ipipe, fst,
|
||||
FCM_PACKET_LENGTH, &partial);
|
||||
|
||||
if (partial != 4 || result > USB_STOR_XFER_SHORT)
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
if ((fst->Status & ERR_STAT) != 0) {
|
||||
US_DEBUGP("operation failed\n");
|
||||
return USB_STOR_TRANSPORT_FAILED;
|
||||
}
|
||||
if ((fst->Reason & 3) != 3) {
|
||||
US_DEBUGP("Drive seems still hungry\n");
|
||||
return USB_STOR_TRANSPORT_FAILED;
|
||||
}
|
||||
|
||||
US_DEBUGP("Transfer happy\n");
|
||||
break;
|
||||
|
||||
|
||||
case DMA_NONE:
|
||||
/* Easy, do nothing. */
|
||||
break;
|
||||
|
||||
default:
|
||||
/* should never hit here -- filtered in usb.c */
|
||||
US_DEBUGP ("freecom unimplemented direction: %d\n",
|
||||
us->srb->sc_data_direction);
|
||||
// Return fail, SCSI seems to handle this better.
|
||||
return USB_STOR_TRANSPORT_FAILED;
|
||||
break;
|
||||
}
|
||||
|
||||
return USB_STOR_TRANSPORT_GOOD;
|
||||
}
|
||||
|
||||
int
|
||||
freecom_init (struct us_data *us)
|
||||
{
|
||||
int result;
|
||||
char *buffer = us->iobuf;
|
||||
|
||||
/* The DMA-mapped I/O buffer is 64 bytes long, just right for
|
||||
* all our packets. No need to allocate any extra buffer space.
|
||||
*/
|
||||
|
||||
result = usb_stor_control_msg(us, us->recv_ctrl_pipe,
|
||||
0x4c, 0xc0, 0x4346, 0x0, buffer, 0x20, 3*HZ);
|
||||
buffer[32] = '\0';
|
||||
US_DEBUGP("String returned from FC init is: %s\n", buffer);
|
||||
|
||||
/* Special thanks to the people at Freecom for providing me with
|
||||
* this "magic sequence", which they use in their Windows and MacOS
|
||||
* drivers to make sure that all the attached perhiperals are
|
||||
* properly reset.
|
||||
*/
|
||||
|
||||
/* send reset */
|
||||
result = usb_stor_control_msg(us, us->send_ctrl_pipe,
|
||||
0x4d, 0x40, 0x24d8, 0x0, NULL, 0x0, 3*HZ);
|
||||
US_DEBUGP("result from activate reset is %d\n", result);
|
||||
|
||||
/* wait 250ms */
|
||||
mdelay(250);
|
||||
|
||||
/* clear reset */
|
||||
result = usb_stor_control_msg(us, us->send_ctrl_pipe,
|
||||
0x4d, 0x40, 0x24f8, 0x0, NULL, 0x0, 3*HZ);
|
||||
US_DEBUGP("result from clear reset is %d\n", result);
|
||||
|
||||
/* wait 3 seconds */
|
||||
mdelay(3 * 1000);
|
||||
|
||||
return USB_STOR_TRANSPORT_GOOD;
|
||||
}
|
||||
|
||||
int usb_stor_freecom_reset(struct us_data *us)
|
||||
{
|
||||
printk (KERN_CRIT "freecom reset called\n");
|
||||
|
||||
/* We don't really have this feature. */
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_STORAGE_DEBUG
|
||||
static void pdump (void *ibuffer, int length)
|
||||
{
|
||||
static char line[80];
|
||||
int offset = 0;
|
||||
unsigned char *buffer = (unsigned char *) ibuffer;
|
||||
int i, j;
|
||||
int from, base;
|
||||
|
||||
offset = 0;
|
||||
for (i = 0; i < length; i++) {
|
||||
if ((i & 15) == 0) {
|
||||
if (i > 0) {
|
||||
offset += sprintf (line+offset, " - ");
|
||||
for (j = i - 16; j < i; j++) {
|
||||
if (buffer[j] >= 32 && buffer[j] <= 126)
|
||||
line[offset++] = buffer[j];
|
||||
else
|
||||
line[offset++] = '.';
|
||||
}
|
||||
line[offset] = 0;
|
||||
US_DEBUGP("%s\n", line);
|
||||
offset = 0;
|
||||
}
|
||||
offset += sprintf (line+offset, "%08x:", i);
|
||||
}
|
||||
else if ((i & 7) == 0) {
|
||||
offset += sprintf (line+offset, " -");
|
||||
}
|
||||
offset += sprintf (line+offset, " %02x", buffer[i] & 0xff);
|
||||
}
|
||||
|
||||
/* Add the last "chunk" of data. */
|
||||
from = (length - 1) % 16;
|
||||
base = ((length - 1) / 16) * 16;
|
||||
|
||||
for (i = from + 1; i < 16; i++)
|
||||
offset += sprintf (line+offset, " ");
|
||||
if (from < 8)
|
||||
offset += sprintf (line+offset, " ");
|
||||
offset += sprintf (line+offset, " - ");
|
||||
|
||||
for (i = 0; i <= from; i++) {
|
||||
if (buffer[base+i] >= 32 && buffer[base+i] <= 126)
|
||||
line[offset++] = buffer[base+i];
|
||||
else
|
||||
line[offset++] = '.';
|
||||
}
|
||||
line[offset] = 0;
|
||||
US_DEBUGP("%s\n", line);
|
||||
offset = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
36
drivers/usb/storage/freecom.h
Normal file
36
drivers/usb/storage/freecom.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/* Driver for Freecom USB/IDE adaptor
|
||||
*
|
||||
* $Id: freecom.h,v 1.1.1.1 2007/06/12 07:27:09 eyryu Exp $
|
||||
*
|
||||
* Freecom v0.1:
|
||||
*
|
||||
* First release
|
||||
*
|
||||
* Current development and maintenance by:
|
||||
* (c) 2000 David Brown <usb-storage@davidb.org>
|
||||
*
|
||||
* See freecom.c for more explanation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef _FREECOM_USB_H
|
||||
#define _FREECOM_USB_H
|
||||
|
||||
extern int freecom_transport(struct scsi_cmnd *srb, struct us_data *us);
|
||||
extern int usb_stor_freecom_reset(struct us_data *us);
|
||||
extern int freecom_init (struct us_data *us);
|
||||
|
||||
#endif
|
||||
92
drivers/usb/storage/initializers.c
Normal file
92
drivers/usb/storage/initializers.c
Normal file
@@ -0,0 +1,92 @@
|
||||
/* Special Initializers for certain USB Mass Storage devices
|
||||
*
|
||||
* $Id: initializers.c,v 1.1.1.1 2007/06/12 07:27:09 eyryu Exp $
|
||||
*
|
||||
* Current development and maintenance by:
|
||||
* (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
|
||||
*
|
||||
* This driver is based on the 'USB Mass Storage Class' document. This
|
||||
* describes in detail the protocol used to communicate with such
|
||||
* devices. Clearly, the designers had SCSI and ATAPI commands in
|
||||
* mind when they created this document. The commands are all very
|
||||
* similar to commands in the SCSI-II and ATAPI specifications.
|
||||
*
|
||||
* It is important to note that in a number of cases this class
|
||||
* exhibits class-specific exemptions from the USB specification.
|
||||
* Notably the usage of NAK, STALL and ACK differs from the norm, in
|
||||
* that they are used to communicate wait, failed and OK on commands.
|
||||
*
|
||||
* Also, for certain devices, the interrupt endpoint is used to convey
|
||||
* status of a command.
|
||||
*
|
||||
* Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more
|
||||
* information about this driver.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/errno.h>
|
||||
|
||||
#include "usb.h"
|
||||
#include "initializers.h"
|
||||
#include "debug.h"
|
||||
#include "transport.h"
|
||||
|
||||
/* This places the Shuttle/SCM USB<->SCSI bridge devices in multi-target
|
||||
* mode */
|
||||
int usb_stor_euscsi_init(struct us_data *us)
|
||||
{
|
||||
int result;
|
||||
|
||||
US_DEBUGP("Attempting to init eUSCSI bridge...\n");
|
||||
us->iobuf[0] = 0x1;
|
||||
result = usb_stor_control_msg(us, us->send_ctrl_pipe,
|
||||
0x0C, USB_RECIP_INTERFACE | USB_TYPE_VENDOR,
|
||||
0x01, 0x0, us->iobuf, 0x1, 5*HZ);
|
||||
US_DEBUGP("-- result is %d\n", result);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This function is required to activate all four slots on the UCR-61S2B
|
||||
* flash reader */
|
||||
int usb_stor_ucr61s2b_init(struct us_data *us)
|
||||
{
|
||||
struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap*) us->iobuf;
|
||||
struct bulk_cs_wrap *bcs = (struct bulk_cs_wrap*) us->iobuf;
|
||||
int res, partial;
|
||||
static char init_string[] = "\xec\x0a\x06\x00$PCCHIPS";
|
||||
|
||||
US_DEBUGP("Sending UCR-61S2B initialization packet...\n");
|
||||
|
||||
bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN);
|
||||
bcb->Tag = 0;
|
||||
bcb->DataTransferLength = cpu_to_le32(0);
|
||||
bcb->Flags = bcb->Lun = 0;
|
||||
bcb->Length = sizeof(init_string) - 1;
|
||||
memset(bcb->CDB, 0, sizeof(bcb->CDB));
|
||||
memcpy(bcb->CDB, init_string, sizeof(init_string) - 1);
|
||||
|
||||
res = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, bcb,
|
||||
US_BULK_CB_WRAP_LEN, &partial);
|
||||
if(res)
|
||||
return res;
|
||||
|
||||
US_DEBUGP("Getting status packet...\n");
|
||||
res = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, bcs,
|
||||
US_BULK_CS_WRAP_LEN, &partial);
|
||||
|
||||
return (res ? -1 : 0);
|
||||
}
|
||||
49
drivers/usb/storage/initializers.h
Normal file
49
drivers/usb/storage/initializers.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/* Header file for Special Initializers for certain USB Mass Storage devices
|
||||
*
|
||||
* $Id: initializers.h,v 1.1.1.1 2007/06/12 07:27:09 eyryu Exp $
|
||||
*
|
||||
* Current development and maintenance by:
|
||||
* (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
|
||||
*
|
||||
* This driver is based on the 'USB Mass Storage Class' document. This
|
||||
* describes in detail the protocol used to communicate with such
|
||||
* devices. Clearly, the designers had SCSI and ATAPI commands in
|
||||
* mind when they created this document. The commands are all very
|
||||
* similar to commands in the SCSI-II and ATAPI specifications.
|
||||
*
|
||||
* It is important to note that in a number of cases this class
|
||||
* exhibits class-specific exemptions from the USB specification.
|
||||
* Notably the usage of NAK, STALL and ACK differs from the norm, in
|
||||
* that they are used to communicate wait, failed and OK on commands.
|
||||
*
|
||||
* Also, for certain devices, the interrupt endpoint is used to convey
|
||||
* status of a command.
|
||||
*
|
||||
* Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more
|
||||
* information about this driver.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "usb.h"
|
||||
#include "transport.h"
|
||||
|
||||
/* This places the Shuttle/SCM USB<->SCSI bridge devices in multi-target
|
||||
* mode */
|
||||
int usb_stor_euscsi_init(struct us_data *us);
|
||||
|
||||
/* This function is required to activate all four slots on the UCR-61S2B
|
||||
* flash reader */
|
||||
int usb_stor_ucr61s2b_init(struct us_data *us);
|
||||
1440
drivers/usb/storage/isd200.c
Normal file
1440
drivers/usb/storage/isd200.c
Normal file
File diff suppressed because it is too large
Load Diff
31
drivers/usb/storage/isd200.h
Normal file
31
drivers/usb/storage/isd200.h
Normal file
@@ -0,0 +1,31 @@
|
||||
/* Header File for In-System Design, Inc. ISD200 ASIC
|
||||
*
|
||||
* First release
|
||||
*
|
||||
* Current development and maintenance by:
|
||||
* (c) 2000 In-System Design, Inc. (support@in-system.com)
|
||||
*
|
||||
* See isd200.c for more information.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef _USB_ISD200_H
|
||||
#define _USB_ISD200_H
|
||||
|
||||
extern void isd200_ata_command(struct scsi_cmnd *srb, struct us_data *us);
|
||||
extern int isd200_Initialization(struct us_data *us);
|
||||
|
||||
#endif
|
||||
594
drivers/usb/storage/jumpshot.c
Normal file
594
drivers/usb/storage/jumpshot.c
Normal file
@@ -0,0 +1,594 @@
|
||||
/* Driver for Lexar "Jumpshot" Compact Flash reader
|
||||
*
|
||||
* $Id: jumpshot.c,v 1.1.1.1 2007/06/12 07:27:09 eyryu Exp $
|
||||
*
|
||||
* jumpshot driver v0.1:
|
||||
*
|
||||
* First release
|
||||
*
|
||||
* Current development and maintenance by:
|
||||
* (c) 2000 Jimmie Mayfield (mayfield+usb@sackheads.org)
|
||||
*
|
||||
* Many thanks to Robert Baruch for the SanDisk SmartMedia reader driver
|
||||
* which I used as a template for this driver.
|
||||
*
|
||||
* Some bugfixes and scatter-gather code by Gregory P. Smith
|
||||
* (greg-usb@electricrain.com)
|
||||
*
|
||||
* Fix for media change by Joerg Schneider (js@joergschneider.com)
|
||||
*
|
||||
* Developed with the assistance of:
|
||||
*
|
||||
* (C) 2002 Alan Stern <stern@rowland.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This driver attempts to support the Lexar Jumpshot USB CompactFlash
|
||||
* reader. Like many other USB CompactFlash readers, the Jumpshot contains
|
||||
* a USB-to-ATA chip.
|
||||
*
|
||||
* This driver supports reading and writing. If you're truly paranoid,
|
||||
* however, you can force the driver into a write-protected state by setting
|
||||
* the WP enable bits in jumpshot_handle_mode_sense. See the comments
|
||||
* in that routine.
|
||||
*/
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <scsi/scsi.h>
|
||||
#include <scsi/scsi_cmnd.h>
|
||||
|
||||
#include "usb.h"
|
||||
#include "transport.h"
|
||||
#include "protocol.h"
|
||||
#include "debug.h"
|
||||
#include "jumpshot.h"
|
||||
|
||||
|
||||
static inline int jumpshot_bulk_read(struct us_data *us,
|
||||
unsigned char *data,
|
||||
unsigned int len)
|
||||
{
|
||||
if (len == 0)
|
||||
return USB_STOR_XFER_GOOD;
|
||||
|
||||
US_DEBUGP("jumpshot_bulk_read: len = %d\n", len);
|
||||
return usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe,
|
||||
data, len, NULL);
|
||||
}
|
||||
|
||||
|
||||
static inline int jumpshot_bulk_write(struct us_data *us,
|
||||
unsigned char *data,
|
||||
unsigned int len)
|
||||
{
|
||||
if (len == 0)
|
||||
return USB_STOR_XFER_GOOD;
|
||||
|
||||
US_DEBUGP("jumpshot_bulk_write: len = %d\n", len);
|
||||
return usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe,
|
||||
data, len, NULL);
|
||||
}
|
||||
|
||||
|
||||
static int jumpshot_get_status(struct us_data *us)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (!us)
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
|
||||
// send the setup
|
||||
rc = usb_stor_ctrl_transfer(us, us->recv_ctrl_pipe,
|
||||
0, 0xA0, 0, 7, us->iobuf, 1);
|
||||
|
||||
if (rc != USB_STOR_XFER_GOOD)
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
|
||||
if (us->iobuf[0] != 0x50) {
|
||||
US_DEBUGP("jumpshot_get_status: 0x%2x\n",
|
||||
us->iobuf[0]);
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
}
|
||||
|
||||
return USB_STOR_TRANSPORT_GOOD;
|
||||
}
|
||||
|
||||
static int jumpshot_read_data(struct us_data *us,
|
||||
struct jumpshot_info *info,
|
||||
u32 sector,
|
||||
u32 sectors)
|
||||
{
|
||||
unsigned char *command = us->iobuf;
|
||||
unsigned char *buffer;
|
||||
unsigned char thistime;
|
||||
unsigned int totallen, alloclen;
|
||||
int len, result;
|
||||
unsigned int sg_idx = 0, sg_offset = 0;
|
||||
|
||||
// we're working in LBA mode. according to the ATA spec,
|
||||
// we can support up to 28-bit addressing. I don't know if Jumpshot
|
||||
// supports beyond 24-bit addressing. It's kind of hard to test
|
||||
// since it requires > 8GB CF card.
|
||||
|
||||
if (sector > 0x0FFFFFFF)
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
|
||||
totallen = sectors * info->ssize;
|
||||
|
||||
// Since we don't read more than 64 KB at a time, we have to create
|
||||
// a bounce buffer and move the data a piece at a time between the
|
||||
// bounce buffer and the actual transfer buffer.
|
||||
|
||||
alloclen = min(totallen, 65536u);
|
||||
buffer = kmalloc(alloclen, GFP_NOIO);
|
||||
if (buffer == NULL)
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
|
||||
do {
|
||||
// loop, never allocate or transfer more than 64k at once
|
||||
// (min(128k, 255*info->ssize) is the real limit)
|
||||
len = min(totallen, alloclen);
|
||||
thistime = (len / info->ssize) & 0xff;
|
||||
|
||||
command[0] = 0;
|
||||
command[1] = thistime;
|
||||
command[2] = sector & 0xFF;
|
||||
command[3] = (sector >> 8) & 0xFF;
|
||||
command[4] = (sector >> 16) & 0xFF;
|
||||
|
||||
command[5] = 0xE0 | ((sector >> 24) & 0x0F);
|
||||
command[6] = 0x20;
|
||||
|
||||
// send the setup + command
|
||||
result = usb_stor_ctrl_transfer(us, us->send_ctrl_pipe,
|
||||
0, 0x20, 0, 1, command, 7);
|
||||
if (result != USB_STOR_XFER_GOOD)
|
||||
goto leave;
|
||||
|
||||
// read the result
|
||||
result = jumpshot_bulk_read(us, buffer, len);
|
||||
if (result != USB_STOR_XFER_GOOD)
|
||||
goto leave;
|
||||
|
||||
US_DEBUGP("jumpshot_read_data: %d bytes\n", len);
|
||||
|
||||
// Store the data in the transfer buffer
|
||||
usb_stor_access_xfer_buf(buffer, len, us->srb,
|
||||
&sg_idx, &sg_offset, TO_XFER_BUF);
|
||||
|
||||
sector += thistime;
|
||||
totallen -= len;
|
||||
} while (totallen > 0);
|
||||
|
||||
kfree(buffer);
|
||||
return USB_STOR_TRANSPORT_GOOD;
|
||||
|
||||
leave:
|
||||
kfree(buffer);
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
}
|
||||
|
||||
|
||||
static int jumpshot_write_data(struct us_data *us,
|
||||
struct jumpshot_info *info,
|
||||
u32 sector,
|
||||
u32 sectors)
|
||||
{
|
||||
unsigned char *command = us->iobuf;
|
||||
unsigned char *buffer;
|
||||
unsigned char thistime;
|
||||
unsigned int totallen, alloclen;
|
||||
int len, result, waitcount;
|
||||
unsigned int sg_idx = 0, sg_offset = 0;
|
||||
|
||||
// we're working in LBA mode. according to the ATA spec,
|
||||
// we can support up to 28-bit addressing. I don't know if Jumpshot
|
||||
// supports beyond 24-bit addressing. It's kind of hard to test
|
||||
// since it requires > 8GB CF card.
|
||||
//
|
||||
if (sector > 0x0FFFFFFF)
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
|
||||
totallen = sectors * info->ssize;
|
||||
|
||||
// Since we don't write more than 64 KB at a time, we have to create
|
||||
// a bounce buffer and move the data a piece at a time between the
|
||||
// bounce buffer and the actual transfer buffer.
|
||||
|
||||
alloclen = min(totallen, 65536u);
|
||||
buffer = kmalloc(alloclen, GFP_NOIO);
|
||||
if (buffer == NULL)
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
|
||||
do {
|
||||
// loop, never allocate or transfer more than 64k at once
|
||||
// (min(128k, 255*info->ssize) is the real limit)
|
||||
|
||||
len = min(totallen, alloclen);
|
||||
thistime = (len / info->ssize) & 0xff;
|
||||
|
||||
// Get the data from the transfer buffer
|
||||
usb_stor_access_xfer_buf(buffer, len, us->srb,
|
||||
&sg_idx, &sg_offset, FROM_XFER_BUF);
|
||||
|
||||
command[0] = 0;
|
||||
command[1] = thistime;
|
||||
command[2] = sector & 0xFF;
|
||||
command[3] = (sector >> 8) & 0xFF;
|
||||
command[4] = (sector >> 16) & 0xFF;
|
||||
|
||||
command[5] = 0xE0 | ((sector >> 24) & 0x0F);
|
||||
command[6] = 0x30;
|
||||
|
||||
// send the setup + command
|
||||
result = usb_stor_ctrl_transfer(us, us->send_ctrl_pipe,
|
||||
0, 0x20, 0, 1, command, 7);
|
||||
if (result != USB_STOR_XFER_GOOD)
|
||||
goto leave;
|
||||
|
||||
// send the data
|
||||
result = jumpshot_bulk_write(us, buffer, len);
|
||||
if (result != USB_STOR_XFER_GOOD)
|
||||
goto leave;
|
||||
|
||||
// read the result. apparently the bulk write can complete
|
||||
// before the jumpshot drive is finished writing. so we loop
|
||||
// here until we get a good return code
|
||||
waitcount = 0;
|
||||
do {
|
||||
result = jumpshot_get_status(us);
|
||||
if (result != USB_STOR_TRANSPORT_GOOD) {
|
||||
// I have not experimented to find the smallest value.
|
||||
//
|
||||
msleep(50);
|
||||
}
|
||||
} while ((result != USB_STOR_TRANSPORT_GOOD) && (waitcount < 10));
|
||||
|
||||
if (result != USB_STOR_TRANSPORT_GOOD)
|
||||
US_DEBUGP("jumpshot_write_data: Gah! Waitcount = 10. Bad write!?\n");
|
||||
|
||||
sector += thistime;
|
||||
totallen -= len;
|
||||
} while (totallen > 0);
|
||||
|
||||
kfree(buffer);
|
||||
return result;
|
||||
|
||||
leave:
|
||||
kfree(buffer);
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
}
|
||||
|
||||
static int jumpshot_id_device(struct us_data *us,
|
||||
struct jumpshot_info *info)
|
||||
{
|
||||
unsigned char *command = us->iobuf;
|
||||
unsigned char *reply;
|
||||
int rc;
|
||||
|
||||
if (!us || !info)
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
|
||||
command[0] = 0xE0;
|
||||
command[1] = 0xEC;
|
||||
reply = kmalloc(512, GFP_NOIO);
|
||||
if (!reply)
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
|
||||
// send the setup
|
||||
rc = usb_stor_ctrl_transfer(us, us->send_ctrl_pipe,
|
||||
0, 0x20, 0, 6, command, 2);
|
||||
|
||||
if (rc != USB_STOR_XFER_GOOD) {
|
||||
US_DEBUGP("jumpshot_id_device: Gah! "
|
||||
"send_control for read_capacity failed\n");
|
||||
rc = USB_STOR_TRANSPORT_ERROR;
|
||||
goto leave;
|
||||
}
|
||||
|
||||
// read the reply
|
||||
rc = jumpshot_bulk_read(us, reply, 512);
|
||||
if (rc != USB_STOR_XFER_GOOD) {
|
||||
rc = USB_STOR_TRANSPORT_ERROR;
|
||||
goto leave;
|
||||
}
|
||||
|
||||
info->sectors = ((u32)(reply[117]) << 24) |
|
||||
((u32)(reply[116]) << 16) |
|
||||
((u32)(reply[115]) << 8) |
|
||||
((u32)(reply[114]) );
|
||||
|
||||
rc = USB_STOR_TRANSPORT_GOOD;
|
||||
|
||||
leave:
|
||||
kfree(reply);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int jumpshot_handle_mode_sense(struct us_data *us,
|
||||
struct scsi_cmnd * srb,
|
||||
int sense_6)
|
||||
{
|
||||
static unsigned char rw_err_page[12] = {
|
||||
0x1, 0xA, 0x21, 1, 0, 0, 0, 0, 1, 0, 0, 0
|
||||
};
|
||||
static unsigned char cache_page[12] = {
|
||||
0x8, 0xA, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
static unsigned char rbac_page[12] = {
|
||||
0x1B, 0xA, 0, 0x81, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
static unsigned char timer_page[8] = {
|
||||
0x1C, 0x6, 0, 0, 0, 0
|
||||
};
|
||||
unsigned char pc, page_code;
|
||||
unsigned int i = 0;
|
||||
struct jumpshot_info *info = (struct jumpshot_info *) (us->extra);
|
||||
unsigned char *ptr = us->iobuf;
|
||||
|
||||
pc = srb->cmnd[2] >> 6;
|
||||
page_code = srb->cmnd[2] & 0x3F;
|
||||
|
||||
switch (pc) {
|
||||
case 0x0:
|
||||
US_DEBUGP("jumpshot_handle_mode_sense: Current values\n");
|
||||
break;
|
||||
case 0x1:
|
||||
US_DEBUGP("jumpshot_handle_mode_sense: Changeable values\n");
|
||||
break;
|
||||
case 0x2:
|
||||
US_DEBUGP("jumpshot_handle_mode_sense: Default values\n");
|
||||
break;
|
||||
case 0x3:
|
||||
US_DEBUGP("jumpshot_handle_mode_sense: Saves values\n");
|
||||
break;
|
||||
}
|
||||
|
||||
memset(ptr, 0, 8);
|
||||
if (sense_6) {
|
||||
ptr[2] = 0x00; // WP enable: 0x80
|
||||
i = 4;
|
||||
} else {
|
||||
ptr[3] = 0x00; // WP enable: 0x80
|
||||
i = 8;
|
||||
}
|
||||
|
||||
switch (page_code) {
|
||||
case 0x0:
|
||||
// vendor-specific mode
|
||||
info->sense_key = 0x05;
|
||||
info->sense_asc = 0x24;
|
||||
info->sense_ascq = 0x00;
|
||||
return USB_STOR_TRANSPORT_FAILED;
|
||||
|
||||
case 0x1:
|
||||
memcpy(ptr + i, rw_err_page, sizeof(rw_err_page));
|
||||
i += sizeof(rw_err_page);
|
||||
break;
|
||||
|
||||
case 0x8:
|
||||
memcpy(ptr + i, cache_page, sizeof(cache_page));
|
||||
i += sizeof(cache_page);
|
||||
break;
|
||||
|
||||
case 0x1B:
|
||||
memcpy(ptr + i, rbac_page, sizeof(rbac_page));
|
||||
i += sizeof(rbac_page);
|
||||
break;
|
||||
|
||||
case 0x1C:
|
||||
memcpy(ptr + i, timer_page, sizeof(timer_page));
|
||||
i += sizeof(timer_page);
|
||||
break;
|
||||
|
||||
case 0x3F:
|
||||
memcpy(ptr + i, timer_page, sizeof(timer_page));
|
||||
i += sizeof(timer_page);
|
||||
memcpy(ptr + i, rbac_page, sizeof(rbac_page));
|
||||
i += sizeof(rbac_page);
|
||||
memcpy(ptr + i, cache_page, sizeof(cache_page));
|
||||
i += sizeof(cache_page);
|
||||
memcpy(ptr + i, rw_err_page, sizeof(rw_err_page));
|
||||
i += sizeof(rw_err_page);
|
||||
break;
|
||||
}
|
||||
|
||||
if (sense_6)
|
||||
ptr[0] = i - 1;
|
||||
else
|
||||
((__be16 *) ptr)[0] = cpu_to_be16(i - 2);
|
||||
usb_stor_set_xfer_buf(ptr, i, srb);
|
||||
|
||||
return USB_STOR_TRANSPORT_GOOD;
|
||||
}
|
||||
|
||||
|
||||
static void jumpshot_info_destructor(void *extra)
|
||||
{
|
||||
// this routine is a placeholder...
|
||||
// currently, we don't allocate any extra blocks so we're okay
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Transport for the Lexar 'Jumpshot'
|
||||
//
|
||||
int jumpshot_transport(struct scsi_cmnd * srb, struct us_data *us)
|
||||
{
|
||||
struct jumpshot_info *info;
|
||||
int rc;
|
||||
unsigned long block, blocks;
|
||||
unsigned char *ptr = us->iobuf;
|
||||
static unsigned char inquiry_response[8] = {
|
||||
0x00, 0x80, 0x00, 0x01, 0x1F, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
if (!us->extra) {
|
||||
us->extra = kzalloc(sizeof(struct jumpshot_info), GFP_NOIO);
|
||||
if (!us->extra) {
|
||||
US_DEBUGP("jumpshot_transport: Gah! Can't allocate storage for jumpshot info struct!\n");
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
}
|
||||
us->extra_destructor = jumpshot_info_destructor;
|
||||
}
|
||||
|
||||
info = (struct jumpshot_info *) (us->extra);
|
||||
|
||||
if (srb->cmnd[0] == INQUIRY) {
|
||||
US_DEBUGP("jumpshot_transport: INQUIRY. Returning bogus response.\n");
|
||||
memcpy(ptr, inquiry_response, sizeof(inquiry_response));
|
||||
fill_inquiry_response(us, ptr, 36);
|
||||
return USB_STOR_TRANSPORT_GOOD;
|
||||
}
|
||||
|
||||
if (srb->cmnd[0] == READ_CAPACITY) {
|
||||
info->ssize = 0x200; // hard coded 512 byte sectors as per ATA spec
|
||||
|
||||
rc = jumpshot_get_status(us);
|
||||
if (rc != USB_STOR_TRANSPORT_GOOD)
|
||||
return rc;
|
||||
|
||||
rc = jumpshot_id_device(us, info);
|
||||
if (rc != USB_STOR_TRANSPORT_GOOD)
|
||||
return rc;
|
||||
|
||||
US_DEBUGP("jumpshot_transport: READ_CAPACITY: %ld sectors, %ld bytes per sector\n",
|
||||
info->sectors, info->ssize);
|
||||
|
||||
// build the reply
|
||||
//
|
||||
((__be32 *) ptr)[0] = cpu_to_be32(info->sectors - 1);
|
||||
((__be32 *) ptr)[1] = cpu_to_be32(info->ssize);
|
||||
usb_stor_set_xfer_buf(ptr, 8, srb);
|
||||
|
||||
return USB_STOR_TRANSPORT_GOOD;
|
||||
}
|
||||
|
||||
if (srb->cmnd[0] == MODE_SELECT_10) {
|
||||
US_DEBUGP("jumpshot_transport: Gah! MODE_SELECT_10.\n");
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
}
|
||||
|
||||
if (srb->cmnd[0] == READ_10) {
|
||||
block = ((u32)(srb->cmnd[2]) << 24) | ((u32)(srb->cmnd[3]) << 16) |
|
||||
((u32)(srb->cmnd[4]) << 8) | ((u32)(srb->cmnd[5]));
|
||||
|
||||
blocks = ((u32)(srb->cmnd[7]) << 8) | ((u32)(srb->cmnd[8]));
|
||||
|
||||
US_DEBUGP("jumpshot_transport: READ_10: read block 0x%04lx count %ld\n", block, blocks);
|
||||
return jumpshot_read_data(us, info, block, blocks);
|
||||
}
|
||||
|
||||
if (srb->cmnd[0] == READ_12) {
|
||||
// I don't think we'll ever see a READ_12 but support it anyway...
|
||||
//
|
||||
block = ((u32)(srb->cmnd[2]) << 24) | ((u32)(srb->cmnd[3]) << 16) |
|
||||
((u32)(srb->cmnd[4]) << 8) | ((u32)(srb->cmnd[5]));
|
||||
|
||||
blocks = ((u32)(srb->cmnd[6]) << 24) | ((u32)(srb->cmnd[7]) << 16) |
|
||||
((u32)(srb->cmnd[8]) << 8) | ((u32)(srb->cmnd[9]));
|
||||
|
||||
US_DEBUGP("jumpshot_transport: READ_12: read block 0x%04lx count %ld\n", block, blocks);
|
||||
return jumpshot_read_data(us, info, block, blocks);
|
||||
}
|
||||
|
||||
if (srb->cmnd[0] == WRITE_10) {
|
||||
block = ((u32)(srb->cmnd[2]) << 24) | ((u32)(srb->cmnd[3]) << 16) |
|
||||
((u32)(srb->cmnd[4]) << 8) | ((u32)(srb->cmnd[5]));
|
||||
|
||||
blocks = ((u32)(srb->cmnd[7]) << 8) | ((u32)(srb->cmnd[8]));
|
||||
|
||||
US_DEBUGP("jumpshot_transport: WRITE_10: write block 0x%04lx count %ld\n", block, blocks);
|
||||
return jumpshot_write_data(us, info, block, blocks);
|
||||
}
|
||||
|
||||
if (srb->cmnd[0] == WRITE_12) {
|
||||
// I don't think we'll ever see a WRITE_12 but support it anyway...
|
||||
//
|
||||
block = ((u32)(srb->cmnd[2]) << 24) | ((u32)(srb->cmnd[3]) << 16) |
|
||||
((u32)(srb->cmnd[4]) << 8) | ((u32)(srb->cmnd[5]));
|
||||
|
||||
blocks = ((u32)(srb->cmnd[6]) << 24) | ((u32)(srb->cmnd[7]) << 16) |
|
||||
((u32)(srb->cmnd[8]) << 8) | ((u32)(srb->cmnd[9]));
|
||||
|
||||
US_DEBUGP("jumpshot_transport: WRITE_12: write block 0x%04lx count %ld\n", block, blocks);
|
||||
return jumpshot_write_data(us, info, block, blocks);
|
||||
}
|
||||
|
||||
|
||||
if (srb->cmnd[0] == TEST_UNIT_READY) {
|
||||
US_DEBUGP("jumpshot_transport: TEST_UNIT_READY.\n");
|
||||
return jumpshot_get_status(us);
|
||||
}
|
||||
|
||||
if (srb->cmnd[0] == REQUEST_SENSE) {
|
||||
US_DEBUGP("jumpshot_transport: REQUEST_SENSE.\n");
|
||||
|
||||
memset(ptr, 0, 18);
|
||||
ptr[0] = 0xF0;
|
||||
ptr[2] = info->sense_key;
|
||||
ptr[7] = 11;
|
||||
ptr[12] = info->sense_asc;
|
||||
ptr[13] = info->sense_ascq;
|
||||
usb_stor_set_xfer_buf(ptr, 18, srb);
|
||||
|
||||
return USB_STOR_TRANSPORT_GOOD;
|
||||
}
|
||||
|
||||
if (srb->cmnd[0] == MODE_SENSE) {
|
||||
US_DEBUGP("jumpshot_transport: MODE_SENSE_6 detected\n");
|
||||
return jumpshot_handle_mode_sense(us, srb, 1);
|
||||
}
|
||||
|
||||
if (srb->cmnd[0] == MODE_SENSE_10) {
|
||||
US_DEBUGP("jumpshot_transport: MODE_SENSE_10 detected\n");
|
||||
return jumpshot_handle_mode_sense(us, srb, 0);
|
||||
}
|
||||
|
||||
if (srb->cmnd[0] == ALLOW_MEDIUM_REMOVAL) {
|
||||
// sure. whatever. not like we can stop the user from popping
|
||||
// the media out of the device (no locking doors, etc)
|
||||
//
|
||||
return USB_STOR_TRANSPORT_GOOD;
|
||||
}
|
||||
|
||||
if (srb->cmnd[0] == START_STOP) {
|
||||
/* this is used by sd.c'check_scsidisk_media_change to detect
|
||||
media change */
|
||||
US_DEBUGP("jumpshot_transport: START_STOP.\n");
|
||||
/* the first jumpshot_id_device after a media change returns
|
||||
an error (determined experimentally) */
|
||||
rc = jumpshot_id_device(us, info);
|
||||
if (rc == USB_STOR_TRANSPORT_GOOD) {
|
||||
info->sense_key = NO_SENSE;
|
||||
srb->result = SUCCESS;
|
||||
} else {
|
||||
info->sense_key = UNIT_ATTENTION;
|
||||
srb->result = SAM_STAT_CHECK_CONDITION;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
US_DEBUGP("jumpshot_transport: Gah! Unknown command: %d (0x%x)\n",
|
||||
srb->cmnd[0], srb->cmnd[0]);
|
||||
info->sense_key = 0x05;
|
||||
info->sense_asc = 0x20;
|
||||
info->sense_ascq = 0x00;
|
||||
return USB_STOR_TRANSPORT_FAILED;
|
||||
}
|
||||
39
drivers/usb/storage/jumpshot.h
Normal file
39
drivers/usb/storage/jumpshot.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/* Driver for Lexar "Jumpshot" USB Compact Flash reader
|
||||
* Header File
|
||||
*
|
||||
* Current development and maintenance by:
|
||||
* (c) 2000 Jimmie Mayfield (mayfield+usb@sackheads.org)
|
||||
*
|
||||
* See jumpshot.c for more explanation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef _USB_JUMPSHOT_H
|
||||
#define _USB_JUMPSHOT_H
|
||||
|
||||
extern int jumpshot_transport(struct scsi_cmnd *srb, struct us_data *us);
|
||||
|
||||
struct jumpshot_info {
|
||||
unsigned long sectors; // total sector count
|
||||
unsigned long ssize; // sector size in bytes
|
||||
|
||||
// the following aren't used yet
|
||||
unsigned char sense_key;
|
||||
unsigned long sense_asc; // additional sense code
|
||||
unsigned long sense_ascq; // additional sense code qualifier
|
||||
};
|
||||
|
||||
#endif
|
||||
155
drivers/usb/storage/karma.c
Normal file
155
drivers/usb/storage/karma.c
Normal file
@@ -0,0 +1,155 @@
|
||||
/* Driver for Rio Karma
|
||||
*
|
||||
* (c) 2006 Bob Copeland <me@bobcopeland.com>
|
||||
* (c) 2006 Keith Bennett <keith@mcs.st-and.ac.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <scsi/scsi.h>
|
||||
#include <scsi/scsi_cmnd.h>
|
||||
#include <scsi/scsi_device.h>
|
||||
|
||||
#include "usb.h"
|
||||
#include "transport.h"
|
||||
#include "debug.h"
|
||||
#include "karma.h"
|
||||
|
||||
#define RIO_PREFIX "RIOP\x00"
|
||||
#define RIO_PREFIX_LEN 5
|
||||
#define RIO_SEND_LEN 40
|
||||
#define RIO_RECV_LEN 0x200
|
||||
|
||||
#define RIO_ENTER_STORAGE 0x1
|
||||
#define RIO_LEAVE_STORAGE 0x2
|
||||
#define RIO_RESET 0xC
|
||||
|
||||
extern int usb_stor_Bulk_transport(struct scsi_cmnd *, struct us_data *);
|
||||
|
||||
struct karma_data {
|
||||
int in_storage;
|
||||
char *recv;
|
||||
};
|
||||
|
||||
/*
|
||||
* Send commands to Rio Karma.
|
||||
*
|
||||
* For each command we send 40 bytes starting 'RIOP\0' followed by
|
||||
* the command number and a sequence number, which the device will ack
|
||||
* with a 512-byte packet with the high four bits set and everything
|
||||
* else null. Then we send 'RIOP\x80' followed by a zero and the
|
||||
* sequence number, until byte 5 in the response repeats the sequence
|
||||
* number.
|
||||
*/
|
||||
static int rio_karma_send_command(char cmd, struct us_data *us)
|
||||
{
|
||||
int result, partial;
|
||||
unsigned long timeout;
|
||||
static unsigned char seq = 1;
|
||||
struct karma_data *data = (struct karma_data *) us->extra;
|
||||
|
||||
US_DEBUGP("karma: sending command %04x\n", cmd);
|
||||
memset(us->iobuf, 0, RIO_SEND_LEN);
|
||||
memcpy(us->iobuf, RIO_PREFIX, RIO_PREFIX_LEN);
|
||||
us->iobuf[5] = cmd;
|
||||
us->iobuf[6] = seq;
|
||||
|
||||
timeout = jiffies + msecs_to_jiffies(6000);
|
||||
for (;;) {
|
||||
result = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe,
|
||||
us->iobuf, RIO_SEND_LEN, &partial);
|
||||
if (result != USB_STOR_XFER_GOOD)
|
||||
goto err;
|
||||
|
||||
result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe,
|
||||
data->recv, RIO_RECV_LEN, &partial);
|
||||
if (result != USB_STOR_XFER_GOOD)
|
||||
goto err;
|
||||
|
||||
if (data->recv[5] == seq)
|
||||
break;
|
||||
|
||||
if (time_after(jiffies, timeout))
|
||||
goto err;
|
||||
|
||||
us->iobuf[4] = 0x80;
|
||||
us->iobuf[5] = 0;
|
||||
msleep(50);
|
||||
}
|
||||
|
||||
seq++;
|
||||
if (seq == 0)
|
||||
seq = 1;
|
||||
|
||||
US_DEBUGP("karma: sent command %04x\n", cmd);
|
||||
return 0;
|
||||
err:
|
||||
US_DEBUGP("karma: command %04x failed\n", cmd);
|
||||
return USB_STOR_TRANSPORT_FAILED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Trap START_STOP and READ_10 to leave/re-enter storage mode.
|
||||
* Everything else is propagated to the normal bulk layer.
|
||||
*/
|
||||
int rio_karma_transport(struct scsi_cmnd *srb, struct us_data *us)
|
||||
{
|
||||
int ret;
|
||||
struct karma_data *data = (struct karma_data *) us->extra;
|
||||
|
||||
if (srb->cmnd[0] == READ_10 && !data->in_storage) {
|
||||
ret = rio_karma_send_command(RIO_ENTER_STORAGE, us);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
data->in_storage = 1;
|
||||
return usb_stor_Bulk_transport(srb, us);
|
||||
} else if (srb->cmnd[0] == START_STOP) {
|
||||
ret = rio_karma_send_command(RIO_LEAVE_STORAGE, us);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
data->in_storage = 0;
|
||||
return rio_karma_send_command(RIO_RESET, us);
|
||||
}
|
||||
return usb_stor_Bulk_transport(srb, us);
|
||||
}
|
||||
|
||||
static void rio_karma_destructor(void *extra)
|
||||
{
|
||||
struct karma_data *data = (struct karma_data *) extra;
|
||||
kfree(data->recv);
|
||||
}
|
||||
|
||||
int rio_karma_init(struct us_data *us)
|
||||
{
|
||||
int ret = 0;
|
||||
struct karma_data *data = kzalloc(sizeof(struct karma_data), GFP_NOIO);
|
||||
if (!data)
|
||||
goto out;
|
||||
|
||||
data->recv = kmalloc(RIO_RECV_LEN, GFP_NOIO);
|
||||
if (!data->recv) {
|
||||
kfree(data);
|
||||
goto out;
|
||||
}
|
||||
|
||||
us->extra = data;
|
||||
us->extra_destructor = rio_karma_destructor;
|
||||
ret = rio_karma_send_command(RIO_ENTER_STORAGE, us);
|
||||
data->in_storage = (ret == 0);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
7
drivers/usb/storage/karma.h
Normal file
7
drivers/usb/storage/karma.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#ifndef _KARMA_USB_H
|
||||
#define _KARMA_USB_H
|
||||
|
||||
extern int rio_karma_init(struct us_data *us);
|
||||
extern int rio_karma_transport(struct scsi_cmnd *srb, struct us_data *us);
|
||||
|
||||
#endif
|
||||
266
drivers/usb/storage/libusual.c
Normal file
266
drivers/usb/storage/libusual.c
Normal file
@@ -0,0 +1,266 @@
|
||||
/*
|
||||
* libusual
|
||||
*
|
||||
* The libusual contains the table of devices common for ub and usb-storage.
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb_usual.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/kthread.h>
|
||||
|
||||
/*
|
||||
*/
|
||||
#define USU_MOD_FL_THREAD 1 /* Thread is running */
|
||||
#define USU_MOD_FL_PRESENT 2 /* The module is loaded */
|
||||
|
||||
struct mod_status {
|
||||
unsigned long fls;
|
||||
};
|
||||
|
||||
static struct mod_status stat[3];
|
||||
static DEFINE_SPINLOCK(usu_lock);
|
||||
|
||||
/*
|
||||
*/
|
||||
#define USB_US_DEFAULT_BIAS USB_US_TYPE_STOR
|
||||
static atomic_t usu_bias = ATOMIC_INIT(USB_US_DEFAULT_BIAS);
|
||||
|
||||
#define BIAS_NAME_SIZE (sizeof("usb-storage"))
|
||||
static const char *bias_names[3] = { "none", "usb-storage", "ub" };
|
||||
|
||||
static DECLARE_MUTEX_LOCKED(usu_init_notify);
|
||||
static DECLARE_COMPLETION(usu_end_notify);
|
||||
static atomic_t total_threads = ATOMIC_INIT(0);
|
||||
|
||||
static int usu_probe_thread(void *arg);
|
||||
|
||||
/*
|
||||
* The table.
|
||||
*/
|
||||
#define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \
|
||||
vendorName, productName,useProtocol, useTransport, \
|
||||
initFunction, flags) \
|
||||
{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin,bcdDeviceMax), \
|
||||
.driver_info = (flags)|(USB_US_TYPE_STOR<<24) }
|
||||
|
||||
#define USUAL_DEV(useProto, useTrans, useType) \
|
||||
{ USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, useProto, useTrans), \
|
||||
.driver_info = ((useType)<<24) }
|
||||
|
||||
struct usb_device_id storage_usb_ids [] = {
|
||||
# include "unusual_devs.h"
|
||||
{ } /* Terminating entry */
|
||||
};
|
||||
|
||||
#undef USUAL_DEV
|
||||
#undef UNUSUAL_DEV
|
||||
|
||||
MODULE_DEVICE_TABLE(usb, storage_usb_ids);
|
||||
EXPORT_SYMBOL_GPL(storage_usb_ids);
|
||||
|
||||
/*
|
||||
* @type: the module type as an integer
|
||||
*/
|
||||
void usb_usual_set_present(int type)
|
||||
{
|
||||
struct mod_status *st;
|
||||
unsigned long flags;
|
||||
|
||||
if (type <= 0 || type >= 3)
|
||||
return;
|
||||
st = &stat[type];
|
||||
spin_lock_irqsave(&usu_lock, flags);
|
||||
st->fls |= USU_MOD_FL_PRESENT;
|
||||
spin_unlock_irqrestore(&usu_lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_usual_set_present);
|
||||
|
||||
void usb_usual_clear_present(int type)
|
||||
{
|
||||
struct mod_status *st;
|
||||
unsigned long flags;
|
||||
|
||||
if (type <= 0 || type >= 3)
|
||||
return;
|
||||
st = &stat[type];
|
||||
spin_lock_irqsave(&usu_lock, flags);
|
||||
st->fls &= ~USU_MOD_FL_PRESENT;
|
||||
spin_unlock_irqrestore(&usu_lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_usual_clear_present);
|
||||
|
||||
/*
|
||||
* Match the calling driver type against the table.
|
||||
* Returns: 0 if the device matches.
|
||||
*/
|
||||
int usb_usual_check_type(const struct usb_device_id *id, int caller_type)
|
||||
{
|
||||
int id_type = USB_US_TYPE(id->driver_info);
|
||||
|
||||
if (caller_type <= 0 || caller_type >= 3)
|
||||
return -EINVAL;
|
||||
|
||||
/* Drivers grab fixed assignment devices */
|
||||
if (id_type == caller_type)
|
||||
return 0;
|
||||
/* Drivers grab devices biased to them */
|
||||
if (id_type == USB_US_TYPE_NONE && caller_type == atomic_read(&usu_bias))
|
||||
return 0;
|
||||
return -ENODEV;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_usual_check_type);
|
||||
|
||||
/*
|
||||
*/
|
||||
static int usu_probe(struct usb_interface *intf,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
unsigned long type;
|
||||
struct task_struct* task;
|
||||
unsigned long flags;
|
||||
|
||||
type = USB_US_TYPE(id->driver_info);
|
||||
if (type == 0)
|
||||
type = atomic_read(&usu_bias);
|
||||
|
||||
spin_lock_irqsave(&usu_lock, flags);
|
||||
if ((stat[type].fls & (USU_MOD_FL_THREAD|USU_MOD_FL_PRESENT)) != 0) {
|
||||
spin_unlock_irqrestore(&usu_lock, flags);
|
||||
return -ENXIO;
|
||||
}
|
||||
stat[type].fls |= USU_MOD_FL_THREAD;
|
||||
spin_unlock_irqrestore(&usu_lock, flags);
|
||||
|
||||
task = kthread_run(usu_probe_thread, (void*)type, "libusual_%d", type);
|
||||
if (IS_ERR(task)) {
|
||||
int rc = PTR_ERR(task);
|
||||
printk(KERN_WARNING "libusual: "
|
||||
"Unable to start the thread for %s: %d\n",
|
||||
bias_names[type], rc);
|
||||
spin_lock_irqsave(&usu_lock, flags);
|
||||
stat[type].fls &= ~USU_MOD_FL_THREAD;
|
||||
spin_unlock_irqrestore(&usu_lock, flags);
|
||||
return rc; /* Not being -ENXIO causes a message printed */
|
||||
}
|
||||
atomic_inc(&total_threads);
|
||||
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static void usu_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
; /* We should not be here. */
|
||||
}
|
||||
|
||||
static struct usb_driver usu_driver = {
|
||||
.name = "libusual",
|
||||
.probe = usu_probe,
|
||||
.disconnect = usu_disconnect,
|
||||
.id_table = storage_usb_ids,
|
||||
};
|
||||
|
||||
/*
|
||||
* A whole new thread for a purpose of request_module seems quite stupid.
|
||||
* The request_module forks once inside again. However, if we attempt
|
||||
* to load a storage module from our own modprobe thread, that module
|
||||
* references our symbols, which cannot be resolved until our module is
|
||||
* initialized. I wish there was a way to wait for the end of initialization.
|
||||
* The module notifier reports MODULE_STATE_COMING only.
|
||||
* So, we wait until module->init ends as the next best thing.
|
||||
*/
|
||||
static int usu_probe_thread(void *arg)
|
||||
{
|
||||
int type = (unsigned long) arg;
|
||||
struct mod_status *st = &stat[type];
|
||||
int rc;
|
||||
unsigned long flags;
|
||||
|
||||
/* A completion does not work here because it's counted. */
|
||||
down(&usu_init_notify);
|
||||
up(&usu_init_notify);
|
||||
|
||||
rc = request_module(bias_names[type]);
|
||||
spin_lock_irqsave(&usu_lock, flags);
|
||||
if (rc == 0 && (st->fls & USU_MOD_FL_PRESENT) == 0) {
|
||||
/*
|
||||
* This should not happen, but let us keep tabs on it.
|
||||
*/
|
||||
printk(KERN_NOTICE "libusual: "
|
||||
"modprobe for %s succeeded, but module is not present\n",
|
||||
bias_names[type]);
|
||||
}
|
||||
st->fls &= ~USU_MOD_FL_THREAD;
|
||||
spin_unlock_irqrestore(&usu_lock, flags);
|
||||
|
||||
complete_and_exit(&usu_end_notify, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
*/
|
||||
static int __init usb_usual_init(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = usb_register(&usu_driver);
|
||||
up(&usu_init_notify);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void __exit usb_usual_exit(void)
|
||||
{
|
||||
/*
|
||||
* We do not check for any drivers present, because
|
||||
* they keep us pinned with symbol references.
|
||||
*/
|
||||
|
||||
usb_deregister(&usu_driver);
|
||||
|
||||
while (atomic_read(&total_threads) > 0) {
|
||||
wait_for_completion(&usu_end_notify);
|
||||
atomic_dec(&total_threads);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Validate and accept the bias parameter.
|
||||
*/
|
||||
static int usu_set_bias(const char *bias_s, struct kernel_param *kp)
|
||||
{
|
||||
int i;
|
||||
int len;
|
||||
int bias_n = 0;
|
||||
|
||||
len = strlen(bias_s);
|
||||
if (len == 0)
|
||||
return -EDOM;
|
||||
if (bias_s[len-1] == '\n')
|
||||
--len;
|
||||
|
||||
for (i = 1; i < 3; i++) {
|
||||
if (strncmp(bias_s, bias_names[i], len) == 0) {
|
||||
bias_n = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (bias_n == 0)
|
||||
return -EINVAL;
|
||||
|
||||
atomic_set(&usu_bias, bias_n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usu_get_bias(char *buffer, struct kernel_param *kp)
|
||||
{
|
||||
return strlen(strcpy(buffer, bias_names[atomic_read(&usu_bias)]));
|
||||
}
|
||||
|
||||
module_init(usb_usual_init);
|
||||
module_exit(usb_usual_exit);
|
||||
|
||||
module_param_call(bias, usu_set_bias, usu_get_bias, NULL, S_IRUGO|S_IWUSR);
|
||||
__MODULE_PARM_TYPE(bias, "string");
|
||||
MODULE_PARM_DESC(bias, "Bias to usb-storage or ub");
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
235
drivers/usb/storage/onetouch.c
Normal file
235
drivers/usb/storage/onetouch.c
Normal file
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
* Support for the Maxtor OneTouch USB hard drive's button
|
||||
*
|
||||
* Current development and maintenance by:
|
||||
* Copyright (c) 2005 Nick Sillik <n.sillik@temple.edu>
|
||||
*
|
||||
* Initial work by:
|
||||
* Copyright (c) 2003 Erik Thyren <erth7411@student.uu.se>
|
||||
*
|
||||
* Based on usbmouse.c (Vojtech Pavlik) and xpad.c (Marko Friedemann)
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/usb/input.h>
|
||||
#include "usb.h"
|
||||
#include "onetouch.h"
|
||||
#include "debug.h"
|
||||
|
||||
void onetouch_release_input(void *onetouch_);
|
||||
|
||||
struct usb_onetouch {
|
||||
char name[128];
|
||||
char phys[64];
|
||||
struct input_dev *dev; /* input device interface */
|
||||
struct usb_device *udev; /* usb device */
|
||||
|
||||
struct urb *irq; /* urb for interrupt in report */
|
||||
unsigned char *data; /* input data */
|
||||
dma_addr_t data_dma;
|
||||
unsigned int is_open:1;
|
||||
};
|
||||
|
||||
static void usb_onetouch_irq(struct urb *urb)
|
||||
{
|
||||
struct usb_onetouch *onetouch = urb->context;
|
||||
signed char *data = onetouch->data;
|
||||
struct input_dev *dev = onetouch->dev;
|
||||
int status;
|
||||
|
||||
switch (urb->status) {
|
||||
case 0: /* success */
|
||||
break;
|
||||
case -ECONNRESET: /* unlink */
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
return;
|
||||
/* -EPIPE: should clear the halt */
|
||||
default: /* error */
|
||||
goto resubmit;
|
||||
}
|
||||
|
||||
input_report_key(dev, ONETOUCH_BUTTON, data[0] & 0x02);
|
||||
input_sync(dev);
|
||||
|
||||
resubmit:
|
||||
status = usb_submit_urb (urb, GFP_ATOMIC);
|
||||
if (status)
|
||||
err ("can't resubmit intr, %s-%s/input0, status %d",
|
||||
onetouch->udev->bus->bus_name,
|
||||
onetouch->udev->devpath, status);
|
||||
}
|
||||
|
||||
static int usb_onetouch_open(struct input_dev *dev)
|
||||
{
|
||||
struct usb_onetouch *onetouch = dev->private;
|
||||
|
||||
onetouch->is_open = 1;
|
||||
onetouch->irq->dev = onetouch->udev;
|
||||
if (usb_submit_urb(onetouch->irq, GFP_KERNEL)) {
|
||||
err("usb_submit_urb failed");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usb_onetouch_close(struct input_dev *dev)
|
||||
{
|
||||
struct usb_onetouch *onetouch = dev->private;
|
||||
|
||||
usb_kill_urb(onetouch->irq);
|
||||
onetouch->is_open = 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static void usb_onetouch_pm_hook(struct us_data *us, int action)
|
||||
{
|
||||
struct usb_onetouch *onetouch = (struct usb_onetouch *) us->extra;
|
||||
|
||||
if (onetouch->is_open) {
|
||||
switch (action) {
|
||||
case US_SUSPEND:
|
||||
usb_kill_urb(onetouch->irq);
|
||||
break;
|
||||
case US_RESUME:
|
||||
if (usb_submit_urb(onetouch->irq, GFP_KERNEL) != 0)
|
||||
err("usb_submit_urb failed");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
int onetouch_connect_input(struct us_data *ss)
|
||||
{
|
||||
struct usb_device *udev = ss->pusb_dev;
|
||||
struct usb_host_interface *interface;
|
||||
struct usb_endpoint_descriptor *endpoint;
|
||||
struct usb_onetouch *onetouch;
|
||||
struct input_dev *input_dev;
|
||||
int pipe, maxp;
|
||||
int error = -ENOMEM;
|
||||
|
||||
interface = ss->pusb_intf->cur_altsetting;
|
||||
|
||||
if (interface->desc.bNumEndpoints != 3)
|
||||
return -ENODEV;
|
||||
|
||||
endpoint = &interface->endpoint[2].desc;
|
||||
if (!usb_endpoint_is_int_in(endpoint))
|
||||
return -ENODEV;
|
||||
|
||||
pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress);
|
||||
maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
|
||||
|
||||
onetouch = kzalloc(sizeof(struct usb_onetouch), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!onetouch || !input_dev)
|
||||
goto fail1;
|
||||
|
||||
onetouch->data = usb_buffer_alloc(udev, ONETOUCH_PKT_LEN,
|
||||
GFP_ATOMIC, &onetouch->data_dma);
|
||||
if (!onetouch->data)
|
||||
goto fail1;
|
||||
|
||||
onetouch->irq = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!onetouch->irq)
|
||||
goto fail2;
|
||||
|
||||
onetouch->udev = udev;
|
||||
onetouch->dev = input_dev;
|
||||
|
||||
if (udev->manufacturer)
|
||||
strlcpy(onetouch->name, udev->manufacturer,
|
||||
sizeof(onetouch->name));
|
||||
if (udev->product) {
|
||||
if (udev->manufacturer)
|
||||
strlcat(onetouch->name, " ", sizeof(onetouch->name));
|
||||
strlcat(onetouch->name, udev->product, sizeof(onetouch->name));
|
||||
}
|
||||
|
||||
if (!strlen(onetouch->name))
|
||||
snprintf(onetouch->name, sizeof(onetouch->name),
|
||||
"Maxtor Onetouch %04x:%04x",
|
||||
le16_to_cpu(udev->descriptor.idVendor),
|
||||
le16_to_cpu(udev->descriptor.idProduct));
|
||||
|
||||
usb_make_path(udev, onetouch->phys, sizeof(onetouch->phys));
|
||||
strlcat(onetouch->phys, "/input0", sizeof(onetouch->phys));
|
||||
|
||||
input_dev->name = onetouch->name;
|
||||
input_dev->phys = onetouch->phys;
|
||||
usb_to_input_id(udev, &input_dev->id);
|
||||
input_dev->cdev.dev = &udev->dev;
|
||||
|
||||
set_bit(EV_KEY, input_dev->evbit);
|
||||
set_bit(ONETOUCH_BUTTON, input_dev->keybit);
|
||||
clear_bit(0, input_dev->keybit);
|
||||
|
||||
input_dev->private = onetouch;
|
||||
input_dev->open = usb_onetouch_open;
|
||||
input_dev->close = usb_onetouch_close;
|
||||
|
||||
usb_fill_int_urb(onetouch->irq, udev, pipe, onetouch->data,
|
||||
(maxp > 8 ? 8 : maxp),
|
||||
usb_onetouch_irq, onetouch, endpoint->bInterval);
|
||||
onetouch->irq->transfer_dma = onetouch->data_dma;
|
||||
onetouch->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
|
||||
ss->extra_destructor = onetouch_release_input;
|
||||
ss->extra = onetouch;
|
||||
#ifdef CONFIG_PM
|
||||
ss->suspend_resume_hook = usb_onetouch_pm_hook;
|
||||
#endif
|
||||
|
||||
error = input_register_device(onetouch->dev);
|
||||
if (error)
|
||||
goto fail3;
|
||||
|
||||
return 0;
|
||||
|
||||
fail3: usb_free_urb(onetouch->irq);
|
||||
fail2: usb_buffer_free(udev, ONETOUCH_PKT_LEN,
|
||||
onetouch->data, onetouch->data_dma);
|
||||
fail1: kfree(onetouch);
|
||||
input_free_device(input_dev);
|
||||
return error;
|
||||
}
|
||||
|
||||
void onetouch_release_input(void *onetouch_)
|
||||
{
|
||||
struct usb_onetouch *onetouch = (struct usb_onetouch *) onetouch_;
|
||||
|
||||
if (onetouch) {
|
||||
usb_kill_urb(onetouch->irq);
|
||||
input_unregister_device(onetouch->dev);
|
||||
usb_free_urb(onetouch->irq);
|
||||
usb_buffer_free(onetouch->udev, ONETOUCH_PKT_LEN,
|
||||
onetouch->data, onetouch->data_dma);
|
||||
}
|
||||
}
|
||||
9
drivers/usb/storage/onetouch.h
Normal file
9
drivers/usb/storage/onetouch.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#ifndef _ONETOUCH_H_
|
||||
#define _ONETOUCH_H_
|
||||
|
||||
#define ONETOUCH_PKT_LEN 0x02
|
||||
#define ONETOUCH_BUTTON KEY_PROG1
|
||||
|
||||
int onetouch_connect_input(struct us_data *ss);
|
||||
|
||||
#endif
|
||||
254
drivers/usb/storage/protocol.c
Normal file
254
drivers/usb/storage/protocol.c
Normal file
@@ -0,0 +1,254 @@
|
||||
/* Driver for USB Mass Storage compliant devices
|
||||
*
|
||||
* $Id: protocol.c,v 1.1.1.1 2007/06/12 07:27:09 eyryu Exp $
|
||||
*
|
||||
* Current development and maintenance by:
|
||||
* (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
|
||||
*
|
||||
* Developed with the assistance of:
|
||||
* (c) 2000 David L. Brown, Jr. (usb-storage@davidb.org)
|
||||
* (c) 2002 Alan Stern (stern@rowland.org)
|
||||
*
|
||||
* Initial work by:
|
||||
* (c) 1999 Michael Gee (michael@linuxspecific.com)
|
||||
*
|
||||
* This driver is based on the 'USB Mass Storage Class' document. This
|
||||
* describes in detail the protocol used to communicate with such
|
||||
* devices. Clearly, the designers had SCSI and ATAPI commands in
|
||||
* mind when they created this document. The commands are all very
|
||||
* similar to commands in the SCSI-II and ATAPI specifications.
|
||||
*
|
||||
* It is important to note that in a number of cases this class
|
||||
* exhibits class-specific exemptions from the USB specification.
|
||||
* Notably the usage of NAK, STALL and ACK differs from the norm, in
|
||||
* that they are used to communicate wait, failed and OK on commands.
|
||||
*
|
||||
* Also, for certain devices, the interrupt endpoint is used to convey
|
||||
* status of a command.
|
||||
*
|
||||
* Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more
|
||||
* information about this driver.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/highmem.h>
|
||||
#include <scsi/scsi.h>
|
||||
#include <scsi/scsi_cmnd.h>
|
||||
|
||||
#include "usb.h"
|
||||
#include "protocol.h"
|
||||
#include "debug.h"
|
||||
#include "scsiglue.h"
|
||||
#include "transport.h"
|
||||
|
||||
/***********************************************************************
|
||||
* Protocol routines
|
||||
***********************************************************************/
|
||||
|
||||
void usb_stor_qic157_command(struct scsi_cmnd *srb, struct us_data *us)
|
||||
{
|
||||
/* Pad the ATAPI command with zeros
|
||||
*
|
||||
* NOTE: This only works because a scsi_cmnd struct field contains
|
||||
* a unsigned char cmnd[16], so we know we have storage available
|
||||
*/
|
||||
for (; srb->cmd_len<12; srb->cmd_len++)
|
||||
srb->cmnd[srb->cmd_len] = 0;
|
||||
|
||||
/* set command length to 12 bytes */
|
||||
srb->cmd_len = 12;
|
||||
|
||||
/* send the command to the transport layer */
|
||||
usb_stor_invoke_transport(srb, us);
|
||||
}
|
||||
|
||||
void usb_stor_ATAPI_command(struct scsi_cmnd *srb, struct us_data *us)
|
||||
{
|
||||
/* Pad the ATAPI command with zeros
|
||||
*
|
||||
* NOTE: This only works because a scsi_cmnd struct field contains
|
||||
* a unsigned char cmnd[16], so we know we have storage available
|
||||
*/
|
||||
|
||||
/* Pad the ATAPI command with zeros */
|
||||
for (; srb->cmd_len<12; srb->cmd_len++)
|
||||
srb->cmnd[srb->cmd_len] = 0;
|
||||
|
||||
/* set command length to 12 bytes */
|
||||
srb->cmd_len = 12;
|
||||
|
||||
/* send the command to the transport layer */
|
||||
usb_stor_invoke_transport(srb, us);
|
||||
}
|
||||
|
||||
|
||||
void usb_stor_ufi_command(struct scsi_cmnd *srb, struct us_data *us)
|
||||
{
|
||||
/* fix some commands -- this is a form of mode translation
|
||||
* UFI devices only accept 12 byte long commands
|
||||
*
|
||||
* NOTE: This only works because a scsi_cmnd struct field contains
|
||||
* a unsigned char cmnd[16], so we know we have storage available
|
||||
*/
|
||||
|
||||
/* Pad the ATAPI command with zeros */
|
||||
for (; srb->cmd_len<12; srb->cmd_len++)
|
||||
srb->cmnd[srb->cmd_len] = 0;
|
||||
|
||||
/* set command length to 12 bytes (this affects the transport layer) */
|
||||
srb->cmd_len = 12;
|
||||
|
||||
/* XXX We should be constantly re-evaluating the need for these */
|
||||
|
||||
/* determine the correct data length for these commands */
|
||||
switch (srb->cmnd[0]) {
|
||||
|
||||
/* for INQUIRY, UFI devices only ever return 36 bytes */
|
||||
case INQUIRY:
|
||||
srb->cmnd[4] = 36;
|
||||
break;
|
||||
|
||||
/* again, for MODE_SENSE_10, we get the minimum (8) */
|
||||
case MODE_SENSE_10:
|
||||
srb->cmnd[7] = 0;
|
||||
srb->cmnd[8] = 8;
|
||||
break;
|
||||
|
||||
/* for REQUEST_SENSE, UFI devices only ever return 18 bytes */
|
||||
case REQUEST_SENSE:
|
||||
srb->cmnd[4] = 18;
|
||||
break;
|
||||
} /* end switch on cmnd[0] */
|
||||
|
||||
/* send the command to the transport layer */
|
||||
usb_stor_invoke_transport(srb, us);
|
||||
}
|
||||
|
||||
void usb_stor_transparent_scsi_command(struct scsi_cmnd *srb,
|
||||
struct us_data *us)
|
||||
{
|
||||
/* send the command to the transport layer */
|
||||
usb_stor_invoke_transport(srb, us);
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* Scatter-gather transfer buffer access routines
|
||||
***********************************************************************/
|
||||
|
||||
/* Copy a buffer of length buflen to/from the srb's transfer buffer.
|
||||
* (Note: for scatter-gather transfers (srb->use_sg > 0), srb->request_buffer
|
||||
* points to a list of s-g entries and we ignore srb->request_bufflen.
|
||||
* For non-scatter-gather transfers, srb->request_buffer points to the
|
||||
* transfer buffer itself and srb->request_bufflen is the buffer's length.)
|
||||
* Update the *index and *offset variables so that the next copy will
|
||||
* pick up from where this one left off. */
|
||||
|
||||
unsigned int usb_stor_access_xfer_buf(unsigned char *buffer,
|
||||
unsigned int buflen, struct scsi_cmnd *srb, unsigned int *index,
|
||||
unsigned int *offset, enum xfer_buf_dir dir)
|
||||
{
|
||||
unsigned int cnt;
|
||||
|
||||
/* If not using scatter-gather, just transfer the data directly.
|
||||
* Make certain it will fit in the available buffer space. */
|
||||
if (srb->use_sg == 0) {
|
||||
if (*offset >= srb->request_bufflen)
|
||||
return 0;
|
||||
cnt = min(buflen, srb->request_bufflen - *offset);
|
||||
if (dir == TO_XFER_BUF)
|
||||
memcpy((unsigned char *) srb->request_buffer + *offset,
|
||||
buffer, cnt);
|
||||
else
|
||||
memcpy(buffer, (unsigned char *) srb->request_buffer +
|
||||
*offset, cnt);
|
||||
*offset += cnt;
|
||||
|
||||
/* Using scatter-gather. We have to go through the list one entry
|
||||
* at a time. Each s-g entry contains some number of pages, and
|
||||
* each page has to be kmap()'ed separately. If the page is already
|
||||
* in kernel-addressable memory then kmap() will return its address.
|
||||
* If the page is not directly accessible -- such as a user buffer
|
||||
* located in high memory -- then kmap() will map it to a temporary
|
||||
* position in the kernel's virtual address space. */
|
||||
} else {
|
||||
struct scatterlist *sg =
|
||||
(struct scatterlist *) srb->request_buffer
|
||||
+ *index;
|
||||
|
||||
/* This loop handles a single s-g list entry, which may
|
||||
* include multiple pages. Find the initial page structure
|
||||
* and the starting offset within the page, and update
|
||||
* the *offset and *index values for the next loop. */
|
||||
cnt = 0;
|
||||
while (cnt < buflen && *index < srb->use_sg) {
|
||||
struct page *page = sg->page +
|
||||
((sg->offset + *offset) >> PAGE_SHIFT);
|
||||
unsigned int poff =
|
||||
(sg->offset + *offset) & (PAGE_SIZE-1);
|
||||
unsigned int sglen = sg->length - *offset;
|
||||
|
||||
if (sglen > buflen - cnt) {
|
||||
|
||||
/* Transfer ends within this s-g entry */
|
||||
sglen = buflen - cnt;
|
||||
*offset += sglen;
|
||||
} else {
|
||||
|
||||
/* Transfer continues to next s-g entry */
|
||||
*offset = 0;
|
||||
++*index;
|
||||
++sg;
|
||||
}
|
||||
|
||||
/* Transfer the data for all the pages in this
|
||||
* s-g entry. For each page: call kmap(), do the
|
||||
* transfer, and call kunmap() immediately after. */
|
||||
while (sglen > 0) {
|
||||
unsigned int plen = min(sglen, (unsigned int)
|
||||
PAGE_SIZE - poff);
|
||||
unsigned char *ptr = kmap(page);
|
||||
|
||||
if (dir == TO_XFER_BUF)
|
||||
memcpy(ptr + poff, buffer + cnt, plen);
|
||||
else
|
||||
memcpy(buffer + cnt, ptr + poff, plen);
|
||||
kunmap(page);
|
||||
|
||||
/* Start at the beginning of the next page */
|
||||
poff = 0;
|
||||
++page;
|
||||
cnt += plen;
|
||||
sglen -= plen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Return the amount actually transferred */
|
||||
return cnt;
|
||||
}
|
||||
|
||||
/* Store the contents of buffer into srb's transfer buffer and set the
|
||||
* SCSI residue. */
|
||||
void usb_stor_set_xfer_buf(unsigned char *buffer,
|
||||
unsigned int buflen, struct scsi_cmnd *srb)
|
||||
{
|
||||
unsigned int index = 0, offset = 0;
|
||||
|
||||
usb_stor_access_xfer_buf(buffer, buflen, srb, &index, &offset,
|
||||
TO_XFER_BUF);
|
||||
if (buflen < srb->request_bufflen)
|
||||
srb->resid = srb->request_bufflen - buflen;
|
||||
}
|
||||
60
drivers/usb/storage/protocol.h
Normal file
60
drivers/usb/storage/protocol.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/* Driver for USB Mass Storage compliant devices
|
||||
* Protocol Functions Header File
|
||||
*
|
||||
* $Id: protocol.h,v 1.1.1.1 2007/06/12 07:27:09 eyryu Exp $
|
||||
*
|
||||
* Current development and maintenance by:
|
||||
* (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
|
||||
*
|
||||
* This driver is based on the 'USB Mass Storage Class' document. This
|
||||
* describes in detail the protocol used to communicate with such
|
||||
* devices. Clearly, the designers had SCSI and ATAPI commands in
|
||||
* mind when they created this document. The commands are all very
|
||||
* similar to commands in the SCSI-II and ATAPI specifications.
|
||||
*
|
||||
* It is important to note that in a number of cases this class
|
||||
* exhibits class-specific exemptions from the USB specification.
|
||||
* Notably the usage of NAK, STALL and ACK differs from the norm, in
|
||||
* that they are used to communicate wait, failed and OK on commands.
|
||||
*
|
||||
* Also, for certain devices, the interrupt endpoint is used to convey
|
||||
* status of a command.
|
||||
*
|
||||
* Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more
|
||||
* information about this driver.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef _PROTOCOL_H_
|
||||
#define _PROTOCOL_H_
|
||||
|
||||
/* Protocol handling routines */
|
||||
extern void usb_stor_ATAPI_command(struct scsi_cmnd*, struct us_data*);
|
||||
extern void usb_stor_qic157_command(struct scsi_cmnd*, struct us_data*);
|
||||
extern void usb_stor_ufi_command(struct scsi_cmnd*, struct us_data*);
|
||||
extern void usb_stor_transparent_scsi_command(struct scsi_cmnd*,
|
||||
struct us_data*);
|
||||
|
||||
/* struct scsi_cmnd transfer buffer access utilities */
|
||||
enum xfer_buf_dir {TO_XFER_BUF, FROM_XFER_BUF};
|
||||
|
||||
extern unsigned int usb_stor_access_xfer_buf(unsigned char *buffer,
|
||||
unsigned int buflen, struct scsi_cmnd *srb, unsigned int *index,
|
||||
unsigned int *offset, enum xfer_buf_dir dir);
|
||||
|
||||
extern void usb_stor_set_xfer_buf(unsigned char *buffer,
|
||||
unsigned int buflen, struct scsi_cmnd *srb);
|
||||
#endif
|
||||
498
drivers/usb/storage/scsiglue.c
Normal file
498
drivers/usb/storage/scsiglue.c
Normal file
@@ -0,0 +1,498 @@
|
||||
/* Driver for USB Mass Storage compliant devices
|
||||
* SCSI layer glue code
|
||||
*
|
||||
* $Id: scsiglue.c,v 1.1.1.1 2007/06/12 07:27:09 eyryu Exp $
|
||||
*
|
||||
* Current development and maintenance by:
|
||||
* (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
|
||||
*
|
||||
* Developed with the assistance of:
|
||||
* (c) 2000 David L. Brown, Jr. (usb-storage@davidb.org)
|
||||
* (c) 2000 Stephen J. Gowdy (SGowdy@lbl.gov)
|
||||
*
|
||||
* Initial work by:
|
||||
* (c) 1999 Michael Gee (michael@linuxspecific.com)
|
||||
*
|
||||
* This driver is based on the 'USB Mass Storage Class' document. This
|
||||
* describes in detail the protocol used to communicate with such
|
||||
* devices. Clearly, the designers had SCSI and ATAPI commands in
|
||||
* mind when they created this document. The commands are all very
|
||||
* similar to commands in the SCSI-II and ATAPI specifications.
|
||||
*
|
||||
* It is important to note that in a number of cases this class
|
||||
* exhibits class-specific exemptions from the USB specification.
|
||||
* Notably the usage of NAK, STALL and ACK differs from the norm, in
|
||||
* that they are used to communicate wait, failed and OK on commands.
|
||||
*
|
||||
* Also, for certain devices, the interrupt endpoint is used to convey
|
||||
* status of a command.
|
||||
*
|
||||
* Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more
|
||||
* information about this driver.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#include <scsi/scsi.h>
|
||||
#include <scsi/scsi_cmnd.h>
|
||||
#include <scsi/scsi_devinfo.h>
|
||||
#include <scsi/scsi_device.h>
|
||||
#include <scsi/scsi_eh.h>
|
||||
|
||||
#include "usb.h"
|
||||
#include "scsiglue.h"
|
||||
#include "debug.h"
|
||||
#include "transport.h"
|
||||
#include "protocol.h"
|
||||
|
||||
/***********************************************************************
|
||||
* Host functions
|
||||
***********************************************************************/
|
||||
|
||||
static const char* host_info(struct Scsi_Host *host)
|
||||
{
|
||||
return "SCSI emulation for USB Mass Storage devices";
|
||||
}
|
||||
|
||||
static int slave_alloc (struct scsi_device *sdev)
|
||||
{
|
||||
struct us_data *us = host_to_us(sdev->host);
|
||||
|
||||
/*
|
||||
* Set the INQUIRY transfer length to 36. We don't use any of
|
||||
* the extra data and many devices choke if asked for more or
|
||||
* less than 36 bytes.
|
||||
*/
|
||||
sdev->inquiry_len = 36;
|
||||
|
||||
/*
|
||||
* The UFI spec treates the Peripheral Qualifier bits in an
|
||||
* INQUIRY result as reserved and requires devices to set them
|
||||
* to 0. However the SCSI spec requires these bits to be set
|
||||
* to 3 to indicate when a LUN is not present.
|
||||
*
|
||||
* Let the scanning code know if this target merely sets
|
||||
* Peripheral Device Type to 0x1f to indicate no LUN.
|
||||
*/
|
||||
if (us->subclass == US_SC_UFI)
|
||||
sdev->sdev_target->pdt_1f_for_no_lun = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int slave_configure(struct scsi_device *sdev)
|
||||
{
|
||||
struct us_data *us = host_to_us(sdev->host);
|
||||
|
||||
/* Scatter-gather buffers (all but the last) must have a length
|
||||
* divisible by the bulk maxpacket size. Otherwise a data packet
|
||||
* would end up being short, causing a premature end to the data
|
||||
* transfer. Since high-speed bulk pipes have a maxpacket size
|
||||
* of 512, we'll use that as the scsi device queue's DMA alignment
|
||||
* mask. Guaranteeing proper alignment of the first buffer will
|
||||
* have the desired effect because, except at the beginning and
|
||||
* the end, scatter-gather buffers follow page boundaries. */
|
||||
blk_queue_dma_alignment(sdev->request_queue, (512 - 1));
|
||||
|
||||
/* Many devices have trouble transfering more than 32KB at a time,
|
||||
* while others have trouble with more than 64K. At this time we
|
||||
* are limiting both to 32K (64 sectores).
|
||||
*/
|
||||
if ((us->flags & US_FL_MAX_SECTORS_64) &&
|
||||
sdev->request_queue->max_sectors > 64)
|
||||
blk_queue_max_sectors(sdev->request_queue, 64);
|
||||
|
||||
/* We can't put these settings in slave_alloc() because that gets
|
||||
* called before the device type is known. Consequently these
|
||||
* settings can't be overridden via the scsi devinfo mechanism. */
|
||||
if (sdev->type == TYPE_DISK) {
|
||||
|
||||
/* Disk-type devices use MODE SENSE(6) if the protocol
|
||||
* (SubClass) is Transparent SCSI, otherwise they use
|
||||
* MODE SENSE(10). */
|
||||
if (us->subclass != US_SC_SCSI)
|
||||
sdev->use_10_for_ms = 1;
|
||||
|
||||
/* Many disks only accept MODE SENSE transfer lengths of
|
||||
* 192 bytes (that's what Windows uses). */
|
||||
sdev->use_192_bytes_for_3f = 1;
|
||||
|
||||
/* Some devices don't like MODE SENSE with page=0x3f,
|
||||
* which is the command used for checking if a device
|
||||
* is write-protected. Now that we tell the sd driver
|
||||
* to do a 192-byte transfer with this command the
|
||||
* majority of devices work fine, but a few still can't
|
||||
* handle it. The sd driver will simply assume those
|
||||
* devices are write-enabled. */
|
||||
if (us->flags & US_FL_NO_WP_DETECT)
|
||||
sdev->skip_ms_page_3f = 1;
|
||||
|
||||
/* A number of devices have problems with MODE SENSE for
|
||||
* page x08, so we will skip it. */
|
||||
sdev->skip_ms_page_8 = 1;
|
||||
|
||||
/* Some disks return the total number of blocks in response
|
||||
* to READ CAPACITY rather than the highest block number.
|
||||
* If this device makes that mistake, tell the sd driver. */
|
||||
if (us->flags & US_FL_FIX_CAPACITY)
|
||||
sdev->fix_capacity = 1;
|
||||
|
||||
/* A few disks have two indistinguishable version, one of
|
||||
* which reports the correct capacity and the other does not.
|
||||
* The sd driver has to guess which is the case. */
|
||||
if (us->flags & US_FL_CAPACITY_HEURISTICS)
|
||||
sdev->guess_capacity = 1;
|
||||
|
||||
/* Some devices report a SCSI revision level above 2 but are
|
||||
* unable to handle the REPORT LUNS command (for which
|
||||
* support is mandatory at level 3). Since we already have
|
||||
* a Get-Max-LUN request, we won't lose much by setting the
|
||||
* revision level down to 2. The only devices that would be
|
||||
* affected are those with sparse LUNs. */
|
||||
if (sdev->scsi_level > SCSI_2)
|
||||
sdev->sdev_target->scsi_level =
|
||||
sdev->scsi_level = SCSI_2;
|
||||
|
||||
/* USB-IDE bridges tend to report SK = 0x04 (Non-recoverable
|
||||
* Hardware Error) when any low-level error occurs,
|
||||
* recoverable or not. Setting this flag tells the SCSI
|
||||
* midlayer to retry such commands, which frequently will
|
||||
* succeed and fix the error. The worst this can lead to
|
||||
* is an occasional series of retries that will all fail. */
|
||||
sdev->retry_hwerror = 1;
|
||||
|
||||
} else {
|
||||
|
||||
/* Non-disk-type devices don't need to blacklist any pages
|
||||
* or to force 192-byte transfer lengths for MODE SENSE.
|
||||
* But they do need to use MODE SENSE(10). */
|
||||
sdev->use_10_for_ms = 1;
|
||||
}
|
||||
|
||||
/* The CB and CBI transports have no way to pass LUN values
|
||||
* other than the bits in the second byte of a CDB. But those
|
||||
* bits don't get set to the LUN value if the device reports
|
||||
* scsi_level == 0 (UNKNOWN). Hence such devices must necessarily
|
||||
* be single-LUN.
|
||||
*/
|
||||
if ((us->protocol == US_PR_CB || us->protocol == US_PR_CBI) &&
|
||||
sdev->scsi_level == SCSI_UNKNOWN)
|
||||
us->max_lun = 0;
|
||||
|
||||
/* Some devices choke when they receive a PREVENT-ALLOW MEDIUM
|
||||
* REMOVAL command, so suppress those commands. */
|
||||
if (us->flags & US_FL_NOT_LOCKABLE)
|
||||
sdev->lockable = 0;
|
||||
|
||||
/* this is to satisfy the compiler, tho I don't think the
|
||||
* return code is ever checked anywhere. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* queue a command */
|
||||
/* This is always called with scsi_lock(host) held */
|
||||
static int queuecommand(struct scsi_cmnd *srb,
|
||||
void (*done)(struct scsi_cmnd *))
|
||||
{
|
||||
struct us_data *us = host_to_us(srb->device->host);
|
||||
|
||||
US_DEBUGP("%s called\n", __FUNCTION__);
|
||||
|
||||
/* check for state-transition errors */
|
||||
if (us->srb != NULL) {
|
||||
printk(KERN_ERR USB_STORAGE "Error in %s: us->srb = %p\n",
|
||||
__FUNCTION__, us->srb);
|
||||
return SCSI_MLQUEUE_HOST_BUSY;
|
||||
}
|
||||
|
||||
/* fail the command if we are disconnecting */
|
||||
if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) {
|
||||
US_DEBUGP("Fail command during disconnect\n");
|
||||
srb->result = DID_NO_CONNECT << 16;
|
||||
done(srb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* enqueue the command and wake up the control thread */
|
||||
srb->scsi_done = done;
|
||||
us->srb = srb;
|
||||
up(&(us->sema));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* Error handling functions
|
||||
***********************************************************************/
|
||||
|
||||
/* Command timeout and abort */
|
||||
static int command_abort(struct scsi_cmnd *srb)
|
||||
{
|
||||
struct us_data *us = host_to_us(srb->device->host);
|
||||
|
||||
US_DEBUGP("%s called\n", __FUNCTION__);
|
||||
|
||||
/* us->srb together with the TIMED_OUT, RESETTING, and ABORTING
|
||||
* bits are protected by the host lock. */
|
||||
scsi_lock(us_to_host(us));
|
||||
|
||||
/* Is this command still active? */
|
||||
if (us->srb != srb) {
|
||||
scsi_unlock(us_to_host(us));
|
||||
US_DEBUGP ("-- nothing to abort\n");
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
/* Set the TIMED_OUT bit. Also set the ABORTING bit, but only if
|
||||
* a device reset isn't already in progress (to avoid interfering
|
||||
* with the reset). Note that we must retain the host lock while
|
||||
* calling usb_stor_stop_transport(); otherwise it might interfere
|
||||
* with an auto-reset that begins as soon as we release the lock. */
|
||||
set_bit(US_FLIDX_TIMED_OUT, &us->flags);
|
||||
if (!test_bit(US_FLIDX_RESETTING, &us->flags)) {
|
||||
set_bit(US_FLIDX_ABORTING, &us->flags);
|
||||
usb_stor_stop_transport(us);
|
||||
}
|
||||
scsi_unlock(us_to_host(us));
|
||||
|
||||
/* Wait for the aborted command to finish */
|
||||
wait_for_completion(&us->notify);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
/* This invokes the transport reset mechanism to reset the state of the
|
||||
* device */
|
||||
static int device_reset(struct scsi_cmnd *srb)
|
||||
{
|
||||
struct us_data *us = host_to_us(srb->device->host);
|
||||
int result;
|
||||
|
||||
US_DEBUGP("%s called\n", __FUNCTION__);
|
||||
|
||||
/* lock the device pointers and do the reset */
|
||||
mutex_lock(&(us->dev_mutex));
|
||||
result = us->transport_reset(us);
|
||||
mutex_unlock(&us->dev_mutex);
|
||||
|
||||
return result < 0 ? FAILED : SUCCESS;
|
||||
}
|
||||
|
||||
/* Simulate a SCSI bus reset by resetting the device's USB port. */
|
||||
static int bus_reset(struct scsi_cmnd *srb)
|
||||
{
|
||||
struct us_data *us = host_to_us(srb->device->host);
|
||||
int result;
|
||||
|
||||
US_DEBUGP("%s called\n", __FUNCTION__);
|
||||
result = usb_stor_port_reset(us);
|
||||
return result < 0 ? FAILED : SUCCESS;
|
||||
}
|
||||
|
||||
/* Report a driver-initiated device reset to the SCSI layer.
|
||||
* Calling this for a SCSI-initiated reset is unnecessary but harmless.
|
||||
* The caller must own the SCSI host lock. */
|
||||
void usb_stor_report_device_reset(struct us_data *us)
|
||||
{
|
||||
int i;
|
||||
struct Scsi_Host *host = us_to_host(us);
|
||||
|
||||
scsi_report_device_reset(host, 0, 0);
|
||||
if (us->flags & US_FL_SCM_MULT_TARG) {
|
||||
for (i = 1; i < host->max_id; ++i)
|
||||
scsi_report_device_reset(host, 0, i);
|
||||
}
|
||||
}
|
||||
|
||||
/* Report a driver-initiated bus reset to the SCSI layer.
|
||||
* Calling this for a SCSI-initiated reset is unnecessary but harmless.
|
||||
* The caller must own the SCSI host lock. */
|
||||
void usb_stor_report_bus_reset(struct us_data *us)
|
||||
{
|
||||
scsi_report_bus_reset(us_to_host(us), 0);
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* /proc/scsi/ functions
|
||||
***********************************************************************/
|
||||
|
||||
/* we use this macro to help us write into the buffer */
|
||||
#undef SPRINTF
|
||||
#define SPRINTF(args...) \
|
||||
do { if (pos < buffer+length) pos += sprintf(pos, ## args); } while (0)
|
||||
|
||||
static int proc_info (struct Scsi_Host *host, char *buffer,
|
||||
char **start, off_t offset, int length, int inout)
|
||||
{
|
||||
struct us_data *us = host_to_us(host);
|
||||
char *pos = buffer;
|
||||
const char *string;
|
||||
|
||||
/* if someone is sending us data, just throw it away */
|
||||
if (inout)
|
||||
return length;
|
||||
|
||||
/* print the controller name */
|
||||
SPRINTF(" Host scsi%d: usb-storage\n", host->host_no);
|
||||
|
||||
/* print product, vendor, and serial number strings */
|
||||
if (us->pusb_dev->manufacturer)
|
||||
string = us->pusb_dev->manufacturer;
|
||||
else if (us->unusual_dev->vendorName)
|
||||
string = us->unusual_dev->vendorName;
|
||||
else
|
||||
string = "Unknown";
|
||||
SPRINTF(" Vendor: %s\n", string);
|
||||
if (us->pusb_dev->product)
|
||||
string = us->pusb_dev->product;
|
||||
else if (us->unusual_dev->productName)
|
||||
string = us->unusual_dev->productName;
|
||||
else
|
||||
string = "Unknown";
|
||||
SPRINTF(" Product: %s\n", string);
|
||||
if (us->pusb_dev->serial)
|
||||
string = us->pusb_dev->serial;
|
||||
else
|
||||
string = "None";
|
||||
SPRINTF("Serial Number: %s\n", string);
|
||||
|
||||
/* show the protocol and transport */
|
||||
SPRINTF(" Protocol: %s\n", us->protocol_name);
|
||||
SPRINTF(" Transport: %s\n", us->transport_name);
|
||||
|
||||
/* show the device flags */
|
||||
if (pos < buffer + length) {
|
||||
pos += sprintf(pos, " Quirks:");
|
||||
|
||||
#define US_FLAG(name, value) \
|
||||
if (us->flags & value) pos += sprintf(pos, " " #name);
|
||||
US_DO_ALL_FLAGS
|
||||
#undef US_FLAG
|
||||
|
||||
*(pos++) = '\n';
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate start of next buffer, and return value.
|
||||
*/
|
||||
*start = buffer + offset;
|
||||
|
||||
if ((pos - buffer) < offset)
|
||||
return (0);
|
||||
else if ((pos - buffer - offset) < length)
|
||||
return (pos - buffer - offset);
|
||||
else
|
||||
return (length);
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* Sysfs interface
|
||||
***********************************************************************/
|
||||
|
||||
/* Output routine for the sysfs max_sectors file */
|
||||
static ssize_t show_max_sectors(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct scsi_device *sdev = to_scsi_device(dev);
|
||||
|
||||
return sprintf(buf, "%u\n", sdev->request_queue->max_sectors);
|
||||
}
|
||||
|
||||
/* Input routine for the sysfs max_sectors file */
|
||||
static ssize_t store_max_sectors(struct device *dev, struct device_attribute *attr, const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
struct scsi_device *sdev = to_scsi_device(dev);
|
||||
unsigned short ms;
|
||||
|
||||
if (sscanf(buf, "%hu", &ms) > 0 && ms <= SCSI_DEFAULT_MAX_SECTORS) {
|
||||
blk_queue_max_sectors(sdev->request_queue, ms);
|
||||
return strlen(buf);
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(max_sectors, S_IRUGO | S_IWUSR, show_max_sectors,
|
||||
store_max_sectors);
|
||||
|
||||
static struct device_attribute *sysfs_device_attr_list[] = {
|
||||
&dev_attr_max_sectors,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/*
|
||||
* this defines our host template, with which we'll allocate hosts
|
||||
*/
|
||||
|
||||
struct scsi_host_template usb_stor_host_template = {
|
||||
/* basic userland interface stuff */
|
||||
.name = "usb-storage",
|
||||
.proc_name = "usb-storage",
|
||||
.proc_info = proc_info,
|
||||
.info = host_info,
|
||||
|
||||
/* command interface -- queued only */
|
||||
.queuecommand = queuecommand,
|
||||
|
||||
/* error and abort handlers */
|
||||
.eh_abort_handler = command_abort,
|
||||
.eh_device_reset_handler = device_reset,
|
||||
.eh_bus_reset_handler = bus_reset,
|
||||
|
||||
/* queue commands only, only one command per LUN */
|
||||
.can_queue = 1,
|
||||
.cmd_per_lun = 1,
|
||||
|
||||
/* unknown initiator id */
|
||||
.this_id = -1,
|
||||
|
||||
.slave_alloc = slave_alloc,
|
||||
.slave_configure = slave_configure,
|
||||
|
||||
/* lots of sg segments can be handled */
|
||||
.sg_tablesize = SG_ALL,
|
||||
|
||||
/* limit the total size of a transfer to 120 KB */
|
||||
.max_sectors = 240,
|
||||
|
||||
/* merge commands... this seems to help performance, but
|
||||
* periodically someone should test to see which setting is more
|
||||
* optimal.
|
||||
*/
|
||||
.use_clustering = 1,
|
||||
|
||||
/* emulated HBA */
|
||||
.emulated = 1,
|
||||
|
||||
/* we do our own delay after a device or bus reset */
|
||||
.skip_settle_delay = 1,
|
||||
|
||||
/* sysfs device attributes */
|
||||
.sdev_attrs = sysfs_device_attr_list,
|
||||
|
||||
/* module management */
|
||||
.module = THIS_MODULE
|
||||
};
|
||||
|
||||
/* To Report "Illegal Request: Invalid Field in CDB */
|
||||
unsigned char usb_stor_sense_invalidCDB[18] = {
|
||||
[0] = 0x70, /* current error */
|
||||
[2] = ILLEGAL_REQUEST, /* Illegal Request = 0x05 */
|
||||
[7] = 0x0a, /* additional length */
|
||||
[12] = 0x24 /* Invalid Field in CDB */
|
||||
};
|
||||
|
||||
50
drivers/usb/storage/scsiglue.h
Normal file
50
drivers/usb/storage/scsiglue.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/* Driver for USB Mass Storage compliant devices
|
||||
* SCSI Connecting Glue Header File
|
||||
*
|
||||
* $Id: scsiglue.h,v 1.1.1.1 2007/06/12 07:27:09 eyryu Exp $
|
||||
*
|
||||
* Current development and maintenance by:
|
||||
* (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
|
||||
*
|
||||
* This driver is based on the 'USB Mass Storage Class' document. This
|
||||
* describes in detail the protocol used to communicate with such
|
||||
* devices. Clearly, the designers had SCSI and ATAPI commands in
|
||||
* mind when they created this document. The commands are all very
|
||||
* similar to commands in the SCSI-II and ATAPI specifications.
|
||||
*
|
||||
* It is important to note that in a number of cases this class
|
||||
* exhibits class-specific exemptions from the USB specification.
|
||||
* Notably the usage of NAK, STALL and ACK differs from the norm, in
|
||||
* that they are used to communicate wait, failed and OK on commands.
|
||||
*
|
||||
* Also, for certain devices, the interrupt endpoint is used to convey
|
||||
* status of a command.
|
||||
*
|
||||
* Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more
|
||||
* information about this driver.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef _SCSIGLUE_H_
|
||||
#define _SCSIGLUE_H_
|
||||
|
||||
extern void usb_stor_report_device_reset(struct us_data *us);
|
||||
extern void usb_stor_report_bus_reset(struct us_data *us);
|
||||
|
||||
extern unsigned char usb_stor_sense_invalidCDB[18];
|
||||
extern struct scsi_host_template usb_stor_host_template;
|
||||
|
||||
#endif
|
||||
1653
drivers/usb/storage/sddr09.c
Normal file
1653
drivers/usb/storage/sddr09.c
Normal file
File diff suppressed because it is too large
Load Diff
37
drivers/usb/storage/sddr09.h
Normal file
37
drivers/usb/storage/sddr09.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/* Driver for SanDisk SDDR-09 SmartMedia reader
|
||||
* Header File
|
||||
*
|
||||
* $Id: sddr09.h,v 1.1.1.1 2007/06/12 07:27:09 eyryu Exp $
|
||||
*
|
||||
* Current development and maintenance by:
|
||||
* (c) 2000 Robert Baruch (autophile@dol.net)
|
||||
* (c) 2002 Andries Brouwer (aeb@cwi.nl)
|
||||
*
|
||||
* See sddr09.c for more explanation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef _USB_SHUTTLE_EUSB_SDDR09_H
|
||||
#define _USB_SHUTTLE_EUSB_SDDR09_H
|
||||
|
||||
/* Sandisk SDDR-09 stuff */
|
||||
|
||||
extern int sddr09_transport(struct scsi_cmnd *srb, struct us_data *us);
|
||||
|
||||
extern int usb_stor_sddr09_dpcm_init(struct us_data *us);
|
||||
extern int usb_stor_sddr09_init(struct us_data *us);
|
||||
|
||||
#endif
|
||||
929
drivers/usb/storage/sddr55.c
Normal file
929
drivers/usb/storage/sddr55.c
Normal file
@@ -0,0 +1,929 @@
|
||||
/* Driver for SanDisk SDDR-55 SmartMedia reader
|
||||
*
|
||||
* $Id: sddr55.c,v 1.1.1.1 2007/06/12 07:27:09 eyryu Exp $
|
||||
*
|
||||
* SDDR55 driver v0.1:
|
||||
*
|
||||
* First release
|
||||
*
|
||||
* Current development and maintenance by:
|
||||
* (c) 2002 Simon Munton
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <scsi/scsi.h>
|
||||
#include <scsi/scsi_cmnd.h>
|
||||
|
||||
#include "usb.h"
|
||||
#include "transport.h"
|
||||
#include "protocol.h"
|
||||
#include "debug.h"
|
||||
#include "sddr55.h"
|
||||
|
||||
|
||||
#define short_pack(lsb,msb) ( ((u16)(lsb)) | ( ((u16)(msb))<<8 ) )
|
||||
#define LSB_of(s) ((s)&0xFF)
|
||||
#define MSB_of(s) ((s)>>8)
|
||||
#define PAGESIZE 512
|
||||
|
||||
#define set_sense_info(sk, asc, ascq) \
|
||||
do { \
|
||||
info->sense_data[2] = sk; \
|
||||
info->sense_data[12] = asc; \
|
||||
info->sense_data[13] = ascq; \
|
||||
} while (0)
|
||||
|
||||
|
||||
struct sddr55_card_info {
|
||||
unsigned long capacity; /* Size of card in bytes */
|
||||
int max_log_blks; /* maximum number of logical blocks */
|
||||
int pageshift; /* log2 of pagesize */
|
||||
int smallpageshift; /* 1 if pagesize == 256 */
|
||||
int blocksize; /* Size of block in pages */
|
||||
int blockshift; /* log2 of blocksize */
|
||||
int blockmask; /* 2^blockshift - 1 */
|
||||
int read_only; /* non zero if card is write protected */
|
||||
int force_read_only; /* non zero if we find a map error*/
|
||||
int *lba_to_pba; /* logical to physical map */
|
||||
int *pba_to_lba; /* physical to logical map */
|
||||
int fatal_error; /* set if we detect something nasty */
|
||||
unsigned long last_access; /* number of jiffies since we last talked to device */
|
||||
unsigned char sense_data[18];
|
||||
};
|
||||
|
||||
|
||||
#define NOT_ALLOCATED 0xffffffff
|
||||
#define BAD_BLOCK 0xffff
|
||||
#define CIS_BLOCK 0x400
|
||||
#define UNUSED_BLOCK 0x3ff
|
||||
|
||||
static int
|
||||
sddr55_bulk_transport(struct us_data *us, int direction,
|
||||
unsigned char *data, unsigned int len) {
|
||||
struct sddr55_card_info *info = (struct sddr55_card_info *)us->extra;
|
||||
unsigned int pipe = (direction == DMA_FROM_DEVICE) ?
|
||||
us->recv_bulk_pipe : us->send_bulk_pipe;
|
||||
|
||||
if (!len)
|
||||
return USB_STOR_XFER_GOOD;
|
||||
info->last_access = jiffies;
|
||||
return usb_stor_bulk_transfer_buf(us, pipe, data, len, NULL);
|
||||
}
|
||||
|
||||
/* check if card inserted, if there is, update read_only status
|
||||
* return non zero if no card
|
||||
*/
|
||||
|
||||
static int sddr55_status(struct us_data *us)
|
||||
{
|
||||
int result;
|
||||
unsigned char *command = us->iobuf;
|
||||
unsigned char *status = us->iobuf;
|
||||
struct sddr55_card_info *info = (struct sddr55_card_info *)us->extra;
|
||||
|
||||
/* send command */
|
||||
memset(command, 0, 8);
|
||||
command[5] = 0xB0;
|
||||
command[7] = 0x80;
|
||||
result = sddr55_bulk_transport(us,
|
||||
DMA_TO_DEVICE, command, 8);
|
||||
|
||||
US_DEBUGP("Result for send_command in status %d\n",
|
||||
result);
|
||||
|
||||
if (result != USB_STOR_XFER_GOOD) {
|
||||
set_sense_info (4, 0, 0); /* hardware error */
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
}
|
||||
|
||||
result = sddr55_bulk_transport(us,
|
||||
DMA_FROM_DEVICE, status, 4);
|
||||
|
||||
/* expect to get short transfer if no card fitted */
|
||||
if (result == USB_STOR_XFER_SHORT || result == USB_STOR_XFER_STALLED) {
|
||||
/* had a short transfer, no card inserted, free map memory */
|
||||
kfree(info->lba_to_pba);
|
||||
kfree(info->pba_to_lba);
|
||||
info->lba_to_pba = NULL;
|
||||
info->pba_to_lba = NULL;
|
||||
|
||||
info->fatal_error = 0;
|
||||
info->force_read_only = 0;
|
||||
|
||||
set_sense_info (2, 0x3a, 0); /* not ready, medium not present */
|
||||
return USB_STOR_TRANSPORT_FAILED;
|
||||
}
|
||||
|
||||
if (result != USB_STOR_XFER_GOOD) {
|
||||
set_sense_info (4, 0, 0); /* hardware error */
|
||||
return USB_STOR_TRANSPORT_FAILED;
|
||||
}
|
||||
|
||||
/* check write protect status */
|
||||
info->read_only = (status[0] & 0x20);
|
||||
|
||||
/* now read status */
|
||||
result = sddr55_bulk_transport(us,
|
||||
DMA_FROM_DEVICE, status, 2);
|
||||
|
||||
if (result != USB_STOR_XFER_GOOD) {
|
||||
set_sense_info (4, 0, 0); /* hardware error */
|
||||
}
|
||||
|
||||
return (result == USB_STOR_XFER_GOOD ?
|
||||
USB_STOR_TRANSPORT_GOOD : USB_STOR_TRANSPORT_FAILED);
|
||||
}
|
||||
|
||||
|
||||
static int sddr55_read_data(struct us_data *us,
|
||||
unsigned int lba,
|
||||
unsigned int page,
|
||||
unsigned short sectors) {
|
||||
|
||||
int result = USB_STOR_TRANSPORT_GOOD;
|
||||
unsigned char *command = us->iobuf;
|
||||
unsigned char *status = us->iobuf;
|
||||
struct sddr55_card_info *info = (struct sddr55_card_info *)us->extra;
|
||||
unsigned char *buffer;
|
||||
|
||||
unsigned int pba;
|
||||
unsigned long address;
|
||||
|
||||
unsigned short pages;
|
||||
unsigned int len, index, offset;
|
||||
|
||||
// Since we only read in one block at a time, we have to create
|
||||
// a bounce buffer and move the data a piece at a time between the
|
||||
// bounce buffer and the actual transfer buffer.
|
||||
|
||||
len = min((unsigned int) sectors, (unsigned int) info->blocksize >>
|
||||
info->smallpageshift) * PAGESIZE;
|
||||
buffer = kmalloc(len, GFP_NOIO);
|
||||
if (buffer == NULL)
|
||||
return USB_STOR_TRANSPORT_ERROR; /* out of memory */
|
||||
index = offset = 0;
|
||||
|
||||
while (sectors>0) {
|
||||
|
||||
/* have we got to end? */
|
||||
if (lba >= info->max_log_blks)
|
||||
break;
|
||||
|
||||
pba = info->lba_to_pba[lba];
|
||||
|
||||
// Read as many sectors as possible in this block
|
||||
|
||||
pages = min((unsigned int) sectors << info->smallpageshift,
|
||||
info->blocksize - page);
|
||||
len = pages << info->pageshift;
|
||||
|
||||
US_DEBUGP("Read %02X pages, from PBA %04X"
|
||||
" (LBA %04X) page %02X\n",
|
||||
pages, pba, lba, page);
|
||||
|
||||
if (pba == NOT_ALLOCATED) {
|
||||
/* no pba for this lba, fill with zeroes */
|
||||
memset (buffer, 0, len);
|
||||
} else {
|
||||
|
||||
address = (pba << info->blockshift) + page;
|
||||
|
||||
command[0] = 0;
|
||||
command[1] = LSB_of(address>>16);
|
||||
command[2] = LSB_of(address>>8);
|
||||
command[3] = LSB_of(address);
|
||||
|
||||
command[4] = 0;
|
||||
command[5] = 0xB0;
|
||||
command[6] = LSB_of(pages << (1 - info->smallpageshift));
|
||||
command[7] = 0x85;
|
||||
|
||||
/* send command */
|
||||
result = sddr55_bulk_transport(us,
|
||||
DMA_TO_DEVICE, command, 8);
|
||||
|
||||
US_DEBUGP("Result for send_command in read_data %d\n",
|
||||
result);
|
||||
|
||||
if (result != USB_STOR_XFER_GOOD) {
|
||||
result = USB_STOR_TRANSPORT_ERROR;
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* read data */
|
||||
result = sddr55_bulk_transport(us,
|
||||
DMA_FROM_DEVICE, buffer, len);
|
||||
|
||||
if (result != USB_STOR_XFER_GOOD) {
|
||||
result = USB_STOR_TRANSPORT_ERROR;
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* now read status */
|
||||
result = sddr55_bulk_transport(us,
|
||||
DMA_FROM_DEVICE, status, 2);
|
||||
|
||||
if (result != USB_STOR_XFER_GOOD) {
|
||||
result = USB_STOR_TRANSPORT_ERROR;
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* check status for error */
|
||||
if (status[0] == 0xff && status[1] == 0x4) {
|
||||
set_sense_info (3, 0x11, 0);
|
||||
result = USB_STOR_TRANSPORT_FAILED;
|
||||
goto leave;
|
||||
}
|
||||
}
|
||||
|
||||
// Store the data in the transfer buffer
|
||||
usb_stor_access_xfer_buf(buffer, len, us->srb,
|
||||
&index, &offset, TO_XFER_BUF);
|
||||
|
||||
page = 0;
|
||||
lba++;
|
||||
sectors -= pages >> info->smallpageshift;
|
||||
}
|
||||
|
||||
result = USB_STOR_TRANSPORT_GOOD;
|
||||
|
||||
leave:
|
||||
kfree(buffer);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int sddr55_write_data(struct us_data *us,
|
||||
unsigned int lba,
|
||||
unsigned int page,
|
||||
unsigned short sectors) {
|
||||
|
||||
int result = USB_STOR_TRANSPORT_GOOD;
|
||||
unsigned char *command = us->iobuf;
|
||||
unsigned char *status = us->iobuf;
|
||||
struct sddr55_card_info *info = (struct sddr55_card_info *)us->extra;
|
||||
unsigned char *buffer;
|
||||
|
||||
unsigned int pba;
|
||||
unsigned int new_pba;
|
||||
unsigned long address;
|
||||
|
||||
unsigned short pages;
|
||||
int i;
|
||||
unsigned int len, index, offset;
|
||||
|
||||
/* check if we are allowed to write */
|
||||
if (info->read_only || info->force_read_only) {
|
||||
set_sense_info (7, 0x27, 0); /* read only */
|
||||
return USB_STOR_TRANSPORT_FAILED;
|
||||
}
|
||||
|
||||
// Since we only write one block at a time, we have to create
|
||||
// a bounce buffer and move the data a piece at a time between the
|
||||
// bounce buffer and the actual transfer buffer.
|
||||
|
||||
len = min((unsigned int) sectors, (unsigned int) info->blocksize >>
|
||||
info->smallpageshift) * PAGESIZE;
|
||||
buffer = kmalloc(len, GFP_NOIO);
|
||||
if (buffer == NULL)
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
index = offset = 0;
|
||||
|
||||
while (sectors > 0) {
|
||||
|
||||
/* have we got to end? */
|
||||
if (lba >= info->max_log_blks)
|
||||
break;
|
||||
|
||||
pba = info->lba_to_pba[lba];
|
||||
|
||||
// Write as many sectors as possible in this block
|
||||
|
||||
pages = min((unsigned int) sectors << info->smallpageshift,
|
||||
info->blocksize - page);
|
||||
len = pages << info->pageshift;
|
||||
|
||||
// Get the data from the transfer buffer
|
||||
usb_stor_access_xfer_buf(buffer, len, us->srb,
|
||||
&index, &offset, FROM_XFER_BUF);
|
||||
|
||||
US_DEBUGP("Write %02X pages, to PBA %04X"
|
||||
" (LBA %04X) page %02X\n",
|
||||
pages, pba, lba, page);
|
||||
|
||||
command[4] = 0;
|
||||
|
||||
if (pba == NOT_ALLOCATED) {
|
||||
/* no pba allocated for this lba, find a free pba to use */
|
||||
|
||||
int max_pba = (info->max_log_blks / 250 ) * 256;
|
||||
int found_count = 0;
|
||||
int found_pba = -1;
|
||||
|
||||
/* set pba to first block in zone lba is in */
|
||||
pba = (lba / 1000) * 1024;
|
||||
|
||||
US_DEBUGP("No PBA for LBA %04X\n",lba);
|
||||
|
||||
if (max_pba > 1024)
|
||||
max_pba = 1024;
|
||||
|
||||
/*
|
||||
* Scan through the map looking for an unused block
|
||||
* leave 16 unused blocks at start (or as many as
|
||||
* possible) since the sddr55 seems to reuse a used
|
||||
* block when it shouldn't if we don't leave space.
|
||||
*/
|
||||
for (i = 0; i < max_pba; i++, pba++) {
|
||||
if (info->pba_to_lba[pba] == UNUSED_BLOCK) {
|
||||
found_pba = pba;
|
||||
if (found_count++ > 16)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pba = found_pba;
|
||||
|
||||
if (pba == -1) {
|
||||
/* oh dear */
|
||||
US_DEBUGP("Couldn't find unallocated block\n");
|
||||
|
||||
set_sense_info (3, 0x31, 0); /* medium error */
|
||||
result = USB_STOR_TRANSPORT_FAILED;
|
||||
goto leave;
|
||||
}
|
||||
|
||||
US_DEBUGP("Allocating PBA %04X for LBA %04X\n", pba, lba);
|
||||
|
||||
/* set writing to unallocated block flag */
|
||||
command[4] = 0x40;
|
||||
}
|
||||
|
||||
address = (pba << info->blockshift) + page;
|
||||
|
||||
command[1] = LSB_of(address>>16);
|
||||
command[2] = LSB_of(address>>8);
|
||||
command[3] = LSB_of(address);
|
||||
|
||||
/* set the lba into the command, modulo 1000 */
|
||||
command[0] = LSB_of(lba % 1000);
|
||||
command[6] = MSB_of(lba % 1000);
|
||||
|
||||
command[4] |= LSB_of(pages >> info->smallpageshift);
|
||||
command[5] = 0xB0;
|
||||
command[7] = 0x86;
|
||||
|
||||
/* send command */
|
||||
result = sddr55_bulk_transport(us,
|
||||
DMA_TO_DEVICE, command, 8);
|
||||
|
||||
if (result != USB_STOR_XFER_GOOD) {
|
||||
US_DEBUGP("Result for send_command in write_data %d\n",
|
||||
result);
|
||||
|
||||
/* set_sense_info is superfluous here? */
|
||||
set_sense_info (3, 0x3, 0);/* peripheral write error */
|
||||
result = USB_STOR_TRANSPORT_FAILED;
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* send the data */
|
||||
result = sddr55_bulk_transport(us,
|
||||
DMA_TO_DEVICE, buffer, len);
|
||||
|
||||
if (result != USB_STOR_XFER_GOOD) {
|
||||
US_DEBUGP("Result for send_data in write_data %d\n",
|
||||
result);
|
||||
|
||||
/* set_sense_info is superfluous here? */
|
||||
set_sense_info (3, 0x3, 0);/* peripheral write error */
|
||||
result = USB_STOR_TRANSPORT_FAILED;
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* now read status */
|
||||
result = sddr55_bulk_transport(us, DMA_FROM_DEVICE, status, 6);
|
||||
|
||||
if (result != USB_STOR_XFER_GOOD) {
|
||||
US_DEBUGP("Result for get_status in write_data %d\n",
|
||||
result);
|
||||
|
||||
/* set_sense_info is superfluous here? */
|
||||
set_sense_info (3, 0x3, 0);/* peripheral write error */
|
||||
result = USB_STOR_TRANSPORT_FAILED;
|
||||
goto leave;
|
||||
}
|
||||
|
||||
new_pba = (status[3] + (status[4] << 8) + (status[5] << 16))
|
||||
>> info->blockshift;
|
||||
|
||||
/* check status for error */
|
||||
if (status[0] == 0xff && status[1] == 0x4) {
|
||||
info->pba_to_lba[new_pba] = BAD_BLOCK;
|
||||
|
||||
set_sense_info (3, 0x0c, 0);
|
||||
result = USB_STOR_TRANSPORT_FAILED;
|
||||
goto leave;
|
||||
}
|
||||
|
||||
US_DEBUGP("Updating maps for LBA %04X: old PBA %04X, new PBA %04X\n",
|
||||
lba, pba, new_pba);
|
||||
|
||||
/* update the lba<->pba maps, note new_pba might be the same as pba */
|
||||
info->lba_to_pba[lba] = new_pba;
|
||||
info->pba_to_lba[pba] = UNUSED_BLOCK;
|
||||
|
||||
/* check that new_pba wasn't already being used */
|
||||
if (info->pba_to_lba[new_pba] != UNUSED_BLOCK) {
|
||||
printk(KERN_ERR "sddr55 error: new PBA %04X already in use for LBA %04X\n",
|
||||
new_pba, info->pba_to_lba[new_pba]);
|
||||
info->fatal_error = 1;
|
||||
set_sense_info (3, 0x31, 0);
|
||||
result = USB_STOR_TRANSPORT_FAILED;
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* update the pba<->lba maps for new_pba */
|
||||
info->pba_to_lba[new_pba] = lba % 1000;
|
||||
|
||||
page = 0;
|
||||
lba++;
|
||||
sectors -= pages >> info->smallpageshift;
|
||||
}
|
||||
result = USB_STOR_TRANSPORT_GOOD;
|
||||
|
||||
leave:
|
||||
kfree(buffer);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int sddr55_read_deviceID(struct us_data *us,
|
||||
unsigned char *manufacturerID,
|
||||
unsigned char *deviceID) {
|
||||
|
||||
int result;
|
||||
unsigned char *command = us->iobuf;
|
||||
unsigned char *content = us->iobuf;
|
||||
|
||||
memset(command, 0, 8);
|
||||
command[5] = 0xB0;
|
||||
command[7] = 0x84;
|
||||
result = sddr55_bulk_transport(us, DMA_TO_DEVICE, command, 8);
|
||||
|
||||
US_DEBUGP("Result of send_control for device ID is %d\n",
|
||||
result);
|
||||
|
||||
if (result != USB_STOR_XFER_GOOD)
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
|
||||
result = sddr55_bulk_transport(us,
|
||||
DMA_FROM_DEVICE, content, 4);
|
||||
|
||||
if (result != USB_STOR_XFER_GOOD)
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
|
||||
*manufacturerID = content[0];
|
||||
*deviceID = content[1];
|
||||
|
||||
if (content[0] != 0xff) {
|
||||
result = sddr55_bulk_transport(us,
|
||||
DMA_FROM_DEVICE, content, 2);
|
||||
}
|
||||
|
||||
return USB_STOR_TRANSPORT_GOOD;
|
||||
}
|
||||
|
||||
|
||||
int sddr55_reset(struct us_data *us) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static unsigned long sddr55_get_capacity(struct us_data *us) {
|
||||
|
||||
unsigned char manufacturerID;
|
||||
unsigned char deviceID;
|
||||
int result;
|
||||
struct sddr55_card_info *info = (struct sddr55_card_info *)us->extra;
|
||||
|
||||
US_DEBUGP("Reading capacity...\n");
|
||||
|
||||
result = sddr55_read_deviceID(us,
|
||||
&manufacturerID,
|
||||
&deviceID);
|
||||
|
||||
US_DEBUGP("Result of read_deviceID is %d\n",
|
||||
result);
|
||||
|
||||
if (result != USB_STOR_XFER_GOOD)
|
||||
return 0;
|
||||
|
||||
US_DEBUGP("Device ID = %02X\n", deviceID);
|
||||
US_DEBUGP("Manuf ID = %02X\n", manufacturerID);
|
||||
|
||||
info->pageshift = 9;
|
||||
info->smallpageshift = 0;
|
||||
info->blocksize = 16;
|
||||
info->blockshift = 4;
|
||||
info->blockmask = 15;
|
||||
|
||||
switch (deviceID) {
|
||||
|
||||
case 0x6e: // 1MB
|
||||
case 0xe8:
|
||||
case 0xec:
|
||||
info->pageshift = 8;
|
||||
info->smallpageshift = 1;
|
||||
return 0x00100000;
|
||||
|
||||
case 0xea: // 2MB
|
||||
case 0x64:
|
||||
info->pageshift = 8;
|
||||
info->smallpageshift = 1;
|
||||
case 0x5d: // 5d is a ROM card with pagesize 512.
|
||||
return 0x00200000;
|
||||
|
||||
case 0xe3: // 4MB
|
||||
case 0xe5:
|
||||
case 0x6b:
|
||||
case 0xd5:
|
||||
return 0x00400000;
|
||||
|
||||
case 0xe6: // 8MB
|
||||
case 0xd6:
|
||||
return 0x00800000;
|
||||
|
||||
case 0x73: // 16MB
|
||||
info->blocksize = 32;
|
||||
info->blockshift = 5;
|
||||
info->blockmask = 31;
|
||||
return 0x01000000;
|
||||
|
||||
case 0x75: // 32MB
|
||||
info->blocksize = 32;
|
||||
info->blockshift = 5;
|
||||
info->blockmask = 31;
|
||||
return 0x02000000;
|
||||
|
||||
case 0x76: // 64MB
|
||||
info->blocksize = 32;
|
||||
info->blockshift = 5;
|
||||
info->blockmask = 31;
|
||||
return 0x04000000;
|
||||
|
||||
case 0x79: // 128MB
|
||||
info->blocksize = 32;
|
||||
info->blockshift = 5;
|
||||
info->blockmask = 31;
|
||||
return 0x08000000;
|
||||
|
||||
default: // unknown
|
||||
return 0;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static int sddr55_read_map(struct us_data *us) {
|
||||
|
||||
struct sddr55_card_info *info = (struct sddr55_card_info *)(us->extra);
|
||||
int numblocks;
|
||||
unsigned char *buffer;
|
||||
unsigned char *command = us->iobuf;
|
||||
int i;
|
||||
unsigned short lba;
|
||||
unsigned short max_lba;
|
||||
int result;
|
||||
|
||||
if (!info->capacity)
|
||||
return -1;
|
||||
|
||||
numblocks = info->capacity >> (info->blockshift + info->pageshift);
|
||||
|
||||
buffer = kmalloc( numblocks * 2, GFP_NOIO );
|
||||
|
||||
if (!buffer)
|
||||
return -1;
|
||||
|
||||
memset(command, 0, 8);
|
||||
command[5] = 0xB0;
|
||||
command[6] = numblocks * 2 / 256;
|
||||
command[7] = 0x8A;
|
||||
|
||||
result = sddr55_bulk_transport(us, DMA_TO_DEVICE, command, 8);
|
||||
|
||||
if ( result != USB_STOR_XFER_GOOD) {
|
||||
kfree (buffer);
|
||||
return -1;
|
||||
}
|
||||
|
||||
result = sddr55_bulk_transport(us, DMA_FROM_DEVICE, buffer, numblocks * 2);
|
||||
|
||||
if ( result != USB_STOR_XFER_GOOD) {
|
||||
kfree (buffer);
|
||||
return -1;
|
||||
}
|
||||
|
||||
result = sddr55_bulk_transport(us, DMA_FROM_DEVICE, command, 2);
|
||||
|
||||
if ( result != USB_STOR_XFER_GOOD) {
|
||||
kfree (buffer);
|
||||
return -1;
|
||||
}
|
||||
|
||||
kfree(info->lba_to_pba);
|
||||
kfree(info->pba_to_lba);
|
||||
info->lba_to_pba = kmalloc(numblocks*sizeof(int), GFP_NOIO);
|
||||
info->pba_to_lba = kmalloc(numblocks*sizeof(int), GFP_NOIO);
|
||||
|
||||
if (info->lba_to_pba == NULL || info->pba_to_lba == NULL) {
|
||||
kfree(info->lba_to_pba);
|
||||
kfree(info->pba_to_lba);
|
||||
info->lba_to_pba = NULL;
|
||||
info->pba_to_lba = NULL;
|
||||
kfree(buffer);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(info->lba_to_pba, 0xff, numblocks*sizeof(int));
|
||||
memset(info->pba_to_lba, 0xff, numblocks*sizeof(int));
|
||||
|
||||
/* set maximum lba */
|
||||
max_lba = info->max_log_blks;
|
||||
if (max_lba > 1000)
|
||||
max_lba = 1000;
|
||||
|
||||
// Each block is 64 bytes of control data, so block i is located in
|
||||
// scatterlist block i*64/128k = i*(2^6)*(2^-17) = i*(2^-11)
|
||||
|
||||
for (i=0; i<numblocks; i++) {
|
||||
int zone = i / 1024;
|
||||
|
||||
lba = short_pack(buffer[i * 2], buffer[i * 2 + 1]);
|
||||
|
||||
/* Every 1024 physical blocks ("zone"), the LBA numbers
|
||||
* go back to zero, but are within a higher
|
||||
* block of LBA's. Also, there is a maximum of
|
||||
* 1000 LBA's per zone. In other words, in PBA
|
||||
* 1024-2047 you will find LBA 0-999 which are
|
||||
* really LBA 1000-1999. Yes, this wastes 24
|
||||
* physical blocks per zone. Go figure.
|
||||
* These devices can have blocks go bad, so there
|
||||
* are 24 spare blocks to use when blocks do go bad.
|
||||
*/
|
||||
|
||||
/* SDDR55 returns 0xffff for a bad block, and 0x400 for the
|
||||
* CIS block. (Is this true for cards 8MB or less??)
|
||||
* Record these in the physical to logical map
|
||||
*/
|
||||
|
||||
info->pba_to_lba[i] = lba;
|
||||
|
||||
if (lba >= max_lba) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (info->lba_to_pba[lba + zone * 1000] != NOT_ALLOCATED &&
|
||||
!info->force_read_only) {
|
||||
printk("sddr55: map inconsistency at LBA %04X\n", lba + zone * 1000);
|
||||
info->force_read_only = 1;
|
||||
}
|
||||
|
||||
if (lba<0x10 || (lba>=0x3E0 && lba<0x3EF))
|
||||
US_DEBUGP("LBA %04X <-> PBA %04X\n", lba, i);
|
||||
|
||||
info->lba_to_pba[lba + zone * 1000] = i;
|
||||
}
|
||||
|
||||
kfree(buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void sddr55_card_info_destructor(void *extra) {
|
||||
struct sddr55_card_info *info = (struct sddr55_card_info *)extra;
|
||||
|
||||
if (!extra)
|
||||
return;
|
||||
|
||||
kfree(info->lba_to_pba);
|
||||
kfree(info->pba_to_lba);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Transport for the Sandisk SDDR-55
|
||||
*/
|
||||
int sddr55_transport(struct scsi_cmnd *srb, struct us_data *us)
|
||||
{
|
||||
int result;
|
||||
static unsigned char inquiry_response[8] = {
|
||||
0x00, 0x80, 0x00, 0x02, 0x1F, 0x00, 0x00, 0x00
|
||||
};
|
||||
// write-protected for now, no block descriptor support
|
||||
static unsigned char mode_page_01[20] = {
|
||||
0x0, 0x12, 0x00, 0x80, 0x0, 0x0, 0x0, 0x0,
|
||||
0x01, 0x0A,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
unsigned char *ptr = us->iobuf;
|
||||
unsigned long capacity;
|
||||
unsigned int lba;
|
||||
unsigned int pba;
|
||||
unsigned int page;
|
||||
unsigned short pages;
|
||||
struct sddr55_card_info *info;
|
||||
|
||||
if (!us->extra) {
|
||||
us->extra = kzalloc(
|
||||
sizeof(struct sddr55_card_info), GFP_NOIO);
|
||||
if (!us->extra)
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
us->extra_destructor = sddr55_card_info_destructor;
|
||||
}
|
||||
|
||||
info = (struct sddr55_card_info *)(us->extra);
|
||||
|
||||
if (srb->cmnd[0] == REQUEST_SENSE) {
|
||||
US_DEBUGP("SDDR55: request sense %02x/%02x/%02x\n", info->sense_data[2], info->sense_data[12], info->sense_data[13]);
|
||||
|
||||
memcpy (ptr, info->sense_data, sizeof info->sense_data);
|
||||
ptr[0] = 0x70;
|
||||
ptr[7] = 11;
|
||||
usb_stor_set_xfer_buf (ptr, sizeof info->sense_data, srb);
|
||||
memset (info->sense_data, 0, sizeof info->sense_data);
|
||||
|
||||
return USB_STOR_TRANSPORT_GOOD;
|
||||
}
|
||||
|
||||
memset (info->sense_data, 0, sizeof info->sense_data);
|
||||
|
||||
/* Dummy up a response for INQUIRY since SDDR55 doesn't
|
||||
respond to INQUIRY commands */
|
||||
|
||||
if (srb->cmnd[0] == INQUIRY) {
|
||||
memcpy(ptr, inquiry_response, 8);
|
||||
fill_inquiry_response(us, ptr, 36);
|
||||
return USB_STOR_TRANSPORT_GOOD;
|
||||
}
|
||||
|
||||
/* only check card status if the map isn't allocated, ie no card seen yet
|
||||
* or if it's been over half a second since we last accessed it
|
||||
*/
|
||||
if (info->lba_to_pba == NULL || time_after(jiffies, info->last_access + HZ/2)) {
|
||||
|
||||
/* check to see if a card is fitted */
|
||||
result = sddr55_status (us);
|
||||
if (result) {
|
||||
result = sddr55_status (us);
|
||||
if (!result) {
|
||||
set_sense_info (6, 0x28, 0); /* new media, set unit attention, not ready to ready */
|
||||
}
|
||||
return USB_STOR_TRANSPORT_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
/* if we detected a problem with the map when writing,
|
||||
don't allow any more access */
|
||||
if (info->fatal_error) {
|
||||
|
||||
set_sense_info (3, 0x31, 0);
|
||||
return USB_STOR_TRANSPORT_FAILED;
|
||||
}
|
||||
|
||||
if (srb->cmnd[0] == READ_CAPACITY) {
|
||||
|
||||
capacity = sddr55_get_capacity(us);
|
||||
|
||||
if (!capacity) {
|
||||
set_sense_info (3, 0x30, 0); /* incompatible medium */
|
||||
return USB_STOR_TRANSPORT_FAILED;
|
||||
}
|
||||
|
||||
info->capacity = capacity;
|
||||
|
||||
/* figure out the maximum logical block number, allowing for
|
||||
* the fact that only 250 out of every 256 are used */
|
||||
info->max_log_blks = ((info->capacity >> (info->pageshift + info->blockshift)) / 256) * 250;
|
||||
|
||||
/* Last page in the card, adjust as we only use 250 out of
|
||||
* every 256 pages */
|
||||
capacity = (capacity / 256) * 250;
|
||||
|
||||
capacity /= PAGESIZE;
|
||||
capacity--;
|
||||
|
||||
((__be32 *) ptr)[0] = cpu_to_be32(capacity);
|
||||
((__be32 *) ptr)[1] = cpu_to_be32(PAGESIZE);
|
||||
usb_stor_set_xfer_buf(ptr, 8, srb);
|
||||
|
||||
sddr55_read_map(us);
|
||||
|
||||
return USB_STOR_TRANSPORT_GOOD;
|
||||
}
|
||||
|
||||
if (srb->cmnd[0] == MODE_SENSE_10) {
|
||||
|
||||
memcpy(ptr, mode_page_01, sizeof mode_page_01);
|
||||
ptr[3] = (info->read_only || info->force_read_only) ? 0x80 : 0;
|
||||
usb_stor_set_xfer_buf(ptr, sizeof(mode_page_01), srb);
|
||||
|
||||
if ( (srb->cmnd[2] & 0x3F) == 0x01 ) {
|
||||
US_DEBUGP(
|
||||
"SDDR55: Dummy up request for mode page 1\n");
|
||||
return USB_STOR_TRANSPORT_GOOD;
|
||||
|
||||
} else if ( (srb->cmnd[2] & 0x3F) == 0x3F ) {
|
||||
US_DEBUGP(
|
||||
"SDDR55: Dummy up request for all mode pages\n");
|
||||
return USB_STOR_TRANSPORT_GOOD;
|
||||
}
|
||||
|
||||
set_sense_info (5, 0x24, 0); /* invalid field in command */
|
||||
return USB_STOR_TRANSPORT_FAILED;
|
||||
}
|
||||
|
||||
if (srb->cmnd[0] == ALLOW_MEDIUM_REMOVAL) {
|
||||
|
||||
US_DEBUGP(
|
||||
"SDDR55: %s medium removal. Not that I can do"
|
||||
" anything about it...\n",
|
||||
(srb->cmnd[4]&0x03) ? "Prevent" : "Allow");
|
||||
|
||||
return USB_STOR_TRANSPORT_GOOD;
|
||||
|
||||
}
|
||||
|
||||
if (srb->cmnd[0] == READ_10 || srb->cmnd[0] == WRITE_10) {
|
||||
|
||||
page = short_pack(srb->cmnd[3], srb->cmnd[2]);
|
||||
page <<= 16;
|
||||
page |= short_pack(srb->cmnd[5], srb->cmnd[4]);
|
||||
pages = short_pack(srb->cmnd[8], srb->cmnd[7]);
|
||||
|
||||
page <<= info->smallpageshift;
|
||||
|
||||
// convert page to block and page-within-block
|
||||
|
||||
lba = page >> info->blockshift;
|
||||
page = page & info->blockmask;
|
||||
|
||||
// locate physical block corresponding to logical block
|
||||
|
||||
if (lba >= info->max_log_blks) {
|
||||
|
||||
US_DEBUGP("Error: Requested LBA %04X exceeds maximum "
|
||||
"block %04X\n", lba, info->max_log_blks-1);
|
||||
|
||||
set_sense_info (5, 0x24, 0); /* invalid field in command */
|
||||
|
||||
return USB_STOR_TRANSPORT_FAILED;
|
||||
}
|
||||
|
||||
pba = info->lba_to_pba[lba];
|
||||
|
||||
if (srb->cmnd[0] == WRITE_10) {
|
||||
US_DEBUGP("WRITE_10: write block %04X (LBA %04X) page %01X"
|
||||
" pages %d\n",
|
||||
pba, lba, page, pages);
|
||||
|
||||
return sddr55_write_data(us, lba, page, pages);
|
||||
} else {
|
||||
US_DEBUGP("READ_10: read block %04X (LBA %04X) page %01X"
|
||||
" pages %d\n",
|
||||
pba, lba, page, pages);
|
||||
|
||||
return sddr55_read_data(us, lba, page, pages);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (srb->cmnd[0] == TEST_UNIT_READY) {
|
||||
return USB_STOR_TRANSPORT_GOOD;
|
||||
}
|
||||
|
||||
if (srb->cmnd[0] == START_STOP) {
|
||||
return USB_STOR_TRANSPORT_GOOD;
|
||||
}
|
||||
|
||||
set_sense_info (5, 0x20, 0); /* illegal command */
|
||||
|
||||
return USB_STOR_TRANSPORT_FAILED; // FIXME: sense buffer?
|
||||
}
|
||||
|
||||
34
drivers/usb/storage/sddr55.h
Normal file
34
drivers/usb/storage/sddr55.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/* Driver for SanDisk SDDR-55 SmartMedia reader
|
||||
* Header File
|
||||
*
|
||||
* $Id: sddr55.h,v 1.1.1.1 2007/06/12 07:27:09 eyryu Exp $
|
||||
*
|
||||
* Current development and maintenance by:
|
||||
* (c) 2002 Simon Munton
|
||||
*
|
||||
* See sddr55.c for more explanation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef _USB_SHUTTLE_EUSB_SDDR55_H
|
||||
#define _USB_SHUTTLE_EUSB_SDDR55_H
|
||||
|
||||
/* Sandisk SDDR-55 stuff */
|
||||
|
||||
extern int sddr55_transport(struct scsi_cmnd *srb, struct us_data *us);
|
||||
extern int sddr55_reset(struct us_data *us);
|
||||
|
||||
#endif
|
||||
1742
drivers/usb/storage/shuttle_usbat.c
Normal file
1742
drivers/usb/storage/shuttle_usbat.c
Normal file
File diff suppressed because it is too large
Load Diff
125
drivers/usb/storage/shuttle_usbat.h
Normal file
125
drivers/usb/storage/shuttle_usbat.h
Normal file
@@ -0,0 +1,125 @@
|
||||
/* Driver for SCM Microsystems USB-ATAPI cable
|
||||
* Header File
|
||||
*
|
||||
* $Id: shuttle_usbat.h,v 1.1.1.1 2007/06/12 07:27:09 eyryu Exp $
|
||||
*
|
||||
* Current development and maintenance by:
|
||||
* (c) 2000 Robert Baruch (autophile@dol.net)
|
||||
* (c) 2004, 2005 Daniel Drake <dsd@gentoo.org>
|
||||
*
|
||||
* See shuttle_usbat.c for more explanation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef _USB_SHUTTLE_USBAT_H
|
||||
#define _USB_SHUTTLE_USBAT_H
|
||||
|
||||
/* Supported device types */
|
||||
#define USBAT_DEV_HP8200 0x01
|
||||
#define USBAT_DEV_FLASH 0x02
|
||||
|
||||
#define USBAT_EPP_PORT 0x10
|
||||
#define USBAT_EPP_REGISTER 0x30
|
||||
#define USBAT_ATA 0x40
|
||||
#define USBAT_ISA 0x50
|
||||
|
||||
/* Commands (need to be logically OR'd with an access type */
|
||||
#define USBAT_CMD_READ_REG 0x00
|
||||
#define USBAT_CMD_WRITE_REG 0x01
|
||||
#define USBAT_CMD_READ_BLOCK 0x02
|
||||
#define USBAT_CMD_WRITE_BLOCK 0x03
|
||||
#define USBAT_CMD_COND_READ_BLOCK 0x04
|
||||
#define USBAT_CMD_COND_WRITE_BLOCK 0x05
|
||||
#define USBAT_CMD_WRITE_REGS 0x07
|
||||
|
||||
/* Commands (these don't need an access type) */
|
||||
#define USBAT_CMD_EXEC_CMD 0x80
|
||||
#define USBAT_CMD_SET_FEAT 0x81
|
||||
#define USBAT_CMD_UIO 0x82
|
||||
|
||||
/* Methods of accessing UIO register */
|
||||
#define USBAT_UIO_READ 1
|
||||
#define USBAT_UIO_WRITE 0
|
||||
|
||||
/* Qualifier bits */
|
||||
#define USBAT_QUAL_FCQ 0x20 /* full compare */
|
||||
#define USBAT_QUAL_ALQ 0x10 /* auto load subcount */
|
||||
|
||||
/* USBAT Flash Media status types */
|
||||
#define USBAT_FLASH_MEDIA_NONE 0
|
||||
#define USBAT_FLASH_MEDIA_CF 1
|
||||
|
||||
/* USBAT Flash Media change types */
|
||||
#define USBAT_FLASH_MEDIA_SAME 0
|
||||
#define USBAT_FLASH_MEDIA_CHANGED 1
|
||||
|
||||
/* USBAT ATA registers */
|
||||
#define USBAT_ATA_DATA 0x10 /* read/write data (R/W) */
|
||||
#define USBAT_ATA_FEATURES 0x11 /* set features (W) */
|
||||
#define USBAT_ATA_ERROR 0x11 /* error (R) */
|
||||
#define USBAT_ATA_SECCNT 0x12 /* sector count (R/W) */
|
||||
#define USBAT_ATA_SECNUM 0x13 /* sector number (R/W) */
|
||||
#define USBAT_ATA_LBA_ME 0x14 /* cylinder low (R/W) */
|
||||
#define USBAT_ATA_LBA_HI 0x15 /* cylinder high (R/W) */
|
||||
#define USBAT_ATA_DEVICE 0x16 /* head/device selection (R/W) */
|
||||
#define USBAT_ATA_STATUS 0x17 /* device status (R) */
|
||||
#define USBAT_ATA_CMD 0x17 /* device command (W) */
|
||||
#define USBAT_ATA_ALTSTATUS 0x0E /* status (no clear IRQ) (R) */
|
||||
|
||||
/* USBAT User I/O Data registers */
|
||||
#define USBAT_UIO_EPAD 0x80 /* Enable Peripheral Control Signals */
|
||||
#define USBAT_UIO_CDT 0x40 /* Card Detect (Read Only) */
|
||||
/* CDT = ACKD & !UI1 & !UI0 */
|
||||
#define USBAT_UIO_1 0x20 /* I/O 1 */
|
||||
#define USBAT_UIO_0 0x10 /* I/O 0 */
|
||||
#define USBAT_UIO_EPP_ATA 0x08 /* 1=EPP mode, 0=ATA mode */
|
||||
#define USBAT_UIO_UI1 0x04 /* Input 1 */
|
||||
#define USBAT_UIO_UI0 0x02 /* Input 0 */
|
||||
#define USBAT_UIO_INTR_ACK 0x01 /* Interrupt (ATA/ISA)/Acknowledge (EPP) */
|
||||
|
||||
/* USBAT User I/O Enable registers */
|
||||
#define USBAT_UIO_DRVRST 0x80 /* Reset Peripheral */
|
||||
#define USBAT_UIO_ACKD 0x40 /* Enable Card Detect */
|
||||
#define USBAT_UIO_OE1 0x20 /* I/O 1 set=output/clr=input */
|
||||
/* If ACKD=1, set OE1 to 1 also. */
|
||||
#define USBAT_UIO_OE0 0x10 /* I/O 0 set=output/clr=input */
|
||||
#define USBAT_UIO_ADPRST 0x01 /* Reset SCM chip */
|
||||
|
||||
/* USBAT Features */
|
||||
#define USBAT_FEAT_ETEN 0x80 /* External trigger enable */
|
||||
#define USBAT_FEAT_U1 0x08
|
||||
#define USBAT_FEAT_U0 0x04
|
||||
#define USBAT_FEAT_ET1 0x02
|
||||
#define USBAT_FEAT_ET2 0x01
|
||||
|
||||
extern int usbat_transport(struct scsi_cmnd *srb, struct us_data *us);
|
||||
extern int init_usbat_cd(struct us_data *us);
|
||||
extern int init_usbat_flash(struct us_data *us);
|
||||
extern int init_usbat_probe(struct us_data *us);
|
||||
|
||||
struct usbat_info {
|
||||
int devicetype;
|
||||
|
||||
/* Used for Flash readers only */
|
||||
unsigned long sectors; /* total sector count */
|
||||
unsigned long ssize; /* sector size in bytes */
|
||||
|
||||
unsigned char sense_key;
|
||||
unsigned long sense_asc; /* additional sense code */
|
||||
unsigned long sense_ascq; /* additional sense code qualifier */
|
||||
};
|
||||
|
||||
#endif
|
||||
1222
drivers/usb/storage/transport.c
Normal file
1222
drivers/usb/storage/transport.c
Normal file
File diff suppressed because it is too large
Load Diff
144
drivers/usb/storage/transport.h
Normal file
144
drivers/usb/storage/transport.h
Normal file
@@ -0,0 +1,144 @@
|
||||
/* Driver for USB Mass Storage compliant devices
|
||||
* Transport Functions Header File
|
||||
*
|
||||
* $Id: transport.h,v 1.1.1.1 2007/06/12 07:27:09 eyryu Exp $
|
||||
*
|
||||
* Current development and maintenance by:
|
||||
* (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
|
||||
*
|
||||
* This driver is based on the 'USB Mass Storage Class' document. This
|
||||
* describes in detail the protocol used to communicate with such
|
||||
* devices. Clearly, the designers had SCSI and ATAPI commands in
|
||||
* mind when they created this document. The commands are all very
|
||||
* similar to commands in the SCSI-II and ATAPI specifications.
|
||||
*
|
||||
* It is important to note that in a number of cases this class
|
||||
* exhibits class-specific exemptions from the USB specification.
|
||||
* Notably the usage of NAK, STALL and ACK differs from the norm, in
|
||||
* that they are used to communicate wait, failed and OK on commands.
|
||||
*
|
||||
* Also, for certain devices, the interrupt endpoint is used to convey
|
||||
* status of a command.
|
||||
*
|
||||
* Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more
|
||||
* information about this driver.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef _TRANSPORT_H_
|
||||
#define _TRANSPORT_H_
|
||||
|
||||
#include <linux/blkdev.h>
|
||||
|
||||
/*
|
||||
* Bulk only data structures
|
||||
*/
|
||||
|
||||
/* command block wrapper */
|
||||
struct bulk_cb_wrap {
|
||||
__le32 Signature; /* contains 'USBC' */
|
||||
__u32 Tag; /* unique per command id */
|
||||
__le32 DataTransferLength; /* size of data */
|
||||
__u8 Flags; /* direction in bit 0 */
|
||||
__u8 Lun; /* LUN normally 0 */
|
||||
__u8 Length; /* of of the CDB */
|
||||
__u8 CDB[16]; /* max command */
|
||||
};
|
||||
|
||||
#define US_BULK_CB_WRAP_LEN 31
|
||||
#define US_BULK_CB_SIGN 0x43425355 /*spells out USBC */
|
||||
#define US_BULK_FLAG_IN 1
|
||||
#define US_BULK_FLAG_OUT 0
|
||||
|
||||
/* command status wrapper */
|
||||
struct bulk_cs_wrap {
|
||||
__le32 Signature; /* should = 'USBS' */
|
||||
__u32 Tag; /* same as original command */
|
||||
__le32 Residue; /* amount not transferred */
|
||||
__u8 Status; /* see below */
|
||||
__u8 Filler[18];
|
||||
};
|
||||
|
||||
#define US_BULK_CS_WRAP_LEN 13
|
||||
#define US_BULK_CS_SIGN 0x53425355 /* spells out 'USBS' */
|
||||
#define US_BULK_STAT_OK 0
|
||||
#define US_BULK_STAT_FAIL 1
|
||||
#define US_BULK_STAT_PHASE 2
|
||||
|
||||
/* bulk-only class specific requests */
|
||||
#define US_BULK_RESET_REQUEST 0xff
|
||||
#define US_BULK_GET_MAX_LUN 0xfe
|
||||
|
||||
/*
|
||||
* usb_stor_bulk_transfer_xxx() return codes, in order of severity
|
||||
*/
|
||||
|
||||
#define USB_STOR_XFER_GOOD 0 /* good transfer */
|
||||
#define USB_STOR_XFER_SHORT 1 /* transferred less than expected */
|
||||
#define USB_STOR_XFER_STALLED 2 /* endpoint stalled */
|
||||
#define USB_STOR_XFER_LONG 3 /* device tried to send too much */
|
||||
#define USB_STOR_XFER_ERROR 4 /* transfer died in the middle */
|
||||
|
||||
/*
|
||||
* Transport return codes
|
||||
*/
|
||||
|
||||
#define USB_STOR_TRANSPORT_GOOD 0 /* Transport good, command good */
|
||||
#define USB_STOR_TRANSPORT_FAILED 1 /* Transport good, command failed */
|
||||
#define USB_STOR_TRANSPORT_NO_SENSE 2 /* Command failed, no auto-sense */
|
||||
#define USB_STOR_TRANSPORT_ERROR 3 /* Transport bad (i.e. device dead) */
|
||||
|
||||
/*
|
||||
* We used to have USB_STOR_XFER_ABORTED and USB_STOR_TRANSPORT_ABORTED
|
||||
* return codes. But now the transport and low-level transfer routines
|
||||
* treat an abort as just another error (-ENOENT for a cancelled URB).
|
||||
* It is up to the invoke_transport() function to test for aborts and
|
||||
* distinguish them from genuine communication errors.
|
||||
*/
|
||||
|
||||
/*
|
||||
* CBI accept device specific command
|
||||
*/
|
||||
|
||||
#define US_CBI_ADSC 0
|
||||
|
||||
extern int usb_stor_CBI_transport(struct scsi_cmnd *, struct us_data*);
|
||||
|
||||
extern int usb_stor_CB_transport(struct scsi_cmnd *, struct us_data*);
|
||||
extern int usb_stor_CB_reset(struct us_data*);
|
||||
|
||||
extern int usb_stor_Bulk_transport(struct scsi_cmnd *, struct us_data*);
|
||||
extern int usb_stor_Bulk_max_lun(struct us_data*);
|
||||
extern int usb_stor_Bulk_reset(struct us_data*);
|
||||
|
||||
extern void usb_stor_invoke_transport(struct scsi_cmnd *, struct us_data*);
|
||||
extern void usb_stor_stop_transport(struct us_data*);
|
||||
|
||||
extern int usb_stor_control_msg(struct us_data *us, unsigned int pipe,
|
||||
u8 request, u8 requesttype, u16 value, u16 index,
|
||||
void *data, u16 size, int timeout);
|
||||
extern int usb_stor_clear_halt(struct us_data *us, unsigned int pipe);
|
||||
|
||||
extern int usb_stor_ctrl_transfer(struct us_data *us, unsigned int pipe,
|
||||
u8 request, u8 requesttype, u16 value, u16 index,
|
||||
void *data, u16 size);
|
||||
extern int usb_stor_bulk_transfer_buf(struct us_data *us, unsigned int pipe,
|
||||
void *buf, unsigned int length, unsigned int *act_len);
|
||||
extern int usb_stor_bulk_transfer_sg(struct us_data *us, unsigned int pipe,
|
||||
void *buf, unsigned int length, int use_sg, int *residual);
|
||||
|
||||
extern int usb_stor_port_reset(struct us_data *us);
|
||||
#endif
|
||||
1484
drivers/usb/storage/unusual_devs.h
Normal file
1484
drivers/usb/storage/unusual_devs.h
Normal file
File diff suppressed because it is too large
Load Diff
1107
drivers/usb/storage/usb.c
Normal file
1107
drivers/usb/storage/usb.c
Normal file
File diff suppressed because it is too large
Load Diff
179
drivers/usb/storage/usb.h
Normal file
179
drivers/usb/storage/usb.h
Normal file
@@ -0,0 +1,179 @@
|
||||
/* Driver for USB Mass Storage compliant devices
|
||||
* Main Header File
|
||||
*
|
||||
* $Id: usb.h,v 1.1.1.1 2007/06/12 07:27:09 eyryu Exp $
|
||||
*
|
||||
* Current development and maintenance by:
|
||||
* (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
|
||||
*
|
||||
* Initial work by:
|
||||
* (c) 1999 Michael Gee (michael@linuxspecific.com)
|
||||
*
|
||||
* This driver is based on the 'USB Mass Storage Class' document. This
|
||||
* describes in detail the protocol used to communicate with such
|
||||
* devices. Clearly, the designers had SCSI and ATAPI commands in
|
||||
* mind when they created this document. The commands are all very
|
||||
* similar to commands in the SCSI-II and ATAPI specifications.
|
||||
*
|
||||
* It is important to note that in a number of cases this class
|
||||
* exhibits class-specific exemptions from the USB specification.
|
||||
* Notably the usage of NAK, STALL and ACK differs from the norm, in
|
||||
* that they are used to communicate wait, failed and OK on commands.
|
||||
*
|
||||
* Also, for certain devices, the interrupt endpoint is used to convey
|
||||
* status of a command.
|
||||
*
|
||||
* Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more
|
||||
* information about this driver.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef _USB_H_
|
||||
#define _USB_H_
|
||||
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb_usual.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <scsi/scsi_host.h>
|
||||
|
||||
struct us_data;
|
||||
struct scsi_cmnd;
|
||||
|
||||
/*
|
||||
* Unusual device list definitions
|
||||
*/
|
||||
|
||||
struct us_unusual_dev {
|
||||
const char* vendorName;
|
||||
const char* productName;
|
||||
__u8 useProtocol;
|
||||
__u8 useTransport;
|
||||
int (*initFunction)(struct us_data *);
|
||||
};
|
||||
|
||||
|
||||
/* Dynamic flag definitions: used in set_bit() etc. */
|
||||
#define US_FLIDX_URB_ACTIVE 18 /* 0x00040000 current_urb is in use */
|
||||
#define US_FLIDX_SG_ACTIVE 19 /* 0x00080000 current_sg is in use */
|
||||
#define US_FLIDX_ABORTING 20 /* 0x00100000 abort is in progress */
|
||||
#define US_FLIDX_DISCONNECTING 21 /* 0x00200000 disconnect in progress */
|
||||
#define ABORTING_OR_DISCONNECTING ((1UL << US_FLIDX_ABORTING) | \
|
||||
(1UL << US_FLIDX_DISCONNECTING))
|
||||
#define US_FLIDX_RESETTING 22 /* 0x00400000 device reset in progress */
|
||||
#define US_FLIDX_TIMED_OUT 23 /* 0x00800000 SCSI midlayer timed out */
|
||||
|
||||
|
||||
#define USB_STOR_STRING_LEN 32
|
||||
|
||||
/*
|
||||
* We provide a DMA-mapped I/O buffer for use with small USB transfers.
|
||||
* It turns out that CB[I] needs a 12-byte buffer and Bulk-only needs a
|
||||
* 31-byte buffer. But Freecom needs a 64-byte buffer, so that's the
|
||||
* size we'll allocate.
|
||||
*/
|
||||
|
||||
#define US_IOBUF_SIZE 64 /* Size of the DMA-mapped I/O buffer */
|
||||
#define US_SENSE_SIZE 18 /* Size of the autosense data buffer */
|
||||
|
||||
typedef int (*trans_cmnd)(struct scsi_cmnd *, struct us_data*);
|
||||
typedef int (*trans_reset)(struct us_data*);
|
||||
typedef void (*proto_cmnd)(struct scsi_cmnd*, struct us_data*);
|
||||
typedef void (*extra_data_destructor)(void *); /* extra data destructor */
|
||||
typedef void (*pm_hook)(struct us_data *, int); /* power management hook */
|
||||
|
||||
#define US_SUSPEND 0
|
||||
#define US_RESUME 1
|
||||
|
||||
/* we allocate one of these for every device that we remember */
|
||||
struct us_data {
|
||||
/* The device we're working with
|
||||
* It's important to note:
|
||||
* (o) you must hold dev_mutex to change pusb_dev
|
||||
*/
|
||||
struct mutex dev_mutex; /* protect pusb_dev */
|
||||
struct usb_device *pusb_dev; /* this usb_device */
|
||||
struct usb_interface *pusb_intf; /* this interface */
|
||||
struct us_unusual_dev *unusual_dev; /* device-filter entry */
|
||||
unsigned long flags; /* from filter initially */
|
||||
unsigned int send_bulk_pipe; /* cached pipe values */
|
||||
unsigned int recv_bulk_pipe;
|
||||
unsigned int send_ctrl_pipe;
|
||||
unsigned int recv_ctrl_pipe;
|
||||
unsigned int recv_intr_pipe;
|
||||
|
||||
/* information about the device */
|
||||
char *transport_name;
|
||||
char *protocol_name;
|
||||
__le32 bcs_signature;
|
||||
u8 subclass;
|
||||
u8 protocol;
|
||||
u8 max_lun;
|
||||
|
||||
u8 ifnum; /* interface number */
|
||||
u8 ep_bInterval; /* interrupt interval */
|
||||
|
||||
/* function pointers for this device */
|
||||
trans_cmnd transport; /* transport function */
|
||||
trans_reset transport_reset; /* transport device reset */
|
||||
proto_cmnd proto_handler; /* protocol handler */
|
||||
|
||||
/* SCSI interfaces */
|
||||
struct scsi_cmnd *srb; /* current srb */
|
||||
unsigned int tag; /* current dCBWTag */
|
||||
|
||||
/* control and bulk communications data */
|
||||
struct urb *current_urb; /* USB requests */
|
||||
struct usb_ctrlrequest *cr; /* control requests */
|
||||
struct usb_sg_request current_sg; /* scatter-gather req. */
|
||||
unsigned char *iobuf; /* I/O buffer */
|
||||
unsigned char *sensebuf; /* sense data buffer */
|
||||
dma_addr_t cr_dma; /* buffer DMA addresses */
|
||||
dma_addr_t iobuf_dma;
|
||||
|
||||
/* mutual exclusion and synchronization structures */
|
||||
struct semaphore sema; /* to sleep thread on */
|
||||
struct completion notify; /* thread begin/end */
|
||||
wait_queue_head_t delay_wait; /* wait during scan, reset */
|
||||
|
||||
/* subdriver information */
|
||||
void *extra; /* Any extra data */
|
||||
extra_data_destructor extra_destructor;/* extra data destructor */
|
||||
#ifdef CONFIG_PM
|
||||
pm_hook suspend_resume_hook;
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Convert between us_data and the corresponding Scsi_Host */
|
||||
static inline struct Scsi_Host *us_to_host(struct us_data *us) {
|
||||
return container_of((void *) us, struct Scsi_Host, hostdata);
|
||||
}
|
||||
static inline struct us_data *host_to_us(struct Scsi_Host *host) {
|
||||
return (struct us_data *) host->hostdata;
|
||||
}
|
||||
|
||||
/* Function to fill an inquiry response. See usb.c for details */
|
||||
extern void fill_inquiry_response(struct us_data *us,
|
||||
unsigned char *data, unsigned int data_len);
|
||||
|
||||
/* The scsi_lock() and scsi_unlock() macros protect the sm_state and the
|
||||
* single queue element srb for write access */
|
||||
#define scsi_unlock(host) spin_unlock_irq(host->host_lock)
|
||||
#define scsi_lock(host) spin_lock_irq(host->host_lock)
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user