Commit 48ad999d authored by Linus Torvalds's avatar Linus Torvalds

v2.4.7.2 -> v2.4.7.3

  - Ben Collins: 1394 updates
  - Matthew Dharm: USB storage update
  - Ion Badulescu: starfire driver update
  - VM aging cleanups
parent 80b4c191
VERSION = 2
PATCHLEVEL = 4
SUBLEVEL = 8
EXTRAVERSION =-pre2
EXTRAVERSION =-pre3
KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
......
......@@ -147,10 +147,6 @@ static void init_hosts(struct hpsb_host_template *tmpl)
int count;
struct hpsb_host *host;
/* PCI cards should register one host at a time */
if (tmpl->detect_hosts == NULL)
return;
count = tmpl->detect_hosts(tmpl);
for (host = tmpl->hosts; host != NULL; host = host->next) {
......@@ -254,8 +250,13 @@ static int remove_template(struct hpsb_host_template *tmpl)
int hpsb_register_lowlevel(struct hpsb_host_template *tmpl)
{
add_template(tmpl);
HPSB_DEBUG("Registered %s driver, initializing now", tmpl->name);
init_hosts(tmpl);
/* PCI cards should be smart and use the PCI detection layer, and
* not this one shot deal. detect_hosts() will be obsoleted soon. */
if (tmpl->detect_hosts != NULL) {
HPSB_DEBUG("Registered %s driver, initializing now", tmpl->name);
init_hosts(tmpl);
}
return 0;
}
......
......@@ -293,12 +293,13 @@ static void build_speed_map(struct hpsb_host *host, int nodecount)
void hpsb_selfid_received(struct hpsb_host *host, quadlet_t sid)
{
if (host->in_bus_reset) {
HPSB_DEBUG("Including SelfID 0x%x", sid);
#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
HPSB_INFO("Including SelfID 0x%x", sid);
#endif
host->topology_map[host->selfid_count++] = sid;
} else {
/* FIXME - info on which host */
HPSB_NOTICE("Spurious SelfID packet (0x%08x) received from %s",
sid, host->template->name);
HPSB_NOTICE("Spurious SelfID packet (0x%08x) received from bus %d",
sid, (host->node_id & BUS_MASK) >> 6);
}
}
......
......@@ -76,5 +76,5 @@ EXPORT_SYMBOL(highlevel_remove_host);
EXPORT_SYMBOL(highlevel_host_reset);
EXPORT_SYMBOL(highlevel_add_one_host);
EXPORT_SYMBOL(hpsb_guid_get_handle);
EXPORT_SYMBOL(hpsb_get_host_by_ge);
EXPORT_SYMBOL(hpsb_get_host_by_ne);
EXPORT_SYMBOL(hpsb_guid_fill_packet);
......@@ -39,18 +39,31 @@
* XXX: Most of this isn't done yet :) */
static atomic_t outstanding_requests;
static LIST_HEAD(node_list);
rwlock_t node_lock = RW_LOCK_UNLOCKED;
static LIST_HEAD(host_info_list);
spinlock_t host_info_lock = SPIN_LOCK_UNLOCKED;
struct bus_options {
u8 irmc; /* Iso Resource Manager Capable */
u8 cmc; /* Cycle Master Capable */
u8 isc; /* Iso Capable */
u8 bmc; /* Bus Master Capable */
u8 pmc; /* Power Manager Capable (PNP spec) */
u8 cyc_clk_acc; /* Cycle clock accuracy */
u8 generation; /* Incremented when configrom changes */
u8 lnkspd; /* Link speed */
u16 max_rec; /* Maximum packet size node can receive */
atomic_t changed; /* We set this to 1 if generation has changed */
};
struct host_info {
struct hpsb_host *host;
int pid;
wait_queue_head_t reset_wait;
pid_t pid; /* PID of the nodemgr thread */
pid_t ppid; /* Parent PID for the thread */
struct tq_struct task; /* Used to kickstart the thread */
wait_queue_head_t reset_wait; /* Wait queue awoken on bus reset */
struct list_head list;
};
......@@ -62,70 +75,126 @@ struct node_entry {
struct hpsb_host *host;
nodeid_t node_id;
struct bus_options busopt;
atomic_t generation;
};
static struct node_entry *create_node_entry(void)
{
struct node_entry *ge;
struct node_entry *ne;
unsigned long flags;
ge = kmalloc(sizeof(struct node_entry), SLAB_ATOMIC);
if (!ge) return NULL;
ne = kmalloc(sizeof(struct node_entry), SLAB_ATOMIC);
if (!ne) return NULL;
INIT_LIST_HEAD(&ge->list);
atomic_set(&ge->refcount, 0);
ge->guid = (u64) -1;
ge->host = NULL;
ge->node_id = 0;
atomic_set(&ge->generation, -1);
INIT_LIST_HEAD(&ne->list);
atomic_set(&ne->refcount, 0);
ne->guid = (u64) -1;
ne->host = NULL;
ne->node_id = 0;
atomic_set(&ne->generation, -1);
atomic_set(&ne->busopt.changed, 0);
write_lock_irqsave(&node_lock, flags);
list_add_tail(&ge->list, &node_list);
list_add_tail(&ne->list, &node_list);
write_unlock_irqrestore(&node_lock, flags);
return ge;
return ne;
}
static struct node_entry *find_entry(u64 guid)
{
struct list_head *lh;
struct node_entry *ge;
struct node_entry *ne;
lh = node_list.next;
while (lh != &node_list) {
ge = list_entry(lh, struct node_entry, list);
if (ge->guid == guid) return ge;
ne = list_entry(lh, struct node_entry, list);
if (ne->guid == guid) return ne;
lh = lh->next;
}
return NULL;
}
static void associate_guid(struct hpsb_host *host, nodeid_t nodeid, u64 guid)
static int register_guid(struct hpsb_host *host, nodeid_t nodeid, u64 guid,
quadlet_t busoptions)
{
struct node_entry *ge;
unsigned long flags;
HPSB_DEBUG("Node %d on %s host: GUID %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
nodeid & NODE_MASK, host->template->name, ((u8 *)&guid)[0],
((u8 *)&guid)[1], ((u8 *)&guid)[2], ((u8 *)&guid)[3],
((u8 *)&guid)[4], ((u8 *)&guid)[5], ((u8 *)&guid)[6],
((u8 *)&guid)[7]);
struct node_entry *ne;
unsigned long flags, new = 0;
read_lock_irqsave(&node_lock, flags);
ge = find_entry(guid);
ne = find_entry(guid);
read_unlock_irqrestore(&node_lock, flags);
if (!ge) ge = create_node_entry();
if (!ge) return;
/* New entry */
if (!ne) {
if ((ne = create_node_entry()) == NULL)
return -1;
ge->host = host;
ge->node_id = nodeid;
ge->guid = guid;
HPSB_DEBUG("%s added: node %d, bus %d: GUID %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
(host->node_id == nodeid) ? "Local host" : "Device",
nodeid & NODE_MASK, (nodeid & BUS_MASK) >> 6, ((u8 *)&guid)[0],
((u8 *)&guid)[1], ((u8 *)&guid)[2], ((u8 *)&guid)[3],
((u8 *)&guid)[4], ((u8 *)&guid)[5], ((u8 *)&guid)[6],
((u8 *)&guid)[7]);
atomic_set(&ge->generation, get_hpsb_generation());
ne->guid = guid;
new = 1;
}
if (!new && ne->node_id != nodeid)
HPSB_DEBUG("Node %d changed to %d on bus %d",
ne->node_id & NODE_MASK, nodeid & NODE_MASK, (nodeid & BUS_MASK) >> 6);
ne->host = host;
ne->node_id = nodeid;
atomic_set(&ne->generation, get_hpsb_generation());
/* Now set the bus options. Only do all this crap if this is a new
* node, or if the generation number has changed. */
if (new || ne->busopt.generation != ((busoptions >> 6) & 0x3)) {
ne->busopt.irmc = (busoptions >> 31) & 1;
ne->busopt.cmc = (busoptions >> 30) & 1;
ne->busopt.isc = (busoptions >> 29) & 1;
ne->busopt.bmc = (busoptions >> 28) & 1;
ne->busopt.pmc = (busoptions >> 27) & 1;
ne->busopt.cyc_clk_acc = (busoptions >> 16) & 0xff;
ne->busopt.max_rec = 1 << (((busoptions >> 12) & 0xf) + 1);
ne->busopt.generation = (busoptions >> 6) & 0x3;
ne->busopt.lnkspd = busoptions & 0x7;
new = 1; /* To make sure we probe the rest of the ConfigROM too */
}
#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
HPSB_DEBUG("raw=0x%08x irmc=%d cmc=%d isc=%d bmc=%d pmc=%d cyc_clk_acc=%d "
"max_rec=%d gen=%d lspd=%d\n", busoptions,
ne->busopt.irmc, ne->busopt.cmc, ne->busopt.isc, ne->busopt.bmc,
ne->busopt.pmc, ne->busopt.cyc_clk_acc, ne->busopt.max_rec,
ne->busopt.generation, ne->busopt.lnkspd);
#endif
return new;
}
static void nodemgr_remove_node(struct node_entry *ne)
{
HPSB_DEBUG("Device removed: node %d, bus %d: GUID "
"%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
ne->node_id & NODE_MASK, (ne->node_id & BUS_MASK) >> 6,
((u8 *)&ne->guid)[0], ((u8 *)&ne->guid)[1],
((u8 *)&ne->guid)[2], ((u8 *)&ne->guid)[3],
((u8 *)&ne->guid)[4], ((u8 *)&ne->guid)[5],
((u8 *)&ne->guid)[6], ((u8 *)&ne->guid)[7]);
list_del(&ne->list);
kfree(ne);
return;
}
/* This is where we probe the nodes for their information and provided
......@@ -133,94 +202,150 @@ static void associate_guid(struct hpsb_host *host, nodeid_t nodeid, u64 guid)
static void nodemgr_node_probe(struct hpsb_host *host)
{
struct selfid *sid = (struct selfid *)host->topology_map;
struct list_head *lh;
struct node_entry *ne;
int nodecount = host->node_count;
nodeid_t nodeid = LOCAL_BUS;
quadlet_t buffer[5], quad;
octlet_t base = CSR_REGISTER_BASE + CSR_CONFIG_ROM;
int retval;
int flags;
/* We need to detect when the ConfigROM's generation has changed,
* so we only update the node's info when it needs to be. */
for (; nodecount; nodecount--, nodeid++, sid++) {
int header_count = 0;
unsigned header_size = 0;
int retries = 3;
int header_count;
unsigned header_size;
octlet_t guid;
/* Skip extended, and non-active node's */
while (sid->extended)
sid++;
if (!sid->link_active)
continue;
if (nodeid == host->node_id)
/* Just use our local ROM */
if (nodeid == host->node_id) {
int i;
for (i = 0; i < 5; i++)
buffer[i] = be32_to_cpu(host->csr.rom[i]);
goto set_options;
}
retry_configrom:
if (!retries--) {
HPSB_ERR("Giving up on node %d for ConfigROM probe, too many errors",
nodeid & NODE_MASK);
continue;
}
HPSB_DEBUG("Initiating ConfigROM request for node %d", nodeid & NODE_MASK);
header_count = 0;
header_size = 0;
retval = hpsb_read(host, nodeid, base, &quad, 4);
buffer[header_count++] = be32_to_cpu(quad);
#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
HPSB_INFO("Initiating ConfigROM request for node %d", nodeid & NODE_MASK);
#endif
/* Now, P1212 says that devices should support 64byte block
* reads, aligned on 64byte boundaries. That doesn't seem
* to work though, and we are forced to doing quadlet
* sized reads. */
if (retval) {
HPSB_ERR("ConfigROM quadlet transaction error for %d",
if (hpsb_read(host, nodeid, base, &quad, 4)) {
HPSB_ERR("ConfigROM quadlet transaction error for node %d",
nodeid & NODE_MASK);
continue;
goto retry_configrom;
}
buffer[header_count++] = be32_to_cpu(quad);
header_size = buffer[0] >> 24;
if (header_size < 4) {
HPSB_INFO("Node %d on %s host has non-standard ROM format (%d quads), "
"cannot parse", nodeid & NODE_MASK, host->template->name,
HPSB_INFO("Node %d on bus %d has non-standard ROM format (%d quads), "
"cannot parse", nodeid & NODE_MASK, (nodeid & BUS_MASK) >> 6,
header_size);
continue;
}
while (header_count <= header_size && (header_count<<2) < sizeof(buffer)) {
retval = hpsb_read(host, nodeid, base + (header_count<<2), &quad, 4);
buffer[header_count++] = be32_to_cpu(quad);
if (retval) {
if (hpsb_read(host, nodeid, base + (header_count<<2), &quad, 4)) {
HPSB_ERR("ConfigROM quadlet transaction error for %d",
nodeid & NODE_MASK);
goto failed_read;
goto retry_configrom;
}
buffer[header_count++] = be32_to_cpu(quad);
}
set_options:
guid = be64_to_cpu(((u64)buffer[3] << 32) | buffer[4]);
switch (register_guid(host, nodeid, guid, buffer[2])) {
case -1:
HPSB_ERR("Failed to register node in ConfigROM probe");
continue;
case 1:
/* Need to probe the rest of the ConfigROM
* here. */
break;
default:
/* Nothing to do, this is an old unchanged
* node. */
break;
}
associate_guid(host, nodeid, be64_to_cpu(((u64)buffer[3] << 32) | buffer[4]));
failed_read:
continue;
}
/* Need to detect when nodes are no longer associated with
* anything. I believe we can do this using the generation of the
* entries after a reset, compared the the hosts generation. */
/* Now check to see if we have any nodes that aren't referenced
* any longer. */
write_lock_irqsave(&node_lock, flags);
lh = node_list.next;
while (lh != &node_list) {
ne = list_entry(lh, struct node_entry, list);
/* Only checking this host */
if (ne->host != host)
continue;
/* If the generation didn't get updated, then either the
* node was removed, or it failed the above probe. Either
* way, we remove references to it, since they are
* invalid. */
if (atomic_read(&ne->generation) != get_hpsb_generation())
nodemgr_remove_node(ne);
lh = lh->next;
}
write_unlock_irqrestore(&node_lock, flags);
return;
}
struct node_entry *hpsb_guid_get_handle(u64 guid)
{
unsigned long flags;
struct node_entry *ge;
struct node_entry *ne;
read_lock_irqsave(&node_lock, flags);
ge = find_entry(guid);
if (ge) atomic_inc(&ge->refcount);
ne = find_entry(guid);
if (ne) atomic_inc(&ne->refcount);
read_unlock_irqrestore(&node_lock, flags);
return ge;
return ne;
}
struct hpsb_host *hpsb_get_host_by_ge(struct node_entry *ge)
struct hpsb_host *hpsb_get_host_by_ne(struct node_entry *ne)
{
if (atomic_read(&ge->generation) != get_hpsb_generation()) return NULL;
if (ge->node_id == ge->host->node_id) return ge->host;
if (atomic_read(&ne->generation) != get_hpsb_generation()) return NULL;
if (ne->node_id == ne->host->node_id) return ne->host;
return NULL;
}
int hpsb_guid_fill_packet(struct node_entry *ge, struct hpsb_packet *pkt)
int hpsb_guid_fill_packet(struct node_entry *ne, struct hpsb_packet *pkt)
{
if (atomic_read(&ge->generation) != get_hpsb_generation()) return 0;
if (atomic_read(&ne->generation) != get_hpsb_generation()) return 0;
pkt->host = ge->host;
pkt->node_id = ge->node_id;
pkt->generation = atomic_read(&ge->generation);
pkt->host = ne->host;
pkt->node_id = ne->node_id;
pkt->generation = atomic_read(&ne->generation);
return 1;
}
......@@ -229,33 +354,38 @@ static int nodemgr_reset_handler(void *__hi)
struct host_info *hi = (struct host_info *)__hi;
struct hpsb_host *host = hi->host;
/* Standard thread setup */
lock_kernel();
daemonize();
strcpy(current->comm, "NodeMngr");
unlock_kernel();
for (;;) {
if (signal_pending(current))
break;
siginitsetinv(&current->blocked, sigmask(SIGKILL)|sigmask(SIGINT)|sigmask(SIGTERM));
/* Let's take a short pause to make sure all the devices
* have time to settle. */
strcpy(current->comm, "NodeMngr");
unlock_kernel();
do {
current->state = TASK_INTERRUPTIBLE;
schedule_timeout(HZ/50);
if (hi && host)
if (hi && host) {
nodemgr_node_probe(host);
/* Wait for the next bus reset */
if (hi && host)
interruptible_sleep_on(&hi->reset_wait);
}
} else
break;
} while (!signal_pending(current) && hi);
return(0);
}
static void nodemgr_schedule_thread (void *__hi)
{
struct host_info *hi = (struct host_info *)__hi;
hi->ppid = current->pid;
hi->pid = kernel_thread(nodemgr_reset_handler, hi, 0);
}
static void nodemgr_add_host(struct hpsb_host *host)
{
struct host_info *hi = kmalloc (sizeof (struct host_info), GFP_KERNEL);
......@@ -266,10 +396,14 @@ static void nodemgr_add_host(struct hpsb_host *host)
return;
}
/* We simply initialize the struct here. We don't start the thread
* until the first bus reset. */
hi->host = host;
INIT_LIST_HEAD(&hi->list);
hi->pid = -1;
hi->ppid = -1;
init_waitqueue_head(&hi->reset_wait);
INIT_TQUEUE(&hi->task, nodemgr_schedule_thread, hi);
spin_lock_irqsave (&host_info_lock, flags);
list_add_tail (&hi->list, &host_info_list);
......@@ -278,14 +412,6 @@ static void nodemgr_add_host(struct hpsb_host *host)
return;
}
static void nodemgr_schedule_thread (void *__hi)
{
struct host_info *hi = (struct host_info *)__hi;
hi->pid = kernel_thread(nodemgr_reset_handler, hi,
CLONE_FS|CLONE_FILES|CLONE_SIGHAND);
}
static void nodemgr_host_reset(struct hpsb_host *host)
{
struct list_head *lh;
......@@ -311,18 +437,7 @@ static void nodemgr_host_reset(struct hpsb_host *host)
if (hi->pid >= 0) {
wake_up(&hi->reset_wait);
} else {
if (in_interrupt()) {
static struct tq_struct task;
memset(&task, 0, sizeof(struct tq_struct));
task.routine = nodemgr_schedule_thread;
task.data = (void*)hi;
if (schedule_task(&task) < 0)
HPSB_ERR ("Failed to schedule Node Manager thread!\n");
} else {
nodemgr_schedule_thread(hi);
}
schedule_task(&hi->task);
}
done_reset_host:
......@@ -335,8 +450,24 @@ static void nodemgr_remove_host(struct hpsb_host *host)
{
struct list_head *lh;
struct host_info *hi = NULL;
struct node_entry *ne;
int flags;
/* First remove all node entries for this host */
write_lock_irqsave(&node_lock, flags);
lh = node_list.next;
while (lh != &node_list) {
ne = list_entry(lh, struct node_entry, list);
/* Only checking this host */
if (ne->host != host)
continue;
nodemgr_remove_node(ne);
lh = lh->next;
}
write_unlock_irqrestore(&node_lock, flags);
spin_lock_irqsave (&host_info_lock, flags);
lh = host_info_list.next;
while (lh != &host_info_list) {
......@@ -349,15 +480,24 @@ static void nodemgr_remove_host(struct hpsb_host *host)
}
if (hi == NULL) {
HPSB_ERR ("Could not remove non-exitent host in Node Manager");
HPSB_ERR ("Could not remove non-existent host in Node Manager");
goto done_remove_host;
}
if (hi->pid >= 0)
kill_proc(hi->pid, SIGINT, 1);
mb();
if (hi->pid >= 0) {
/* Kill the proc */
kill_proc(hi->pid, SIGKILL, 1);
current->state = TASK_INTERRUPTIBLE;
schedule_timeout(HZ*2); /* 2 second delay */
/* XXX: We need a better way... */
current->state = TASK_INTERRUPTIBLE;
schedule_timeout(HZ/20);
/* Now tell the parent to sluff off the zombied body */
mb();
kill_proc(hi->ppid, SIGCHLD, 1);
}
kfree (hi);
......@@ -377,8 +517,6 @@ static struct hpsb_highlevel *hl;
void init_ieee1394_nodemgr(void)
{
atomic_set(&outstanding_requests, 0);
hl = hpsb_register_highlevel("Node manager", &guid_ops);
if (!hl) {
HPSB_ERR("Out of memory during ieee1394 initialization");
......
......@@ -50,7 +50,7 @@ hpsb_guid_t hpsb_guid_get_handle(u64 guid);
* Note that the local GUID currently isn't collected, so this will always
* return NULL.
*/
struct hpsb_host *hpsb_get_host_by_ge(hpsb_guid_t handle);
struct hpsb_host *hpsb_get_host_by_ne(hpsb_guid_t handle);
/*
* This will fill in the given, pre-initialised hpsb_packet with the current
......
......@@ -36,14 +36,8 @@
* . Async Stream Packets
* . DMA error recovery
*
* Things to be fixed:
* . Latency problems on UltraSPARC
*
* Known bugs:
* . SelfID are sometimes not received properly
* if card is initialized with no other nodes
* on the bus
* . Apple PowerBook detected but not working yet
* . Apple PowerBook detected but not working yet (still true?)
*/
/*
......@@ -277,10 +271,7 @@ static int handle_selfid(struct ti_ohci *ohci, struct hpsb_host *host,
size_t size;
quadlet_t q0, q1;
/* SelfID handling seems much easier than for the aic5800 chip.
All the self-id packets, including this devices own self-id,
should be correctly arranged in the selfid buffer at this
stage */
mdelay(10);
/* Check status of self-id reception */
......@@ -320,7 +311,7 @@ static int handle_selfid(struct ti_ohci *ohci, struct hpsb_host *host,
}
if (q0 == ~q1) {
PRINT(KERN_DEBUG, ohci->id, "SelfID packet 0x%x received", q0);
DBGMSG (ohci->id, "SelfID packet 0x%x received", q0);
hpsb_selfid_received(host, cpu_to_be32(q0));
if (((q0 & 0x3f000000) >> 24) == phyid)
DBGMSG (ohci->id, "SelfID for this node is 0x%08x", q0);
......@@ -332,7 +323,7 @@ static int handle_selfid(struct ti_ohci *ohci, struct hpsb_host *host,
size -= 2;
}
PRINT(KERN_DEBUG, ohci->id, "SelfID complete");
DBGMSG(ohci->id, "SelfID complete");
hpsb_selfid_complete(host, phyid, isroot);
return 0;
......@@ -346,10 +337,10 @@ static int ohci_soft_reset(struct ti_ohci *ohci) {
for (i = 0; i < OHCI_LOOP_COUNT; i++) {
if (reg_read(ohci, OHCI1394_HCControlSet) & 0x00010000)
break;
mdelay(10);
mdelay(1);
}
PRINT(KERN_DEBUG, ohci->id, "Soft reset finished");
DBGMSG (ohci->id, "Soft reset finished");
return 0;
}
......@@ -458,41 +449,32 @@ static int get_nb_iso_ctx(struct ti_ohci *ohci, int reg)
return ctx;
}
static void ohci_init_config_rom(struct ti_ohci *ohci);
/* Global initialization */
static int ohci_initialize(struct hpsb_host *host)
{
struct ti_ohci *ohci=host->hostdata;
int retval, i;
quadlet_t buf;
spin_lock_init(&ohci->phy_reg_lock);
spin_lock_init(&ohci->event_lock);
/*
* Tip by James Goodwin <jamesg@Filanet.com>:
* We need to add delays after the soft reset, setting LPS, and
* enabling our link. This might fixes the self-id reception
* problem at initialization.
*/
/* Soft reset */
if ((retval = ohci_soft_reset(ohci)) < 0)
return retval;
/*
* Delay after soft reset to make sure everything has settled
* down (sanity)
*/
mdelay(10);
/* Put some defaults to these undefined bus options */
buf = reg_read(ohci, OHCI1394_BusOptions);
buf |= 0x60000000; /* Enable CMC and ISC */
buf &= ~0x00ff0000; /* XXX: Set cyc_clk_acc to zero for now */
buf &= ~0x98000000; /* Disable PMC, IRMC and BMC */
reg_write(ohci, OHCI1394_BusOptions, buf);
/* Set Link Power Status (LPS) */
reg_write(ohci, OHCI1394_HCControlSet, 0x00080000);
/*
* Delay after setting LPS in order to make sure link/phy
* communication is established
*/
mdelay(10);
/* Set the bus number */
reg_write(ohci, OHCI1394_NodeID, 0x0000ffc0);
......@@ -515,13 +497,15 @@ static int ohci_initialize(struct hpsb_host *host)
/* enable self-id dma */
reg_write(ohci, OHCI1394_LinkControlSet, 0x00000200);
/* Set the configuration ROM mapping register */
/* Set the Config ROM mapping register */
reg_write(ohci, OHCI1394_ConfigROMmap, ohci->csr_config_rom_bus);
/* Initialize the Config ROM */
ohci_init_config_rom(ohci);
/* Now get our max packet size */
ohci->max_packet_size =
1<<(((reg_read(ohci, OHCI1394_BusOptions)>>12)&0xf)+1);
PRINT(KERN_DEBUG, ohci->id, "Max packet size = %d bytes",
ohci->max_packet_size);
/* Don't accept phy packets into AR request context */
reg_write(ohci, OHCI1394_LinkControlClear, 0x00000400);
......@@ -622,6 +606,14 @@ static int ohci_initialize(struct hpsb_host *host)
/* Enable link */
reg_write(ohci, OHCI1394_HCControlSet, 0x00020000);
buf = reg_read(ohci, OHCI1394_Version);
PRINT(KERN_INFO, ohci->id, "OHCI-1394 %d.%d (PCI): IRQ=[%d] MMIO=[%lx-%lx]"
" Max Packet=[%d]", ((((buf) >> 16) & 0xf) + (((buf) >> 20) & 0xf) * 10),
((((buf) >> 4) & 0xf) + ((buf) & 0xf) * 10), ohci->dev->irq,
pci_resource_start(ohci->dev, 0),
pci_resource_start(ohci->dev, 0) + pci_resource_len(ohci->dev, 0),
ohci->max_packet_size);
return 1;
}
......@@ -884,7 +876,7 @@ static int ohci_devctl(struct hpsb_host *host, enum devctl_cmd cmd, int arg)
switch (cmd) {
case RESET_BUS:
PRINT (KERN_DEBUG, ohci->id, "Resetting bus on request%s",
DBGMSG(ohci->id, "Bus reset requested%s",
((host->attempt_root || attempt_root) ?
" and attempting to become root" : ""));
set_phy_reg_mask (ohci, 1, 0x40 | ((host->attempt_root || attempt_root) ?
......@@ -1076,10 +1068,13 @@ static void ohci_irq_handler(int irq, void *dev_id,
struct hpsb_host *host = ohci->host;
int phyid = -1, isroot = 0, flags;
/* Read the interrupt event register */
/* Read the interrupt event register. We don't clear the bus reset
* here. We wait till we get a selfid complete interrupt and clear
* it then, and _only_ then. */
spin_lock_irqsave(&ohci->event_lock, flags);
event = reg_read(ohci, OHCI1394_IntEventClear);
reg_write(ohci, OHCI1394_IntEventClear, event);
reg_write(ohci, OHCI1394_IntEventClear,
event & ~(OHCI1394_selfIDComplete|OHCI1394_busReset));
spin_unlock_irqrestore(&ohci->event_lock, flags);
if (!event) return;
......@@ -1093,15 +1088,12 @@ static void ohci_irq_handler(int irq, void *dev_id,
return;
}
/* Someone wants a bus reset. Better watch what you wish for...
*
* XXX: Read 6.1.1 of the OHCI1394 spec. We need to take special
* care with the BusReset Interrupt, before and until the SelfID
* phase is over. This is why the SelfID phase sometimes fails for
* this driver. */
/* Someone wants a bus reset. Better watch what you wish for... */
if (event & OHCI1394_busReset) {
if (!host->in_bus_reset) {
PRINT(KERN_DEBUG, ohci->id, "Bus reset requested");
DBGMSG(ohci->id, "Bus reset requested%s",
((host->attempt_root || attempt_root) ?
" and attempting to become root" : ""));
/* Wait for the AT fifo to be flushed */
dma_trm_reset(ohci->at_req_context);
......@@ -1112,7 +1104,8 @@ static void ohci_irq_handler(int irq, void *dev_id,
ohci->NumBusResets++;
}
event &= ~OHCI1394_busReset;
/* Mask out everything except selfid */
event &= OHCI1394_selfIDComplete;
}
/* XXX: We need a way to also queue the OHCI1394_reqTxComplete,
......@@ -1219,7 +1212,7 @@ static void ohci_irq_handler(int irq, void *dev_id,
phyid = node_id & 0x0000003f;
isroot = (node_id & 0x40000000) != 0;
PRINT(KERN_DEBUG, ohci->id,
DBGMSG(ohci->id,
"SelfID interrupt received "
"(phyid %d, %s)", phyid,
(isroot ? "root" : "not root"));
......@@ -1249,6 +1242,12 @@ static void ohci_irq_handler(int irq, void *dev_id,
} else
PRINT(KERN_ERR, ohci->id,
"SelfID received outside of bus reset sequence");
/* Clear everything, it's a new day */
spin_lock_irqsave(&ohci->event_lock, flags);
reg_write(ohci, OHCI1394_IntEventClear, 0xffffffff);
spin_unlock_irqrestore(&ohci->event_lock, flags);
event &= ~OHCI1394_selfIDComplete;
}
if (event & OHCI1394_phyRegRcvd) {
......@@ -1260,6 +1259,9 @@ static void ohci_irq_handler(int irq, void *dev_id,
"Physical register received outside of bus reset sequence");
event &= ~OHCI1394_phyRegRcvd;
}
/* Make sure we handle everything, just in case we accidentally
* enabled an interrupt that we didn't write a handler for. */
if (event)
PRINT(KERN_ERR, ohci->id, "Unhandled interrupt(s) 0x%08x\n",
event);
......@@ -1374,7 +1376,7 @@ static void dma_rcv_tasklet (unsigned long data)
spin_unlock_irqrestore(&d->lock, flags);
return;
}
#if 0
if (le32_to_cpu(d->prg_cpu[(idx+1)%d->num_desc]->status)
== d->buf_size) {
/* Other part of packet not written yet.
......@@ -1387,7 +1389,7 @@ static void dma_rcv_tasklet (unsigned long data)
spin_unlock_irqrestore(&d->lock, flags);
return;
}
#endif
split_left = length;
split_ptr = (char *)d->spb;
memcpy(split_ptr,buf_ptr,d->buf_size-offset);
......@@ -1448,7 +1450,7 @@ static void dma_rcv_tasklet (unsigned long data)
hpsb_packet_received(ohci->host, d->spb,
length-4, ack);
}
#if OHCI1394_DEBUG
#ifdef OHCI1394_DEBUG
else
PRINT (KERN_DEBUG, ohci->id, "Got phy packet ctx=%d ... discarded",
d->ctx);
......@@ -1837,41 +1839,47 @@ struct config_rom_ptr {
#define cf_put_keyval(cr, key, val) (((cr)->data++)[0] = cpu_to_be32((key) << 24) | (val))
#define cf_put_crc16(cr, unit) \
(*(cr)->unitdir[unit].start = cpu_to_be32(((cr)->unitdir[unit].length << 16) | \
ohci_crc16((cr)->unitdir[unit].start + 1, (cr)->unitdir[unit].length)))
#define cf_unit_begin(cr, unit) \
do { \
if ((cr)->unitdir[unit].refer != NULL) { \
*(cr)->unitdir[unit].refer |= \
(cr)->data - (cr)->unitdir[unit].refer; \
cf_put_crc16(cr, (cr)->unitdir[unit].refunit); \
} \
(cr)->unitnum = (unit); \
(cr)->unitdir[unit].start = (cr)->data++; \
} while (0)
#define cf_put_refer(cr, key, unit) \
do { \
(cr)->unitdir[unit].refer = (cr)->data; \
(cr)->unitdir[unit].refunit = (cr)->unitnum; \
((cr)->data++)[0] = cpu_to_be32((key) << 24); \
} while(0)
static inline void cf_put_crc16(struct config_rom_ptr *cr, int unit)
{
*cr->unitdir[unit].start =
cpu_to_be32((cr->unitdir[unit].length << 16) |
ohci_crc16(cr->unitdir[unit].start + 1,
cr->unitdir[unit].length));
}
#define cf_unit_end(cr) \
do { \
(cr)->unitdir[(cr)->unitnum].length = (cr)->data - \
((cr)->unitdir[(cr)->unitnum].start + 1); \
cf_put_crc16((cr), (cr)->unitnum); \
} while(0)
static inline void cf_unit_begin(struct config_rom_ptr *cr, int unit)
{
if (cr->unitdir[unit].refer != NULL) {
*cr->unitdir[unit].refer |=
cr->data - cr->unitdir[unit].refer;
cf_put_crc16(cr, cr->unitdir[unit].refunit);
}
cr->unitnum = unit;
cr->unitdir[unit].start = cr->data++;
}
static inline void cf_put_refer(struct config_rom_ptr *cr, char key, int unit)
{
cr->unitdir[unit].refer = cr->data;
cr->unitdir[unit].refunit = cr->unitnum;
(cr->data++)[0] = cpu_to_be32(key << 24);
}
static inline void cf_unit_end(struct config_rom_ptr *cr)
{
cr->unitdir[cr->unitnum].length = cr->data -
(cr->unitdir[cr->unitnum].start + 1);
cf_put_crc16(cr, cr->unitnum);
}
/* End of NetBSD derived code. */
static void ohci_init_config_rom(struct ti_ohci *ohci)
{
struct config_rom_ptr cr;
memset(&cr, 0, sizeof(cr));
memset (ohci->csr_config_rom_cpu, 0, sizeof (ohci->csr_config_rom_cpu));
memset(ohci->csr_config_rom_cpu, 0, sizeof (ohci->csr_config_rom_cpu));
cr.data = ohci->csr_config_rom_cpu;
......@@ -1887,7 +1895,7 @@ static void ohci_init_config_rom(struct ti_ohci *ohci)
reg_read(ohci, OHCI1394_GUIDLo));
/* IEEE P1212 suggests the initial ROM header CRC should only
* cover the header itself (and not the entire ROM). Since we use
* cover the header itself (and not the entire ROM). Since we do
* this, then we can make our bus_info_len the same as the CRC
* length. */
ohci->csr_config_rom_cpu[0] |= cpu_to_be32(
......@@ -1981,7 +1989,7 @@ int ohci_compare_swap(struct ti_ohci *ohci, quadlet_t *data,
if (reg_read(ohci, OHCI1394_CSRControl) & 0x80000000)
break;
mdelay(10);
mdelay(1);
}
*data = reg_read(ohci, OHCI1394_CSRData);
......@@ -1998,29 +2006,15 @@ static quadlet_t ohci_hw_csr_reg(struct hpsb_host *host, int reg,
return data;
}
struct hpsb_host_template *get_ohci_template(void)
{
static struct hpsb_host_template tmpl;
static int initialized = 0;
if (!initialized) {
memset (&tmpl, 0, sizeof (struct hpsb_host_template));
/* Initialize by field names so that a template structure
* reorganization does not influence this code. */
tmpl.name = "ohci1394";
tmpl.initialize_host = ohci_initialize;
tmpl.release_host = ohci_remove;
tmpl.get_rom = get_ohci_rom;
tmpl.transmit_packet = ohci_transmit;
tmpl.devctl = ohci_devctl;
tmpl.hw_csr_reg = ohci_hw_csr_reg;
initialized = 1;
}
return &tmpl;
}
static struct hpsb_host_template ohci_template = {
name: OHCI1394_DRIVER_NAME,
initialize_host: ohci_initialize,
release_host: ohci_remove,
get_rom: get_ohci_rom,
transmit_packet: ohci_transmit,
devctl: ohci_devctl,
hw_csr_reg: ohci_hw_csr_reg,
};
static int __devinit ohci1394_add_one(struct pci_dev *dev, const struct pci_device_id *ent)
{
......@@ -2040,7 +2034,7 @@ static int __devinit ohci1394_add_one(struct pci_dev *dev, const struct pci_devi
}
pci_set_master(dev);
host = hpsb_get_host(get_ohci_template(), sizeof (struct ti_ohci));
host = hpsb_get_host(&ohci_template, sizeof (struct ti_ohci));
if (!host) {
PRINT_G(KERN_ERR, "Out of memory trying to allocate host structure");
return -ENOMEM;
......@@ -2054,8 +2048,6 @@ static int __devinit ohci1394_add_one(struct pci_dev *dev, const struct pci_devi
ohci->host = host;
pci_set_drvdata(dev, ohci);
PRINT(KERN_INFO, ohci->id, "OHCI (PCI) IEEE-1394 Controller");
/* We don't want hardware swapping */
pci_write_config_dword(dev, OHCI1394_PCI_HCI_Control, 0);
......@@ -2173,14 +2165,10 @@ static int __devinit ohci1394_add_one(struct pci_dev *dev, const struct pci_devi
ohci->ISO_channel_usage = 0;
spin_lock_init(&ohci->IR_channel_lock);
if (!request_irq(dev->irq, ohci_irq_handler, SA_SHIRQ,
if (request_irq(dev->irq, ohci_irq_handler, SA_SHIRQ,
OHCI1394_DRIVER_NAME, ohci))
PRINT(KERN_DEBUG, ohci->id, "Allocated interrupt %d", dev->irq);
else
FAIL("Failed to allocate shared interrupt %d", dev->irq);
ohci_init_config_rom(ohci);
/* Tell the highlevel this host is ready */
highlevel_add_one_host (host);
......@@ -2190,7 +2178,9 @@ static int __devinit ohci1394_add_one(struct pci_dev *dev, const struct pci_devi
static void remove_card(struct ti_ohci *ohci)
{
/* Reset the board properly before leaving */
quadlet_t buf;
/* Soft reset before we start */
ohci_soft_reset(ohci);
/* Free AR dma */
......@@ -2207,6 +2197,10 @@ static void remove_card(struct ti_ohci *ohci)
/* Free IT dma */
free_dma_trm_ctx(&ohci->it_context);
/* Disable all interrupts */
reg_write(ohci, OHCI1394_IntMaskClear, 0x80000000);
free_irq(ohci->dev->irq, ohci);
/* Free self-id buffer */
if (ohci->selfid_buf_cpu) {
pci_free_consistent(ohci->dev, OHCI1394_SI_DMA_BUF_SIZE,
......@@ -2222,9 +2216,15 @@ static void remove_card(struct ti_ohci *ohci)
ohci->csr_config_rom_bus);
OHCI_DMA_FREE("consistent csr_config_rom");
}
/* Free the IRQ */
free_irq(ohci->dev->irq, ohci);
/* Disable our bus options */
buf = reg_read(ohci, OHCI1394_BusOptions);
buf &= ~0xf8000000;
buf |= 0x00ff0000;
reg_write(ohci, OHCI1394_BusOptions, buf);
/* Clear LinkEnable and LPS */
reg_write(ohci, OHCI1394_HCControlClear, 0x000a0000);
if (ohci->registers)
iounmap(ohci->registers);
......@@ -2398,7 +2398,7 @@ static void __devexit ohci1394_remove_one(struct pci_dev *pdev)
}
static struct pci_driver ohci1394_driver = {
name: "ohci1394",
name: OHCI1394_DRIVER_NAME,
id_table: ohci1394_pci_tbl,
probe: ohci1394_add_one,
remove: ohci1394_remove_one,
......@@ -2406,20 +2406,20 @@ static struct pci_driver ohci1394_driver = {
static void __exit ohci1394_cleanup (void)
{
hpsb_unregister_lowlevel(get_ohci_template());
hpsb_unregister_lowlevel(&ohci_template);
pci_unregister_driver(&ohci1394_driver);
}
static int __init ohci1394_init(void)
{
int ret;
if (hpsb_register_lowlevel(get_ohci_template())) {
if (hpsb_register_lowlevel(&ohci_template)) {
PRINT_G(KERN_ERR, "Registering failed");
return -ENXIO;
}
if ((ret = pci_module_init(&ohci1394_driver))) {
PRINT_G(KERN_ERR, "PCI module init failed\n");
hpsb_unregister_lowlevel(get_ohci_template());
hpsb_unregister_lowlevel(&ohci_template);
return ret;
}
return ret;
......
......@@ -58,6 +58,17 @@
#define PRINTD(level, card, fmt, args...) do {} while (0)
#endif
static struct pci_device_id pcilynx_pci_tbl[] __devinitdata = {
{
vendor: PCI_VENDOR_ID_TI,
device: PCI_DEVICE_ID_TI_PCILYNX,
subvendor: PCI_ANY_ID,
subdevice: PCI_ANY_ID,
},
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(pci, pcilynx_pci_tbl);
static struct ti_lynx cards[MAX_PCILYNX_CARDS];
static int num_of_cards = 0;
......
......@@ -807,7 +807,7 @@ static int video1394_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
struct video_card *video = NULL;
struct ti_ohci *ohci= video->ohci;
struct ti_ohci *ohci;
unsigned long flags;
struct list_head *lh;
......@@ -818,6 +818,7 @@ static int video1394_ioctl(struct inode *inode, struct file *file,
p = list_entry(lh, struct video_card, list);
if (p->id == MINOR(inode->i_rdev)) {
video = p;
ohci = video->ohci;
break;
}
}
......@@ -1602,7 +1603,7 @@ static struct hpsb_highlevel_ops hl_ops = {
MODULE_AUTHOR("Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au>");
MODULE_DESCRIPTION("driver for digital video on OHCI board");
MODULE_SUPPORTED_DEVICE("video1394");
MODULE_SUPPORTED_DEVICE(VIDEO1394_DRIVER_NAME);
static void __exit video1394_exit_module (void)
{
......
......@@ -87,8 +87,7 @@
LK1.3.3 (Ion Badulescu)
- Initialize the TxMode register properly
- Set the MII registers _after_ resetting it
- Don't dereference dev->priv after unregister_netdev() has freed it
- Don't dereference dev->priv after freeing it
TODO:
- implement tx_timeout() properly
......@@ -988,12 +987,12 @@ static void check_duplex(struct net_device *dev)
struct netdev_private *np = dev->priv;
u16 reg0;
mdio_write(dev, np->phys[0], MII_ADVERTISE, np->advertising);
mdio_write(dev, np->phys[0], MII_BMCR, BMCR_RESET);
udelay(500);
while (mdio_read(dev, np->phys[0], MII_BMCR) & BMCR_RESET);
reg0 = mdio_read(dev, np->phys[0], MII_BMCR);
mdio_write(dev, np->phys[0], MII_ADVERTISE, np->advertising);
if (np->autoneg) {
reg0 |= BMCR_ANENABLE | BMCR_ANRESTART;
......@@ -1940,12 +1939,12 @@ static void __devexit starfire_remove_one (struct pci_dev *pdev)
pci_free_consistent(pdev, PAGE_SIZE,
np->rx_ring, np->rx_ring_dma);
unregister_netdev(dev); /* Will also free np!! */
unregister_netdev(dev);
iounmap((char *)dev->base_addr);
pci_release_regions(pdev);
pci_set_drvdata(pdev, NULL);
kfree(dev);
kfree(dev); /* Will also free np!! */
}
......
......@@ -19,6 +19,9 @@ usb-storage-obj-$(CONFIG_USB_STORAGE_FREECOM) += freecom.o
usb-storage-obj-$(CONFIG_USB_STORAGE_SHUTTLE_SMARTMEDIA) += shuttle_sm.o
usb-storage-obj-$(CONFIG_USB_STORAGE_SHUTTLE_COMPACTFLASH) += shuttle_cf.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-objs := scsiglue.o protocol.o transport.o usb.o \
initializers.o $(usb-storage-obj-y)
......
/* Driver for Datafab USB Compact Flash reader
*
* 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)
*
* 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(). Basically this means
* setting mode_param_header[3] = 0x80.
*/
#include "transport.h"
#include "protocol.h"
#include "usb.h"
#include "debug.h"
#include "datafab.h"
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/malloc.h>
extern int usb_stor_bulk_msg(struct us_data *us, void *data, int pipe,
unsigned int len, unsigned int *act_len);
static int datafab_determine_lun(struct us_data *us, struct datafab_info *info);
static void datafab_dump_data(unsigned char *data, int len)
{
unsigned char buf[80];
int sofar = 0;
if (!data)
return;
memset(buf, 0, sizeof(buf));
for (sofar = 0; sofar < len; sofar++) {
sprintf(buf + strlen(buf), "%02x ",
((unsigned int) data[sofar]) & 0xFF);
if (sofar % 16 == 15) {
US_DEBUGP("datafab: %s\n", buf);
memset(buf, 0, sizeof(buf));
}
}
if (strlen(buf) != 0)
US_DEBUGP("datafab: %s\n", buf);
}
static int datafab_raw_bulk(int direction,
struct us_data *us,
unsigned char *data,
unsigned int len)
{
int result;
int act_len;
int pipe;
if (direction == SCSI_DATA_READ)
pipe = usb_rcvbulkpipe(us->pusb_dev, us->ep_in);
else
pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out);
result = usb_stor_bulk_msg(us, data, pipe, len, &act_len);
// if we stall, we need to clear it before we go on
if (result == -EPIPE) {
US_DEBUGP("datafab_raw_bulk: EPIPE. clearing endpoint halt for"
" pipe 0x%x, stalled at %d bytes\n", pipe, act_len);
usb_clear_halt(us->pusb_dev, pipe);
}
if (result) {
// NAK - that means we've retried a few times already
if (result == -ETIMEDOUT) {
US_DEBUGP("datafab_raw_bulk: device NAKed\n");
return US_BULK_TRANSFER_FAILED;
}
// -ENOENT -- we canceled this transfer
if (result == -ENOENT) {
US_DEBUGP("datafab_raw_bulk: transfer aborted\n");
return US_BULK_TRANSFER_ABORTED;
}
if (result == -EPIPE) {
US_DEBUGP("datafab_raw_bulk: output pipe stalled\n");
return USB_STOR_TRANSPORT_FAILED;
}
// the catch-all case
US_DEBUGP("datafab_raw_bulk: unknown error\n");
return US_BULK_TRANSFER_FAILED;
}
if (act_len != len) {
US_DEBUGP("datafab_raw_bulk: Warning. Transferred only %d bytes\n", act_len);
return US_BULK_TRANSFER_SHORT;
}
US_DEBUGP("datafab_raw_bulk: Transfered %d of %d bytes\n", act_len, len);
return US_BULK_TRANSFER_GOOD;
}
static inline int datafab_bulk_read(struct us_data *us,
unsigned char *data,
unsigned int len)
{
if (len == 0)
return USB_STOR_TRANSPORT_GOOD;
US_DEBUGP("datafab_bulk_read: len = %d\n", len);
return datafab_raw_bulk(SCSI_DATA_READ, us, data, len);
}
static inline int datafab_bulk_write(struct us_data *us,
unsigned char *data,
unsigned int len)
{
if (len == 0)
return USB_STOR_TRANSPORT_GOOD;
US_DEBUGP("datafab_bulk_write: len = %d\n", len);
return datafab_raw_bulk(SCSI_DATA_WRITE, us, data, len);
}
static int datafab_read_data(struct us_data *us,
struct datafab_info *info,
u32 sector,
u32 sectors,
unsigned char *dest,
int use_sg)
{
unsigned char command[8] = { 0, 0, 0, 0, 0, 0xE0, 0x20, 0x01 };
unsigned char *buffer = NULL;
unsigned char *ptr;
unsigned char thistime;
struct scatterlist *sg = NULL;
int totallen, len, result;
int sg_idx = 0, current_sg_offset = 0;
int transferred, rc;
// 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) {
rc = datafab_determine_lun(us, info);
if (rc != USB_STOR_TRANSPORT_GOOD)
return rc;
}
command[5] += (info->lun << 4);
// If we're using scatter-gather, we have to create a new
// buffer to read all of the data in first, since a
// scatter-gather buffer could in theory start in the middle
// of a page, which would be bad. A developer who wants a
// challenge might want to write a limited-buffer
// version of this code.
totallen = sectors * info->ssize;
do {
// loop, never allocate or transfer more than 64k at once (min(128k, 255*info->ssize) is the real limit)
len = min(totallen, 65536);
if (use_sg) {
sg = (struct scatterlist *) dest;
buffer = kmalloc(len, GFP_KERNEL);
if (buffer == NULL)
return USB_STOR_TRANSPORT_ERROR;
ptr = buffer;
} else {
ptr = dest;
}
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] |= (sector >> 24) & 0x0F;
// send the command
US_DEBUGP("datafab_read_data: sending following command\n");
datafab_dump_data(command, sizeof(command));
result = datafab_bulk_write(us, command, sizeof(command));
if (result != USB_STOR_TRANSPORT_GOOD) {
if (use_sg)
kfree(buffer);
return result;
}
// read the result
result = datafab_bulk_read(us, ptr, len);
if (result != USB_STOR_TRANSPORT_GOOD) {
if (use_sg)
kfree(buffer);
return result;
}
US_DEBUGP("datafab_read_data results: %d bytes\n", len);
// datafab_dump_data(ptr, len);
sectors -= thistime;
sector += thistime;
if (use_sg) {
transferred = 0;
while (sg_idx < use_sg && transferred < len) {
if (len - transferred >= sg[sg_idx].length - current_sg_offset) {
US_DEBUGP("datafab_read_data: adding %d bytes to %d byte sg buffer\n", sg[sg_idx].length - current_sg_offset, sg[sg_idx].length);
memcpy(sg[sg_idx].address + current_sg_offset,
buffer + transferred,
sg[sg_idx].length - current_sg_offset);
transferred += sg[sg_idx].length - current_sg_offset;
current_sg_offset = 0;
// on to the next sg buffer
++sg_idx;
} else {
US_DEBUGP("datafab_read_data: adding %d bytes to %d byte sg buffer\n", len - transferred, sg[sg_idx].length);
memcpy(sg[sg_idx].address + current_sg_offset,
buffer + transferred,
len - transferred);
current_sg_offset += len - transferred;
// this sg buffer is only partially full and we're out of data to copy in
break;
}
}
kfree(buffer);
} else {
dest += len;
}
totallen -= len;
} while (totallen > 0);
return USB_STOR_TRANSPORT_GOOD;
}
static int datafab_write_data(struct us_data *us,
struct datafab_info *info,
u32 sector,
u32 sectors,
unsigned char *src,
int use_sg)
{
unsigned char command[8] = { 0, 0, 0, 0, 0, 0xE0, 0x30, 0x02 };
unsigned char reply[2] = { 0, 0 };
unsigned char *buffer = NULL;
unsigned char *ptr;
unsigned char thistime;
struct scatterlist *sg = NULL;
int totallen, len, result;
int sg_idx = 0, current_sg_offset = 0;
int transferred, rc;
// 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) {
rc = datafab_determine_lun(us, info);
if (rc != USB_STOR_TRANSPORT_GOOD)
return rc;
}
command[5] += (info->lun << 4);
// If we're using scatter-gather, we have to create a new
// buffer to read all of the data in first, since a
// scatter-gather buffer could in theory start in the middle
// of a page, which would be bad. A developer who wants a
// challenge might want to write a limited-buffer
// version of this code.
totallen = sectors * info->ssize;
do {
// loop, never allocate or transfer more than 64k at once (min(128k, 255*info->ssize) is the real limit)
len = min(totallen, 65536);
if (use_sg) {
sg = (struct scatterlist *) src;
buffer = kmalloc(len, GFP_KERNEL);
if (buffer == NULL)
return USB_STOR_TRANSPORT_ERROR;
ptr = buffer;
memset(buffer, 0, len);
// copy the data from the sg bufs into the big contiguous buf
//
transferred = 0;
while (transferred < len) {
if (len - transferred >= sg[sg_idx].length - current_sg_offset) {
US_DEBUGP("datafab_write_data: getting %d bytes from %d byte sg buffer\n", sg[sg_idx].length - current_sg_offset, sg[sg_idx].length);
memcpy(ptr + transferred,
sg[sg_idx].address + current_sg_offset,
sg[sg_idx].length - current_sg_offset);
transferred += sg[sg_idx].length - current_sg_offset;
current_sg_offset = 0;
// on to the next sg buffer
++sg_idx;
} else {
US_DEBUGP("datafab_write_data: getting %d bytes from %d byte sg buffer\n", len - transferred, sg[sg_idx].length);
memcpy(ptr + transferred,
sg[sg_idx].address + current_sg_offset,
len - transferred);
current_sg_offset += len - transferred;
// we only copied part of this sg buffer
break;
}
}
} else {
ptr = src;
}
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] |= (sector >> 24) & 0x0F;
// send the command
US_DEBUGP("datafab_write_data: sending following command\n");
datafab_dump_data(command, sizeof(command));
result = datafab_bulk_write(us, command, sizeof(command));
if (result != USB_STOR_TRANSPORT_GOOD) {
if (use_sg)
kfree(buffer);
return result;
}
// send the data
result = datafab_bulk_write(us, ptr, len);
if (result != USB_STOR_TRANSPORT_GOOD) {
if (use_sg)
kfree(buffer);
return result;
}
// read the result
result = datafab_bulk_read(us, reply, sizeof(reply));
if (result != USB_STOR_TRANSPORT_GOOD) {
if (use_sg)
kfree(buffer);
return result;
}
if (reply[0] != 0x50 && reply[1] != 0) {
US_DEBUGP("datafab_write_data: Gah! write return code: %02x %02x\n", reply[0], reply[1]);
if (use_sg)
kfree(buffer);
return USB_STOR_TRANSPORT_ERROR;
}
sectors -= thistime;
sector += thistime;
if (use_sg) {
kfree(buffer);
} else {
src += len;
}
totallen -= len;
} while (totallen > 0);
return USB_STOR_TRANSPORT_GOOD;
}
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?
//
unsigned char command[8] = { 0, 1, 0, 0, 0, 0xa0, 0xec, 1 };
unsigned char buf[512];
int count = 0, rc;
if (!us || !info)
return USB_STOR_TRANSPORT_ERROR;
US_DEBUGP("datafab_determine_lun: locating...\n");
// we'll try 10 times before giving up...
//
while (count++ < 10) {
command[5] = 0xa0;
rc = datafab_bulk_write(us, command, 8);
if (rc != USB_STOR_TRANSPORT_GOOD)
return rc;
rc = datafab_bulk_read(us, buf, sizeof(buf));
if (rc == USB_STOR_TRANSPORT_GOOD) {
info->lun = 0;
return USB_STOR_TRANSPORT_GOOD;
}
command[5] = 0xb0;
rc = datafab_bulk_write(us, command, 8);
if (rc != USB_STOR_TRANSPORT_GOOD)
return rc;
rc = datafab_bulk_read(us, buf, sizeof(buf));
if (rc == USB_STOR_TRANSPORT_GOOD) {
info->lun = 1;
return USB_STOR_TRANSPORT_GOOD;
}
wait_ms(20);
}
return USB_STOR_TRANSPORT_FAILED;
}
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...
//
unsigned char command[8] = { 0, 1, 0, 0, 0, 0xa0, 0xec, 1 };
unsigned char reply[512];
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;
}
command[5] += (info->lun << 4);
rc = datafab_bulk_write(us, command, 8);
if (rc != USB_STOR_TRANSPORT_GOOD)
return rc;
// we'll go ahead and extract the media capacity while we're here...
//
rc = datafab_bulk_read(us, reply, sizeof(reply));
if (rc == USB_STOR_TRANSPORT_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]) );
}
return rc;
}
static int datafab_handle_mode_sense(struct us_data *us,
Scsi_Cmnd * srb,
unsigned char *ptr,
int sense_6)
{
unsigned char mode_param_header[8] = {
0, 0, 0, 0, 0, 0, 0, 0
};
unsigned char rw_err_page[12] = {
0x1, 0xA, 0x21, 1, 0, 0, 0, 0, 1, 0, 0, 0
};
unsigned char cache_page[12] = {
0x8, 0xA, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
unsigned char rbac_page[12] = {
0x1B, 0xA, 0, 0x81, 0, 0, 0, 0, 0, 0, 0, 0
};
unsigned char timer_page[8] = {
0x1C, 0x6, 0, 0, 0, 0
};
unsigned char pc, page_code;
unsigned short total_len = 0;
unsigned short param_len, i = 0;
// 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...
//
if (sense_6)
param_len = srb->cmnd[4];
else
param_len = ((u16) (srb->cmnd[7]) >> 8) | ((u16) (srb->cmnd[8]));
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;
}
mode_param_header[3] = 0x80; // write enable
switch (page_code) {
case 0x0:
// vendor-specific mode
return USB_STOR_TRANSPORT_ERROR;
case 0x1:
total_len = sizeof(rw_err_page);
mode_param_header[0] = total_len >> 8;
mode_param_header[1] = total_len & 0xFF;
mode_param_header[3] = 0x00; // WP enable: 0x80
memcpy(ptr, mode_param_header, sizeof(mode_param_header));
i += sizeof(mode_param_header);
memcpy(ptr + i, rw_err_page, sizeof(rw_err_page));
break;
case 0x8:
total_len = sizeof(cache_page);
mode_param_header[0] = total_len >> 8;
mode_param_header[1] = total_len & 0xFF;
mode_param_header[3] = 0x00; // WP enable: 0x80
memcpy(ptr, mode_param_header, sizeof(mode_param_header));
i += sizeof(mode_param_header);
memcpy(ptr + i, cache_page, sizeof(cache_page));
break;
case 0x1B:
total_len = sizeof(rbac_page);
mode_param_header[0] = total_len >> 8;
mode_param_header[1] = total_len & 0xFF;
mode_param_header[3] = 0x00; // WP enable: 0x80
memcpy(ptr, mode_param_header, sizeof(mode_param_header));
i += sizeof(mode_param_header);
memcpy(ptr + i, rbac_page, sizeof(rbac_page));
break;
case 0x1C:
total_len = sizeof(timer_page);
mode_param_header[0] = total_len >> 8;
mode_param_header[1] = total_len & 0xFF;
mode_param_header[3] = 0x00; // WP enable: 0x80
memcpy(ptr, mode_param_header, sizeof(mode_param_header));
i += sizeof(mode_param_header);
memcpy(ptr + i, timer_page, sizeof(timer_page));
break;
case 0x3F: // retrieve all pages
total_len = sizeof(timer_page) + sizeof(rbac_page) +
sizeof(cache_page) + sizeof(rw_err_page);
mode_param_header[0] = total_len >> 8;
mode_param_header[1] = total_len & 0xFF;
mode_param_header[3] = 0x00; // WP enable
memcpy(ptr, mode_param_header, sizeof(mode_param_header));
i += sizeof(mode_param_header);
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));
break;
}
return USB_STOR_TRANSPORT_GOOD;
}
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(Scsi_Cmnd * srb, struct us_data *us)
{
struct datafab_info *info;
int rc;
unsigned long block, blocks;
unsigned char *ptr = NULL;
unsigned char inquiry_reply[36] = {
0x00, 0x80, 0x00, 0x01, 0x1F, 0x00, 0x00, 0x00
};
if (!us->extra) {
us->extra = kmalloc(sizeof(struct datafab_info), GFP_KERNEL);
if (!us->extra) {
US_DEBUGP("datafab_transport: Gah! Can't allocate storage for Datafab info struct!\n");
return USB_STOR_TRANSPORT_ERROR;
}
memset(us->extra, 0, sizeof(struct datafab_info));
us->extra_destructor = datafab_info_destructor;
((struct datafab_info *)us->extra)->lun = -1;
}
info = (struct datafab_info *) (us->extra);
ptr = (unsigned char *) srb->request_buffer;
if (srb->cmnd[0] == INQUIRY) {
US_DEBUGP("datafab_transport: INQUIRY. Returning bogus response");
memset( inquiry_reply + 8, 0, 28 );
fill_inquiry_response(us, inquiry_reply, 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
//
ptr[0] = (info->sectors >> 24) & 0xFF;
ptr[1] = (info->sectors >> 16) & 0xFF;
ptr[2] = (info->sectors >> 8) & 0xFF;
ptr[3] = (info->sectors) & 0xFF;
ptr[4] = (info->ssize >> 24) & 0xFF;
ptr[5] = (info->ssize >> 16) & 0xFF;
ptr[6] = (info->ssize >> 8) & 0xFF;
ptr[7] = (info->ssize) & 0xFF;
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. Just set MODE_XLATE and
// let the usb storage code convert to READ_10/WRITE_10
//
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, ptr, srb->use_sg);
}
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, ptr, srb->use_sg);
}
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, ptr, srb->use_sg);
}
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, ptr, srb->use_sg);
}
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
//
ptr[0] = 0xF0;
ptr[2] = info->sense_key;
ptr[7] = 11;
ptr[12] = info->sense_asc;
ptr[13] = info->sense_ascq;
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, ptr, TRUE);
}
if (srb->cmnd[0] == MODE_SENSE_10) {
US_DEBUGP("datafab_transport: MODE_SENSE_10 detected\n");
return datafab_handle_mode_sense(us, srb, ptr, FALSE);
}
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;
}
US_DEBUGP("datafab_transport: Gah! Unknown command: %d (0x%x)\n", srb->cmnd[0], srb->cmnd[0]);
return USB_STOR_TRANSPORT_ERROR;
}
/* 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
#define min(a,b) (((a)<(b))?(a):(b)) // this is defined in tons of header files, i wish it had a standard single definition...
extern int datafab_transport(Scsi_Cmnd *srb, struct us_data *us);
struct datafab_info {
unsigned long sectors; // total sector count
unsigned long ssize; // sector size in bytes
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
/* Driver for USB Mass Storage compliant devices
* Debugging Functions Source Code File
*
* $Id: debug.c,v 1.4 2000/09/04 02:12:47 groovyjava Exp $
* $Id: debug.c,v 1.5 2001/06/27 23:20:45 mdharm Exp $
*
* Current development and maintenance by:
* (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
......@@ -302,12 +302,10 @@ void usb_stor_show_sense(
case 0x1C00: what="defect list not found"; break;
case 0x2400: what="invalid field in CDB"; break;
case 0x2703: what="associated write protect"; break;
case 0x2800: what="not ready to ready transition (media change?)";
break;
case 0x2800: what="not ready to ready transition"; break;
case 0x2903: what="bus device reset function occurred"; break;
case 0x2904: what="device internal reset"; break;
case 0x2B00: what="copy can't execute since host can't disconnect";
break;
case 0x2B00: what="copy can't execute / host can't disconnect"; break;
case 0x2C00: what="command sequence error"; break;
case 0x2C03: what="current program area is not empty"; break;
case 0x2C04: what="current program area is empty"; break;
......
/* Driver for Microtech DPCM-USB CompactFlash/SmartMedia reader
*
* $Id: dpcm.c,v 1.3 2000/08/25 00:13:51 mdharm Exp $
* $Id: dpcm.c,v 1.4 2001/06/11 02:54:25 mdharm Exp $
*
* DPCM driver v0.1:
*
......
/* Driver for Freecom USB/IDE adaptor
*
* $Id: freecom.c,v 1.14 2000/11/13 22:27:57 mdharm Exp $
* $Id: freecom.c,v 1.15 2001/06/27 23:50:28 mdharm Exp $
*
* Freecom v0.1:
*
......
/* Transport & Protocol Driver for In-System Design, Inc. ISD200 ASIC
*
* First release
*
* Current development and maintenance by:
* (c) 2000 In-System Design, Inc. (support@in-system.com)
*
* The ISD200 ASIC does not natively support ATA devices. The chip
* does implement an interface, the ATA Command Block (ATACB) which provides
* a means of passing ATA commands and ATA register accesses to a device.
*
* 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.
*
* History:
*
* 2001-02-24: Removed lots of duplicate code and simplified the structure.
* (bjorn@haxx.se)
*/
/* Include files */
#include "transport.h"
#include "protocol.h"
#include "usb.h"
#include "debug.h"
#include "scsiglue.h"
#include "isd200.h"
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/malloc.h>
#include <linux/hdreg.h>
#include <linux/ide.h>
/*
* Inquiry defines. Used to interpret data returned from target as result
* of inquiry command.
*
* DeviceType field
*/
#define DIRECT_ACCESS_DEVICE 0x00 /* disks */
/* Timeout defines (in Seconds) */
#define ISD200_ENUM_BSY_TIMEOUT 35
#define ISD200_ENUM_DETECT_TIMEOUT 30
#define ISD200_DEFAULT_TIMEOUT 30
/* device flags */
#define DF_ATA_DEVICE 0x0001
#define DF_MEDIA_STATUS_ENABLED 0x0002
#define DF_REMOVABLE_MEDIA 0x0004
/* capability bit definitions */
#define CAPABILITY_DMA 0x01
#define CAPABILITY_LBA 0x02
/* command_setX bit definitions */
#define COMMANDSET_REMOVABLE 0x02
#define COMMANDSET_MEDIA_STATUS 0x10
/* ATA Vendor Specific defines */
#define ATA_ADDRESS_DEVHEAD_STD 0xa0
#define ATA_ADDRESS_DEVHEAD_LBA_MODE 0x40
#define ATA_ADDRESS_DEVHEAD_SLAVE 0x10
/* Action Select bits */
#define ACTION_SELECT_0 0x01
#define ACTION_SELECT_1 0x02
#define ACTION_SELECT_2 0x04
#define ACTION_SELECT_3 0x08
#define ACTION_SELECT_4 0x10
#define ACTION_SELECT_5 0x20
#define ACTION_SELECT_6 0x40
#define ACTION_SELECT_7 0x80
/* ATA error definitions not in <linux/hdreg.h> */
#define ATA_ERROR_MEDIA_CHANGE 0x20
/* ATA command definitions not in <linux/hdreg.h> */
#define ATA_COMMAND_GET_MEDIA_STATUS 0xDA
#define ATA_COMMAND_MEDIA_EJECT 0xED
/* ATA drive control definitions */
#define ATA_DC_DISABLE_INTERRUPTS 0x02
#define ATA_DC_RESET_CONTROLLER 0x04
#define ATA_DC_REENABLE_CONTROLLER 0x00
/*
* General purpose return codes
*/
#define ISD200_ERROR -1
#define ISD200_GOOD 0
/*
* Transport return codes
*/
#define ISD200_TRANSPORT_GOOD 0 /* Transport good, command good */
#define ISD200_TRANSPORT_FAILED 1 /* Transport good, command failed */
#define ISD200_TRANSPORT_ERROR 2 /* Transport bad (i.e. device dead) */
#define ISD200_TRANSPORT_ABORTED 3 /* Transport aborted */
#define ISD200_TRANSPORT_SHORT 4 /* Transport short */
/* driver action codes */
#define ACTION_READ_STATUS 0
#define ACTION_RESET 1
#define ACTION_REENABLE 2
#define ACTION_SOFT_RESET 3
#define ACTION_ENUM 4
#define ACTION_IDENTIFY 5
/*
* ata_cdb struct
*/
union ata_cdb {
struct {
unsigned char SignatureByte0;
unsigned char SignatureByte1;
unsigned char ActionSelect;
unsigned char RegisterSelect;
unsigned char TransferBlockSize;
unsigned char WriteData3F6;
unsigned char WriteData1F1;
unsigned char WriteData1F2;
unsigned char WriteData1F3;
unsigned char WriteData1F4;
unsigned char WriteData1F5;
unsigned char WriteData1F6;
unsigned char WriteData1F7;
unsigned char Reserved[3];
} generic;
struct {
unsigned char SignatureByte0;
unsigned char SignatureByte1;
unsigned char ReadRegisterAccessBit : 1;
unsigned char NoDeviceSelectionBit : 1;
unsigned char NoBSYPollBit : 1;
unsigned char IgnorePhaseErrorBit : 1;
unsigned char IgnoreDeviceErrorBit : 1;
unsigned char Reserved0Bit : 3;
unsigned char SelectAlternateStatus : 1;
unsigned char SelectError : 1;
unsigned char SelectSectorCount : 1;
unsigned char SelectSectorNumber : 1;
unsigned char SelectCylinderLow : 1;
unsigned char SelectCylinderHigh : 1;
unsigned char SelectDeviceHead : 1;
unsigned char SelectStatus : 1;
unsigned char TransferBlockSize;
unsigned char AlternateStatusByte;
unsigned char ErrorByte;
unsigned char SectorCountByte;
unsigned char SectorNumberByte;
unsigned char CylinderLowByte;
unsigned char CylinderHighByte;
unsigned char DeviceHeadByte;
unsigned char StatusByte;
unsigned char Reserved[3];
} read;
struct {
unsigned char SignatureByte0;
unsigned char SignatureByte1;
unsigned char ReadRegisterAccessBit : 1;
unsigned char NoDeviceSelectionBit : 1;
unsigned char NoBSYPollBit : 1;
unsigned char IgnorePhaseErrorBit : 1;
unsigned char IgnoreDeviceErrorBit : 1;
unsigned char Reserved0Bit : 3;
unsigned char SelectDeviceControl : 1;
unsigned char SelectFeatures : 1;
unsigned char SelectSectorCount : 1;
unsigned char SelectSectorNumber : 1;
unsigned char SelectCylinderLow : 1;
unsigned char SelectCylinderHigh : 1;
unsigned char SelectDeviceHead : 1;
unsigned char SelectCommand : 1;
unsigned char TransferBlockSize;
unsigned char DeviceControlByte;
unsigned char FeaturesByte;
unsigned char SectorCountByte;
unsigned char SectorNumberByte;
unsigned char CylinderLowByte;
unsigned char CylinderHighByte;
unsigned char DeviceHeadByte;
unsigned char CommandByte;
unsigned char Reserved[3];
} write;
};
/*
* Inquiry data structure. This is the data returned from the target
* after it receives an inquiry.
*
* This structure may be extended by the number of bytes specified
* in the field AdditionalLength. The defined size constant only
* includes fields through ProductRevisionLevel.
*/
struct inquiry_data {
unsigned char DeviceType : 5;
unsigned char DeviceTypeQualifier : 3;
unsigned char DeviceTypeModifier : 7;
unsigned char RemovableMedia : 1;
unsigned char Versions;
unsigned char ResponseDataFormat : 4;
unsigned char HiSupport : 1;
unsigned char NormACA : 1;
unsigned char ReservedBit : 1;
unsigned char AERC : 1;
unsigned char AdditionalLength;
unsigned char Reserved[2];
unsigned char SoftReset : 1;
unsigned char CommandQueue : 1;
unsigned char Reserved2 : 1;
unsigned char LinkedCommands : 1;
unsigned char Synchronous : 1;
unsigned char Wide16Bit : 1;
unsigned char Wide32Bit : 1;
unsigned char RelativeAddressing : 1;
unsigned char VendorId[8];
unsigned char ProductId[16];
unsigned char ProductRevisionLevel[4];
unsigned char VendorSpecific[20];
unsigned char Reserved3[40];
} __attribute__ ((packed));
/*
* INQUIRY data buffer size
*/
#define INQUIRYDATABUFFERSIZE 36
/*
* ISD200 CONFIG data struct
*/
struct isd200_config {
unsigned char EventNotification;
unsigned char ExternalClock;
unsigned char ATAInitTimeout;
unsigned char ATATiming : 4;
unsigned char ATAPIReset : 1;
unsigned char MasterSlaveSelection : 1;
unsigned char ATAPICommandBlockSize : 2;
unsigned char ATAMajorCommand;
unsigned char ATAMinorCommand;
unsigned char LastLUNIdentifier : 3;
unsigned char DescriptOverride : 1;
unsigned char ATA3StateSuspend : 1;
unsigned char SkipDeviceBoot : 1;
unsigned char ConfigDescriptor2 : 1;
unsigned char InitStatus : 1;
unsigned char SRSTEnable : 1;
unsigned char Reserved0 : 7;
};
/*
* ISD200 driver information struct
*/
struct isd200_info {
struct inquiry_data InquiryData;
struct hd_driveid drive;
struct isd200_config ConfigData;
unsigned char ATARegs[8];
unsigned char DeviceHead;
unsigned char DeviceFlags;
/* maximum number of LUNs supported */
unsigned char MaxLUNs;
};
/*
* Read Capacity Data - returned in Big Endian format
*/
struct read_capacity_data {
unsigned long LogicalBlockAddress;
unsigned long BytesPerBlock;
};
/*
* Read Block Limits Data - returned in Big Endian format
* This structure returns the maximum and minimum block
* size for a TAPE device.
*/
struct read_block_limits {
unsigned char Reserved;
unsigned char BlockMaximumSize[3];
unsigned char BlockMinimumSize[2];
};
/*
* Sense Data Format
*/
struct sense_data {
unsigned char ErrorCode:7;
unsigned char Valid:1;
unsigned char SegmentNumber;
unsigned char SenseKey:4;
unsigned char Reserved:1;
unsigned char IncorrectLength:1;
unsigned char EndOfMedia:1;
unsigned char FileMark:1;
unsigned char Information[4];
unsigned char AdditionalSenseLength;
unsigned char CommandSpecificInformation[4];
unsigned char AdditionalSenseCode;
unsigned char AdditionalSenseCodeQualifier;
unsigned char FieldReplaceableUnitCode;
unsigned char SenseKeySpecific[3];
} __attribute__ ((packed));
/*
* Default request sense buffer size
*/
#define SENSE_BUFFER_SIZE 18
/***********************************************************************
* Helper routines
***********************************************************************/
/**************************************************************************
* isd200_build_sense
*
* Builds an artificial sense buffer to report the results of a
* failed command.
*
* RETURNS:
* void
*/
void isd200_build_sense(struct us_data *us, Scsi_Cmnd *srb)
{
struct isd200_info *info = (struct isd200_info *)us->extra;
struct sense_data *buf = (struct sense_data *) &srb->sense_buffer[0];
unsigned char error = info->ATARegs[IDE_ERROR_OFFSET];
if(error & ATA_ERROR_MEDIA_CHANGE) {
buf->ErrorCode = 0x70;
buf->Valid = 1;
buf->AdditionalSenseLength = 0xb;
buf->SenseKey = UNIT_ATTENTION;
buf->AdditionalSenseCode = 0;
buf->AdditionalSenseCodeQualifier = 0;
} else if(error & MCR_ERR) {
buf->ErrorCode = 0x70;
buf->Valid = 1;
buf->AdditionalSenseLength = 0xb;
buf->SenseKey = UNIT_ATTENTION;
buf->AdditionalSenseCode = 0;
buf->AdditionalSenseCodeQualifier = 0;
} else if(error & TRK0_ERR) {
buf->ErrorCode = 0x70;
buf->Valid = 1;
buf->AdditionalSenseLength = 0xb;
buf->SenseKey = NOT_READY;
buf->AdditionalSenseCode = 0;
buf->AdditionalSenseCodeQualifier = 0;
} else if(error & ECC_ERR) {
buf->ErrorCode = 0x70;
buf->Valid = 1;
buf->AdditionalSenseLength = 0xb;
buf->SenseKey = DATA_PROTECT;
buf->AdditionalSenseCode = 0;
buf->AdditionalSenseCodeQualifier = 0;
} else {
buf->ErrorCode = 0;
buf->Valid = 0;
buf->AdditionalSenseLength = 0;
buf->SenseKey = 0;
buf->AdditionalSenseCode = 0;
buf->AdditionalSenseCodeQualifier = 0;
}
}
/***********************************************************************
* Data transfer routines
***********************************************************************/
/**************************************************************************
* Transfer one SCSI scatter-gather buffer via bulk transfer
*
* Note that this function is necessary because we want the ability to
* use scatter-gather memory. Good performance is achieved by a combination
* of scatter-gather and clustering (which makes each chunk bigger).
*
* Note that the lower layer will always retry when a NAK occurs, up to the
* timeout limit. Thus we don't have to worry about it for individual
* packets.
*/
static int isd200_transfer_partial( struct us_data *us,
unsigned char dataDirection,
char *buf, int length )
{
int result;
int partial;
int pipe;
/* calculate the appropriate pipe information */
if (dataDirection == SCSI_DATA_READ)
pipe = usb_rcvbulkpipe(us->pusb_dev, us->ep_in);
else
pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out);
/* transfer the data */
US_DEBUGP("isd200_transfer_partial(): xfer %d bytes\n", length);
result = usb_stor_bulk_msg(us, buf, pipe, length, &partial);
US_DEBUGP("usb_stor_bulk_msg() returned %d xferred %d/%d\n",
result, partial, length);
/* if we stall, we need to clear it before we go on */
if (result == -EPIPE) {
US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
usb_stor_clear_halt(us->pusb_dev, pipe);
}
/* did we send all the data? */
if (partial == length) {
US_DEBUGP("isd200_transfer_partial(): transfer complete\n");
return ISD200_TRANSPORT_GOOD;
}
/* uh oh... we have an error code, so something went wrong. */
if (result) {
/* NAK - that means we've retried a few times already */
if (result == -ETIMEDOUT) {
US_DEBUGP("isd200_transfer_partial(): device NAKed\n");
return ISD200_TRANSPORT_FAILED;
}
/* -ENOENT -- we canceled this transfer */
if (result == -ENOENT) {
US_DEBUGP("isd200_transfer_partial(): transfer aborted\n");
return ISD200_TRANSPORT_ABORTED;
}
/* the catch-all case */
US_DEBUGP("isd200_transfer_partial(): unknown error\n");
return ISD200_TRANSPORT_FAILED;
}
/* no error code, so we must have transferred some data,
* just not all of it */
return ISD200_TRANSPORT_SHORT;
}
/**************************************************************************
* Transfer an entire SCSI command's worth of data payload over the bulk
* pipe.
*
* Note that this uses us_transfer_partial to achieve it's goals -- this
* function simply determines if we're going to use scatter-gather or not,
* and acts appropriately. For now, it also re-interprets the error codes.
*/
static void isd200_transfer( struct us_data *us, Scsi_Cmnd *srb )
{
int i;
int result = -1;
struct scatterlist *sg;
unsigned int total_transferred = 0;
unsigned int transfer_amount;
/* calculate how much we want to transfer */
int dir = srb->sc_data_direction;
srb->sc_data_direction = SCSI_DATA_WRITE;
transfer_amount = usb_stor_transfer_length(srb);
srb->sc_data_direction = dir;
/* was someone foolish enough to request more data than available
* buffer space? */
if (transfer_amount > srb->request_bufflen)
transfer_amount = srb->request_bufflen;
/* are we scatter-gathering? */
if (srb->use_sg) {
/* loop over all the scatter gather structures and
* make the appropriate requests for each, until done
*/
sg = (struct scatterlist *) srb->request_buffer;
for (i = 0; i < srb->use_sg; i++) {
/* transfer the lesser of the next buffer or the
* remaining data */
if (transfer_amount - total_transferred >=
sg[i].length) {
result = isd200_transfer_partial(us,
srb->sc_data_direction,
sg[i].address,
sg[i].length);
total_transferred += sg[i].length;
} else
result = isd200_transfer_partial(us,
srb->sc_data_direction,
sg[i].address,
transfer_amount - total_transferred);
/* if we get an error, end the loop here */
if (result)
break;
}
}
else
/* no scatter-gather, just make the request */
result = isd200_transfer_partial(us,
srb->sc_data_direction,
srb->request_buffer,
transfer_amount);
/* return the result in the data structure itself */
srb->result = result;
}
/***********************************************************************
* Transport routines
***********************************************************************/
/**************************************************************************
* ISD200 Bulk Transport
*
* Note: This routine was copied from the usb_stor_Bulk_transport routine
* located in the transport.c source file. The scsi command is limited to
* only 12 bytes while the CDB for the ISD200 must be 16 bytes.
*/
int isd200_Bulk_transport( struct us_data *us, Scsi_Cmnd *srb,
union ata_cdb *AtaCdb, unsigned char AtaCdbLength )
{
struct bulk_cb_wrap bcb;
struct bulk_cs_wrap bcs;
int result;
int pipe;
int partial;
unsigned int transfer_amount;
int dir = srb->sc_data_direction;
srb->sc_data_direction = SCSI_DATA_WRITE;
transfer_amount = usb_stor_transfer_length(srb);
srb->sc_data_direction = dir;
/* set up the command wrapper */
bcb.Signature = cpu_to_le32(US_BULK_CB_SIGN);
bcb.DataTransferLength = cpu_to_le32(transfer_amount);
bcb.Flags = srb->sc_data_direction == SCSI_DATA_READ ? 1 << 7 : 0;
bcb.Tag = srb->serial_number;
bcb.Lun = srb->cmnd[1] >> 5;
if (us->flags & US_FL_SCM_MULT_TARG)
bcb.Lun |= srb->target << 4;
bcb.Length = AtaCdbLength;
/* construct the pipe handle */
pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out);
/* copy the command payload */
memset(bcb.CDB, 0, sizeof(bcb.CDB));
memcpy(bcb.CDB, AtaCdb, bcb.Length);
/* send it to out endpoint */
US_DEBUGP("Bulk command S 0x%x T 0x%x Trg %d LUN %d L %d F %d CL %d\n",
le32_to_cpu(bcb.Signature), bcb.Tag,
(bcb.Lun >> 4), (bcb.Lun & 0xFF),
bcb.DataTransferLength, bcb.Flags, bcb.Length);
result = usb_stor_bulk_msg(us, &bcb, pipe, US_BULK_CB_WRAP_LEN,
&partial);
US_DEBUGP("Bulk command transfer result=%d\n", result);
if (result == -ENOENT)
return ISD200_TRANSPORT_ABORTED;
else if (result == -EPIPE) {
/* if we stall, we need to clear it before we go on */
US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
usb_stor_clear_halt(us->pusb_dev, pipe);
} else if (result)
return ISD200_TRANSPORT_ERROR;
/* if the command transfered well, then we go to the data stage */
if (!result && bcb.DataTransferLength) {
isd200_transfer(us, srb);
US_DEBUGP("Bulk data transfer result 0x%x\n", srb->result);
if (srb->result == ISD200_TRANSPORT_ABORTED)
return ISD200_TRANSPORT_ABORTED;
}
/* See flow chart on pg 15 of the Bulk Only Transport spec for
* an explanation of how this code works.
*/
/* construct the pipe handle */
pipe = usb_rcvbulkpipe(us->pusb_dev, us->ep_in);
/* get CSW for device status */
US_DEBUGP("Attempting to get CSW...\n");
result = usb_stor_bulk_msg(us, &bcs, pipe, US_BULK_CS_WRAP_LEN,
&partial);
if (result == -ENOENT)
return ISD200_TRANSPORT_ABORTED;
/* did the attempt to read the CSW fail? */
if (result == -EPIPE) {
US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
usb_stor_clear_halt(us->pusb_dev, pipe);
/* get the status again */
US_DEBUGP("Attempting to get CSW (2nd try)...\n");
result = usb_stor_bulk_msg(us, &bcs, pipe,
US_BULK_CS_WRAP_LEN, &partial);
/* if the command was aborted, indicate that */
if (result == -ENOENT)
return ISD200_TRANSPORT_ABORTED;
/* if it fails again, we need a reset and return an error*/
if (result == -EPIPE) {
US_DEBUGP("clearing halt for pipe 0x%x\n", pipe);
usb_stor_clear_halt(us->pusb_dev, pipe);
return ISD200_TRANSPORT_ERROR;
}
}
/* if we still have a failure at this point, we're in trouble */
US_DEBUGP("Bulk status result = %d\n", result);
if (result)
return ISD200_TRANSPORT_ERROR;
/* check bulk status */
US_DEBUGP("Bulk status Sig 0x%x T 0x%x R %d Stat 0x%x\n",
le32_to_cpu(bcs.Signature), bcs.Tag,
bcs.Residue, bcs.Status);
if (bcs.Signature != cpu_to_le32(US_BULK_CS_SIGN) ||
bcs.Tag != bcb.Tag ||
bcs.Status > US_BULK_STAT_PHASE || partial != 13) {
US_DEBUGP("Bulk logical error\n");
return ISD200_TRANSPORT_ERROR;
}
/* based on the status code, we report good or bad */
switch (bcs.Status) {
case US_BULK_STAT_OK:
/* command good -- note that we could be short on data */
return ISD200_TRANSPORT_GOOD;
case US_BULK_STAT_FAIL:
/* command failed */
return ISD200_TRANSPORT_FAILED;
case US_BULK_STAT_PHASE:
/* phase error */
usb_stor_Bulk_reset(us);
return ISD200_TRANSPORT_ERROR;
}
/* we should never get here, but if we do, we're in trouble */
return ISD200_TRANSPORT_ERROR;
}
/**************************************************************************
* isd200_action
*
* Routine for sending commands to the isd200
*
* RETURNS:
* ISD status code
*/
static int isd200_action( struct us_data *us, int action,
void* pointer, int value )
{
union ata_cdb ata;
struct scsi_cmnd srb;
struct isd200_info *info = (struct isd200_info *)us->extra;
int status;
memset(&ata, 0, sizeof(ata));
memset(&srb, 0, sizeof(srb));
ata.generic.SignatureByte0 = info->ConfigData.ATAMajorCommand;
ata.generic.SignatureByte1 = info->ConfigData.ATAMinorCommand;
ata.generic.TransferBlockSize = 1;
switch ( action ) {
case ACTION_READ_STATUS:
US_DEBUGP(" isd200_action(READ_STATUS)\n");
ata.generic.ActionSelect = ACTION_SELECT_0|ACTION_SELECT_2;
ata.read.SelectStatus = 1;
ata.read.SelectError = 1;
ata.read.SelectCylinderHigh = 1;
ata.read.SelectCylinderLow = 1;
srb.sc_data_direction = SCSI_DATA_READ;
srb.request_buffer = pointer;
srb.request_bufflen = value;
break;
case ACTION_ENUM:
US_DEBUGP(" isd200_action(ENUM,0x%02x)\n",value);
ata.generic.ActionSelect = ACTION_SELECT_1|ACTION_SELECT_2|
ACTION_SELECT_3|ACTION_SELECT_4|
ACTION_SELECT_5;
ata.write.SelectDeviceHead = 1;
ata.write.DeviceHeadByte = value;
srb.sc_data_direction = SCSI_DATA_NONE;
break;
case ACTION_RESET:
US_DEBUGP(" isd200_action(RESET)\n");
ata.generic.ActionSelect = ACTION_SELECT_1|ACTION_SELECT_2|
ACTION_SELECT_3|ACTION_SELECT_4;
ata.write.SelectDeviceControl = 1;
ata.write.DeviceControlByte = ATA_DC_RESET_CONTROLLER;
srb.sc_data_direction = SCSI_DATA_NONE;
break;
case ACTION_REENABLE:
US_DEBUGP(" isd200_action(REENABLE)\n");
ata.generic.ActionSelect = ACTION_SELECT_1|ACTION_SELECT_2|
ACTION_SELECT_3|ACTION_SELECT_4;
ata.write.SelectDeviceControl = 1;
ata.write.DeviceControlByte = ATA_DC_REENABLE_CONTROLLER;
srb.sc_data_direction = SCSI_DATA_NONE;
break;
case ACTION_SOFT_RESET:
US_DEBUGP(" isd200_action(SOFT_RESET)\n");
ata.generic.ActionSelect = ACTION_SELECT_1|ACTION_SELECT_5;
ata.write.SelectDeviceHead = 1;
ata.write.DeviceHeadByte = info->DeviceHead;
ata.write.SelectCommand = 1;
ata.write.CommandByte = WIN_SRST;
srb.sc_data_direction = SCSI_DATA_NONE;
break;
case ACTION_IDENTIFY:
US_DEBUGP(" isd200_action(IDENTIFY)\n");
ata.write.SelectCommand = 1;
ata.write.CommandByte = WIN_IDENTIFY;
srb.sc_data_direction = SCSI_DATA_READ;
srb.request_buffer = (void *)&info->drive;
srb.request_bufflen = sizeof(struct hd_driveid);
break;
default:
US_DEBUGP("Error: Undefined action %d\n",action);
break;
}
status = isd200_Bulk_transport(us, &srb, &ata, sizeof(ata.generic));
if (status != ISD200_TRANSPORT_GOOD) {
US_DEBUGP(" isd200_action(0x%02x) error: %d\n",action,status);
status = ISD200_ERROR;
/* need to reset device here */
}
return status;
}
/**************************************************************************
* isd200_read_regs
*
* Read ATA Registers
*
* RETURNS:
* ISD status code
*/
int isd200_read_regs( struct us_data *us )
{
struct isd200_info *info = (struct isd200_info *)us->extra;
int retStatus = ISD200_GOOD;
int transferStatus;
US_DEBUGP("Entering isd200_IssueATAReadRegs\n");
transferStatus = isd200_action( us, ACTION_READ_STATUS,
info->ATARegs, sizeof(info->ATARegs) );
if (transferStatus != ISD200_TRANSPORT_GOOD) {
US_DEBUGP(" Error reading ATA registers\n");
retStatus = ISD200_ERROR;
} else {
US_DEBUGP(" Got ATA Register[IDE_ERROR_OFFSET] = 0x%x\n",
info->ATARegs[IDE_ERROR_OFFSET]);
}
return retStatus;
}
/**************************************************************************
* Invoke the transport and basic error-handling/recovery methods
*
* This is used by the protocol layers to actually send the message to
* the device and recieve the response.
*/
void isd200_invoke_transport( struct us_data *us,
Scsi_Cmnd *srb,
union ata_cdb *ataCdb )
{
int need_auto_sense = 0;
int transferStatus;
/* send the command to the transport layer */
transferStatus = isd200_Bulk_transport(us, srb, ataCdb,
sizeof(ataCdb->generic));
switch (transferStatus) {
case ISD200_TRANSPORT_GOOD:
/* Indicate a good result */
srb->result = GOOD;
break;
case ISD200_TRANSPORT_ABORTED:
/* if the command gets aborted by the higher layers, we need to
* short-circuit all other processing
*/
US_DEBUGP("-- transport indicates command was aborted\n");
srb->result = DID_ABORT << 16;
break;
case ISD200_TRANSPORT_FAILED:
US_DEBUGP("-- transport indicates command failure\n");
need_auto_sense = 1;
break;
case ISD200_TRANSPORT_ERROR:
US_DEBUGP("-- transport indicates transport failure\n");
srb->result = DID_ERROR << 16;
break;
case ISD200_TRANSPORT_SHORT:
if (!((srb->cmnd[0] == REQUEST_SENSE) ||
(srb->cmnd[0] == INQUIRY) ||
(srb->cmnd[0] == MODE_SENSE) ||
(srb->cmnd[0] == LOG_SENSE) ||
(srb->cmnd[0] == MODE_SENSE_10))) {
US_DEBUGP("-- unexpectedly short transfer\n");
need_auto_sense = 1;
}
break;
default:
US_DEBUGP("-- transport indicates unknown failure\n");
srb->result = DID_ERROR << 16;
}
if (need_auto_sense)
if (isd200_read_regs(us) == ISD200_GOOD)
isd200_build_sense(us, srb);
/* Regardless of auto-sense, if we _know_ we have an error
* condition, show that in the result code
*/
if (transferStatus == ISD200_TRANSPORT_FAILED)
srb->result = CHECK_CONDITION;
}
/**************************************************************************
* isd200_write_config
*
* Write the ISD200 Configuraton data
*
* RETURNS:
* ISD status code
*/
int isd200_write_config( struct us_data *us )
{
struct isd200_info *info = (struct isd200_info *)us->extra;
int retStatus = ISD200_GOOD;
int result;
US_DEBUGP("Entering isd200_write_config\n");
US_DEBUGP(" Writing the following ISD200 Config Data:\n");
US_DEBUGP(" Event Notification: 0x%x\n", info->ConfigData.EventNotification);
US_DEBUGP(" External Clock: 0x%x\n", info->ConfigData.ExternalClock);
US_DEBUGP(" ATA Init Timeout: 0x%x\n", info->ConfigData.ATAInitTimeout);
US_DEBUGP(" ATAPI Command Block Size: 0x%x\n", info->ConfigData.ATAPICommandBlockSize);
US_DEBUGP(" Master/Slave Selection: 0x%x\n", info->ConfigData.MasterSlaveSelection);
US_DEBUGP(" ATAPI Reset: 0x%x\n", info->ConfigData.ATAPIReset);
US_DEBUGP(" ATA Timing: 0x%x\n", info->ConfigData.ATATiming);
US_DEBUGP(" ATA Major Command: 0x%x\n", info->ConfigData.ATAMajorCommand);
US_DEBUGP(" ATA Minor Command: 0x%x\n", info->ConfigData.ATAMinorCommand);
US_DEBUGP(" Init Status: 0x%x\n", info->ConfigData.InitStatus);
US_DEBUGP(" Config Descriptor 2: 0x%x\n", info->ConfigData.ConfigDescriptor2);
US_DEBUGP(" Skip Device Boot: 0x%x\n", info->ConfigData.SkipDeviceBoot);
US_DEBUGP(" ATA 3 State Supsend: 0x%x\n", info->ConfigData.ATA3StateSuspend);
US_DEBUGP(" Descriptor Override: 0x%x\n", info->ConfigData.DescriptOverride);
US_DEBUGP(" Last LUN Identifier: 0x%x\n", info->ConfigData.LastLUNIdentifier);
US_DEBUGP(" SRST Enable: 0x%x\n", info->ConfigData.SRSTEnable);
/* let's send the command via the control pipe */
result = usb_stor_control_msg(
us,
usb_sndctrlpipe(us->pusb_dev,0),
0x01,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
0x0000,
0x0002,
(void *) &info->ConfigData,
sizeof(info->ConfigData));
if (result >= 0) {
US_DEBUGP(" ISD200 Config Data was written successfully\n");
} else {
US_DEBUGP(" Request to write ISD200 Config Data failed!\n");
/* STALL must be cleared when they are detected */
if (result == -EPIPE) {
US_DEBUGP("-- Stall on control pipe. Clearing\n");
result = usb_stor_clear_halt(us->pusb_dev,
usb_sndctrlpipe(us->pusb_dev, 0));
US_DEBUGP("-- usb_stor_clear_halt() returns %d\n", result);
}
retStatus = ISD200_ERROR;
}
US_DEBUGP("Leaving isd200_write_config %08X\n", retStatus);
return retStatus;
}
/**************************************************************************
* isd200_read_config
*
* Reads the ISD200 Configuraton data
*
* RETURNS:
* ISD status code
*/
int isd200_read_config( struct us_data *us )
{
struct isd200_info *info = (struct isd200_info *)us->extra;
int retStatus = ISD200_GOOD;
int result;
US_DEBUGP("Entering isd200_read_config\n");
/* read the configuration information from ISD200. Use this to */
/* determine what the special ATA CDB bytes are. */
result = usb_stor_control_msg(
us,
usb_rcvctrlpipe(us->pusb_dev,0),
0x02,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
0x0000,
0x0002,
(void *) &info->ConfigData,
sizeof(info->ConfigData));
if (result >= 0) {
US_DEBUGP(" Retrieved the following ISD200 Config Data:\n");
US_DEBUGP(" Event Notification: 0x%x\n", info->ConfigData.EventNotification);
US_DEBUGP(" External Clock: 0x%x\n", info->ConfigData.ExternalClock);
US_DEBUGP(" ATA Init Timeout: 0x%x\n", info->ConfigData.ATAInitTimeout);
US_DEBUGP(" ATAPI Command Block Size: 0x%x\n", info->ConfigData.ATAPICommandBlockSize);
US_DEBUGP(" Master/Slave Selection: 0x%x\n", info->ConfigData.MasterSlaveSelection);
US_DEBUGP(" ATAPI Reset: 0x%x\n", info->ConfigData.ATAPIReset);
US_DEBUGP(" ATA Timing: 0x%x\n", info->ConfigData.ATATiming);
US_DEBUGP(" ATA Major Command: 0x%x\n", info->ConfigData.ATAMajorCommand);
US_DEBUGP(" ATA Minor Command: 0x%x\n", info->ConfigData.ATAMinorCommand);
US_DEBUGP(" Init Status: 0x%x\n", info->ConfigData.InitStatus);
US_DEBUGP(" Config Descriptor 2: 0x%x\n", info->ConfigData.ConfigDescriptor2);
US_DEBUGP(" Skip Device Boot: 0x%x\n", info->ConfigData.SkipDeviceBoot);
US_DEBUGP(" ATA 3 State Supsend: 0x%x\n", info->ConfigData.ATA3StateSuspend);
US_DEBUGP(" Descriptor Override: 0x%x\n", info->ConfigData.DescriptOverride);
US_DEBUGP(" Last LUN Identifier: 0x%x\n", info->ConfigData.LastLUNIdentifier);
US_DEBUGP(" SRST Enable: 0x%x\n", info->ConfigData.SRSTEnable);
} else {
US_DEBUGP(" Request to get ISD200 Config Data failed!\n");
/* STALL must be cleared when they are detected */
if (result == -EPIPE) {
US_DEBUGP("-- Stall on control pipe. Clearing\n");
result = usb_stor_clear_halt(us->pusb_dev,
usb_sndctrlpipe(us->pusb_dev, 0));
US_DEBUGP("-- usb_stor_clear_halt() returns %d\n", result);
}
retStatus = ISD200_ERROR;
}
US_DEBUGP("Leaving isd200_read_config %08X\n", retStatus);
return retStatus;
}
/**************************************************************************
* isd200_atapi_soft_reset
*
* Perform an Atapi Soft Reset on the device
*
* RETURNS:
* NT status code
*/
int isd200_atapi_soft_reset( struct us_data *us )
{
int retStatus = ISD200_GOOD;
int transferStatus;
US_DEBUGP("Entering isd200_atapi_soft_reset\n");
transferStatus = isd200_action( us, ACTION_SOFT_RESET, NULL, 0 );
if (transferStatus != ISD200_TRANSPORT_GOOD) {
US_DEBUGP(" Error issuing Atapi Soft Reset\n");
retStatus = ISD200_ERROR;
}
US_DEBUGP("Leaving isd200_atapi_soft_reset %08X\n", retStatus);
return retStatus;
}
/**************************************************************************
* isd200_srst
*
* Perform an SRST on the device
*
* RETURNS:
* ISD status code
*/
int isd200_srst( struct us_data *us )
{
int retStatus = ISD200_GOOD;
int transferStatus;
US_DEBUGP("Entering isd200_SRST\n");
transferStatus = isd200_action( us, ACTION_RESET, NULL, 0 );
/* check to see if this request failed */
if (transferStatus != ISD200_TRANSPORT_GOOD) {
US_DEBUGP(" Error issuing SRST\n");
retStatus = ISD200_ERROR;
} else {
/* delay 10ms to give the drive a chance to see it */
wait_ms(10);
transferStatus = isd200_action( us, ACTION_REENABLE, NULL, 0 );
if (transferStatus != ISD200_TRANSPORT_GOOD) {
US_DEBUGP(" Error taking drive out of reset\n");
retStatus = ISD200_ERROR;
} else {
/* delay 50ms to give the drive a chance to recover after SRST */
wait_ms(50);
}
}
US_DEBUGP("Leaving isd200_srst %08X\n", retStatus);
return retStatus;
}
/**************************************************************************
* isd200_try_enum
*
* Helper function for isd200_manual_enum(). Does ENUM and READ_STATUS
* and tries to analyze the status registers
*
* RETURNS:
* ISD status code
*/
static int isd200_try_enum(struct us_data *us, unsigned char master_slave,
int detect )
{
int status = ISD200_GOOD;
unsigned char regs[8];
unsigned long endTime;
struct isd200_info *info = (struct isd200_info *)us->extra;
int recheckAsMaster = FALSE;
if ( detect )
endTime = jiffies + ISD200_ENUM_DETECT_TIMEOUT * HZ;
else
endTime = jiffies + ISD200_ENUM_BSY_TIMEOUT * HZ;
/* loop until we detect !BSY or timeout */
while(TRUE) {
char* mstr = master_slave == ATA_ADDRESS_DEVHEAD_STD ?
"Master" : "Slave";
status = isd200_action( us, ACTION_ENUM, NULL, master_slave );
if ( status != ISD200_GOOD )
break;
status = isd200_action( us, ACTION_READ_STATUS,
regs, sizeof(regs) );
if ( status != ISD200_GOOD )
break;
if (!detect) {
if (regs[IDE_STATUS_OFFSET] & BUSY_STAT ) {
US_DEBUGP(" %s status is still BSY, try again...\n",mstr);
} else {
US_DEBUGP(" %s status !BSY, continue with next operation\n",mstr);
break;
}
}
/* check for BUSY_STAT and */
/* WRERR_STAT (workaround ATA Zip drive) and */
/* ERR_STAT (workaround for Archos CD-ROM) */
else if (regs[IDE_STATUS_OFFSET] &
(BUSY_STAT | WRERR_STAT | ERR_STAT )) {
US_DEBUGP(" Status indicates it is not ready, try again...\n");
}
/* check for DRDY, ATA devices set DRDY after SRST */
else if (regs[IDE_STATUS_OFFSET] & READY_STAT) {
US_DEBUGP(" Identified ATA device\n");
info->DeviceFlags |= DF_ATA_DEVICE;
info->DeviceHead = master_slave;
break;
}
/* check Cylinder High/Low to
determine if it is an ATAPI device
*/
else if ((regs[IDE_HCYL_OFFSET] == 0xEB) &&
(regs[IDE_LCYL_OFFSET] == 0x14)) {
/* It seems that the RICOH
MP6200A CD/RW drive will
report itself okay as a
slave when it is really a
master. So this check again
as a master device just to
make sure it doesn't report
itself okay as a master also
*/
if ((master_slave & ATA_ADDRESS_DEVHEAD_SLAVE) &&
(recheckAsMaster == FALSE)) {
US_DEBUGP(" Identified ATAPI device as slave. Rechecking again as master\n");
recheckAsMaster = TRUE;
master_slave = ATA_ADDRESS_DEVHEAD_STD;
} else {
US_DEBUGP(" Identified ATAPI device\n");
info->DeviceHead = master_slave;
status = isd200_atapi_soft_reset(us);
break;
}
} else {
US_DEBUGP(" Not ATA, not ATAPI. Weird.\n");
}
/* check for timeout on this request */
if (jiffies >= endTime) {
if (!detect)
US_DEBUGP(" BSY check timeout, just continue with next operation...\n");
else
US_DEBUGP(" Device detect timeout!\n");
break;
}
}
return status;
}
/**************************************************************************
* isd200_manual_enum
*
* Determines if the drive attached is an ATA or ATAPI and if it is a
* master or slave.
*
* RETURNS:
* ISD status code
*/
int isd200_manual_enum(struct us_data *us)
{
struct isd200_info *info = (struct isd200_info *)us->extra;
int retStatus = ISD200_GOOD;
US_DEBUGP("Entering isd200_manual_enum\n");
retStatus = isd200_read_config(us);
if (retStatus == ISD200_GOOD) {
int isslave;
/* master or slave? */
retStatus = isd200_try_enum( us, ATA_ADDRESS_DEVHEAD_STD, FALSE );
if (retStatus == ISD200_GOOD)
retStatus = isd200_try_enum( us, ATA_ADDRESS_DEVHEAD_SLAVE, FALSE );
if (retStatus == ISD200_GOOD) {
retStatus = isd200_srst(us);
if (retStatus == ISD200_GOOD)
/* ata or atapi? */
retStatus = isd200_try_enum( us, ATA_ADDRESS_DEVHEAD_STD, TRUE );
}
isslave = (info->DeviceHead & ATA_ADDRESS_DEVHEAD_SLAVE) ? 1 : 0;
if (info->ConfigData.MasterSlaveSelection != isslave) {
US_DEBUGP(" Setting Master/Slave selection to %d\n", isslave);
info->ConfigData.MasterSlaveSelection = isslave;
retStatus = isd200_write_config(us);
}
}
US_DEBUGP("Leaving isd200_manual_enum %08X\n", retStatus);
return(retStatus);
}
/**************************************************************************
* isd200_get_inquiry_data
*
* Get inquiry data
*
* RETURNS:
* ISD status code
*/
int isd200_get_inquiry_data( struct us_data *us )
{
struct isd200_info *info = (struct isd200_info *)us->extra;
int retStatus = ISD200_GOOD;
US_DEBUGP("Entering isd200_get_inquiry_data\n");
/* set default to Master */
info->DeviceHead = ATA_ADDRESS_DEVHEAD_STD;
/* attempt to manually enumerate this device */
retStatus = isd200_manual_enum(us);
if (retStatus == ISD200_GOOD) {
int transferStatus;
/* check for an ATA device */
if (info->DeviceFlags & DF_ATA_DEVICE) {
/* this must be an ATA device */
/* perform an ATA Commmand Identify */
transferStatus = isd200_action( us, ACTION_IDENTIFY,
&info->drive,
sizeof(struct hd_driveid) );
if (transferStatus != ISD200_TRANSPORT_GOOD) {
/* Error issuing ATA Command Identify */
US_DEBUGP(" Error issuing ATA Command Identify\n");
retStatus = ISD200_ERROR;
} else {
/* ATA Command Identify successful */
int i;
US_DEBUGP(" Identify Data Structure:\n");
US_DEBUGP(" config = 0x%x\n", info->drive.config);
US_DEBUGP(" cyls = 0x%x\n", info->drive.cyls);
US_DEBUGP(" heads = 0x%x\n", info->drive.heads);
US_DEBUGP(" track_bytes = 0x%x\n", info->drive.track_bytes);
US_DEBUGP(" sector_bytes = 0x%x\n", info->drive.sector_bytes);
US_DEBUGP(" sectors = 0x%x\n", info->drive.sectors);
US_DEBUGP(" serial_no[0] = 0x%x\n", info->drive.serial_no[0]);
US_DEBUGP(" buf_type = 0x%x\n", info->drive.buf_type);
US_DEBUGP(" buf_size = 0x%x\n", info->drive.buf_size);
US_DEBUGP(" ecc_bytes = 0x%x\n", info->drive.ecc_bytes);
US_DEBUGP(" fw_rev[0] = 0x%x\n", info->drive.fw_rev[0]);
US_DEBUGP(" model[0] = 0x%x\n", info->drive.model[0]);
US_DEBUGP(" max_multsect = 0x%x\n", info->drive.max_multsect);
US_DEBUGP(" dword_io = 0x%x\n", info->drive.dword_io);
US_DEBUGP(" capability = 0x%x\n", info->drive.capability);
US_DEBUGP(" tPIO = 0x%x\n", info->drive.tPIO);
US_DEBUGP(" tDMA = 0x%x\n", info->drive.tDMA);
US_DEBUGP(" field_valid = 0x%x\n", info->drive.field_valid);
US_DEBUGP(" cur_cyls = 0x%x\n", info->drive.cur_cyls);
US_DEBUGP(" cur_heads = 0x%x\n", info->drive.cur_heads);
US_DEBUGP(" cur_sectors = 0x%x\n", info->drive.cur_sectors);
US_DEBUGP(" cur_capacity = 0x%x\n", (info->drive.cur_capacity1 << 16) + info->drive.cur_capacity0 );
US_DEBUGP(" multsect = 0x%x\n", info->drive.multsect);
US_DEBUGP(" lba_capacity = 0x%x\n", info->drive.lba_capacity);
US_DEBUGP(" command_set_1 = 0x%x\n", info->drive.command_set_1);
US_DEBUGP(" command_set_2 = 0x%x\n", info->drive.command_set_2);
memset(&info->InquiryData, 0, sizeof(info->InquiryData));
/* Standard IDE interface only supports disks */
info->InquiryData.DeviceType = DIRECT_ACCESS_DEVICE;
/* Fix-up the return data from an INQUIRY command to show
* ANSI SCSI rev 2 so we don't confuse the SCSI layers above us
* in Linux.
*/
info->InquiryData.Versions = 0x2;
/* The length must be at least 36 (5 + 31) */
info->InquiryData.AdditionalLength = 0x1F;
if (info->drive.command_set_1 & COMMANDSET_MEDIA_STATUS) {
/* set the removable bit */
info->InquiryData.RemovableMedia = 1;
info->DeviceFlags |= DF_REMOVABLE_MEDIA;
}
/* Fill in vendor identification fields */
for (i = 0; i < 20; i += 2) {
info->InquiryData.VendorId[i] =
info->drive.model[i + 1];
info->InquiryData.VendorId[i+1] =
info->drive.model[i];
}
/* Initialize unused portion of product id */
for (i = 0; i < 4; i++) {
info->InquiryData.ProductId[12+i] = ' ';
}
/* Move firmware revision from IDENTIFY data to */
/* product revision in INQUIRY data */
for (i = 0; i < 4; i += 2) {
info->InquiryData.ProductRevisionLevel[i] =
info->drive.fw_rev[i+1];
info->InquiryData.ProductRevisionLevel[i+1] =
info->drive.fw_rev[i];
}
/* determine if it supports Media Status Notification */
if (info->drive.command_set_2 & COMMANDSET_MEDIA_STATUS) {
US_DEBUGP(" Device supports Media Status Notification\n");
/* Indicate that it is enabled, even though it is not
* This allows the lock/unlock of the media to work
* correctly.
*/
info->DeviceFlags |= DF_MEDIA_STATUS_ENABLED;
}
else
info->DeviceFlags &= ~DF_MEDIA_STATUS_ENABLED;
}
} else {
/*
* this must be an ATAPI device
* use an ATAPI protocol (Transparent SCSI)
*/
us->protocol_name = "Transparent SCSI";
us->proto_handler = usb_stor_transparent_scsi_command;
US_DEBUGP("Protocol changed to: %s\n", us->protocol_name);
/* Free driver structure */
if (us->extra != NULL) {
kfree(us->extra);
us->extra = NULL;
us->extra_destructor = NULL;
}
}
}
US_DEBUGP("Leaving isd200_get_inquiry_data %08X\n", retStatus);
return(retStatus);
}
/**************************************************************************
* isd200_data_copy
*
* Copy data into the srb request buffer. Use scatter gather if required.
*
* RETURNS:
* void
*/
void isd200_data_copy(Scsi_Cmnd *srb, char * src, int length)
{
unsigned int len = length;
struct scatterlist *sg;
if (srb->use_sg) {
int i;
unsigned int total = 0;
/* Add up the sizes of all the sg segments */
sg = (struct scatterlist *) srb->request_buffer;
for (i = 0; i < srb->use_sg; i++)
total += sg[i].length;
if (length > total)
len = total;
total = 0;
/* Copy data into sg buffer(s) */
for (i = 0; i < srb->use_sg; i++) {
if ((len > total) && (len > 0)) {
/* transfer the lesser of the next buffer or the
* remaining data */
if (len - total >= sg[i].length) {
memcpy(sg[i].address, src + total, sg[i].length);
total += sg[i].length;
} else {
memcpy(sg[i].address, src + total, len - total);
total = len;
}
}
else
break;
}
} else {
/* Make sure length does not exceed buffer length */
if (length > srb->request_bufflen)
len = srb->request_bufflen;
if (len > 0)
memcpy(srb->request_buffer, src, len);
}
}
/**************************************************************************
* isd200_scsi_to_ata
*
* Translate SCSI commands to ATA commands.
*
* RETURNS:
* TRUE if the command needs to be sent to the transport layer
* FALSE otherwise
*/
int isd200_scsi_to_ata(Scsi_Cmnd *srb, struct us_data *us,
union ata_cdb * ataCdb)
{
struct isd200_info *info = (struct isd200_info *)us->extra;
int sendToTransport = TRUE;
unsigned char sectnum, head;
unsigned short cylinder;
unsigned long lba;
unsigned long blockCount;
unsigned char senseData[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
memset(ataCdb, 0, sizeof(union ata_cdb));
/* SCSI Command */
switch (srb->cmnd[0]) {
case INQUIRY:
US_DEBUGP(" ATA OUT - INQUIRY\n");
if (srb->request_bufflen > sizeof(struct inquiry_data))
srb->request_bufflen = sizeof(struct inquiry_data);
/* copy InquiryData */
isd200_data_copy(srb, (char *) &info->InquiryData, srb->request_bufflen);
srb->result = GOOD;
sendToTransport = FALSE;
break;
case MODE_SENSE:
US_DEBUGP(" ATA OUT - SCSIOP_MODE_SENSE\n");
/* Initialize the return buffer */
isd200_data_copy(srb, (char *) &senseData, 8);
if (info->DeviceFlags & DF_MEDIA_STATUS_ENABLED)
{
ataCdb->generic.SignatureByte0 = info->ConfigData.ATAMajorCommand;
ataCdb->generic.SignatureByte1 = info->ConfigData.ATAMinorCommand;
ataCdb->generic.TransferBlockSize = 1;
ataCdb->write.SelectCommand = 1;
ataCdb->write.CommandByte = ATA_COMMAND_GET_MEDIA_STATUS;
srb->request_bufflen = 0;
} else {
US_DEBUGP(" Media Status not supported, just report okay\n");
srb->result = GOOD;
sendToTransport = FALSE;
}
break;
case TEST_UNIT_READY:
US_DEBUGP(" ATA OUT - SCSIOP_TEST_UNIT_READY\n");
/* Initialize the return buffer */
isd200_data_copy(srb, (char *) &senseData, 8);
if (info->DeviceFlags & DF_MEDIA_STATUS_ENABLED)
{
ataCdb->generic.SignatureByte0 = info->ConfigData.ATAMajorCommand;
ataCdb->generic.SignatureByte1 = info->ConfigData.ATAMinorCommand;
ataCdb->generic.TransferBlockSize = 1;
ataCdb->write.SelectCommand = 1;
ataCdb->write.CommandByte = ATA_COMMAND_GET_MEDIA_STATUS;
srb->request_bufflen = 0;
} else {
US_DEBUGP(" Media Status not supported, just report okay\n");
srb->result = GOOD;
sendToTransport = FALSE;
}
break;
case READ_CAPACITY:
{
unsigned long capacity;
struct read_capacity_data readCapacityData;
US_DEBUGP(" ATA OUT - SCSIOP_READ_CAPACITY\n");
if (info->drive.capability & CAPABILITY_LBA ) {
capacity = info->drive.lba_capacity - 1;
} else {
capacity = (info->drive.heads *
info->drive.cyls *
info->drive.sectors) - 1;
}
readCapacityData.LogicalBlockAddress = cpu_to_be32(capacity);
readCapacityData.BytesPerBlock = cpu_to_be32(0x200);
if (srb->request_bufflen > sizeof(struct read_capacity_data))
srb->request_bufflen = sizeof(struct read_capacity_data);
isd200_data_copy(srb, (char *) &readCapacityData, srb->request_bufflen);
srb->result = GOOD;
sendToTransport = FALSE;
}
break;
case READ_10:
US_DEBUGP(" ATA OUT - SCSIOP_READ\n");
lba = *(unsigned long *)&srb->cmnd[2];
lba = cpu_to_be32(lba);
blockCount = (unsigned long)srb->cmnd[7]<<8 | (unsigned long)srb->cmnd[8];
if (info->drive.capability & CAPABILITY_LBA) {
sectnum = (unsigned char)(lba);
cylinder = (unsigned short)(lba>>8);
head = ATA_ADDRESS_DEVHEAD_LBA_MODE | (unsigned char)(lba>>24 & 0x0F);
} else {
sectnum = (unsigned char)((lba % info->drive.sectors) + 1);
cylinder = (unsigned short)(lba / (info->drive.sectors *
info->drive.heads));
head = (unsigned char)((lba / info->drive.sectors) %
info->drive.heads);
}
ataCdb->generic.SignatureByte0 = info->ConfigData.ATAMajorCommand;
ataCdb->generic.SignatureByte1 = info->ConfigData.ATAMinorCommand;
ataCdb->generic.TransferBlockSize = 1;
ataCdb->write.SelectSectorCount = 1;
ataCdb->write.SectorCountByte = (unsigned char)blockCount;
ataCdb->write.SelectSectorNumber = 1;
ataCdb->write.SectorNumberByte = sectnum;
ataCdb->write.SelectCylinderHigh = 1;
ataCdb->write.CylinderHighByte = (unsigned char)(cylinder>>8);
ataCdb->write.SelectCylinderLow = 1;
ataCdb->write.CylinderLowByte = (unsigned char)cylinder;
ataCdb->write.SelectDeviceHead = 1;
ataCdb->write.DeviceHeadByte = (head | ATA_ADDRESS_DEVHEAD_STD);
ataCdb->write.SelectCommand = 1;
ataCdb->write.CommandByte = WIN_READ;
break;
case WRITE_10:
US_DEBUGP(" ATA OUT - SCSIOP_WRITE\n");
lba = *(unsigned long *)&srb->cmnd[2];
lba = cpu_to_be32(lba);
blockCount = (unsigned long)srb->cmnd[7]<<8 | (unsigned long)srb->cmnd[8];
if (info->drive.capability & CAPABILITY_LBA) {
sectnum = (unsigned char)(lba);
cylinder = (unsigned short)(lba>>8);
head = ATA_ADDRESS_DEVHEAD_LBA_MODE | (unsigned char)(lba>>24 & 0x0F);
} else {
sectnum = (unsigned char)((lba % info->drive.sectors) + 1);
cylinder = (unsigned short)(lba / (info->drive.sectors * info->drive.heads));
head = (unsigned char)((lba / info->drive.sectors) % info->drive.heads);
}
ataCdb->generic.SignatureByte0 = info->ConfigData.ATAMajorCommand;
ataCdb->generic.SignatureByte1 = info->ConfigData.ATAMinorCommand;
ataCdb->generic.TransferBlockSize = 1;
ataCdb->write.SelectSectorCount = 1;
ataCdb->write.SectorCountByte = (unsigned char)blockCount;
ataCdb->write.SelectSectorNumber = 1;
ataCdb->write.SectorNumberByte = sectnum;
ataCdb->write.SelectCylinderHigh = 1;
ataCdb->write.CylinderHighByte = (unsigned char)(cylinder>>8);
ataCdb->write.SelectCylinderLow = 1;
ataCdb->write.CylinderLowByte = (unsigned char)cylinder;
ataCdb->write.SelectDeviceHead = 1;
ataCdb->write.DeviceHeadByte = (head | ATA_ADDRESS_DEVHEAD_STD);
ataCdb->write.SelectCommand = 1;
ataCdb->write.CommandByte = WIN_WRITE;
break;
case ALLOW_MEDIUM_REMOVAL:
US_DEBUGP(" ATA OUT - SCSIOP_MEDIUM_REMOVAL\n");
if (info->DeviceFlags & DF_REMOVABLE_MEDIA) {
US_DEBUGP(" srb->cmnd[4] = 0x%X\n", srb->cmnd[4]);
ataCdb->generic.SignatureByte0 = info->ConfigData.ATAMajorCommand;
ataCdb->generic.SignatureByte1 = info->ConfigData.ATAMinorCommand;
ataCdb->generic.TransferBlockSize = 1;
ataCdb->write.SelectCommand = 1;
ataCdb->write.CommandByte = (srb->cmnd[4] & 0x1) ?
WIN_DOORLOCK : WIN_DOORUNLOCK;
srb->request_bufflen = 0;
} else {
US_DEBUGP(" Not removeable media, just report okay\n");
srb->result = GOOD;
sendToTransport = FALSE;
}
break;
case START_STOP:
US_DEBUGP(" ATA OUT - SCSIOP_START_STOP_UNIT\n");
US_DEBUGP(" srb->cmnd[4] = 0x%X\n", srb->cmnd[4]);
/* Initialize the return buffer */
isd200_data_copy(srb, (char *) &senseData, 8);
if ((srb->cmnd[4] & 0x3) == 0x2) {
US_DEBUGP(" Media Eject\n");
ataCdb->generic.SignatureByte0 = info->ConfigData.ATAMajorCommand;
ataCdb->generic.SignatureByte1 = info->ConfigData.ATAMinorCommand;
ataCdb->generic.TransferBlockSize = 0;
ataCdb->write.SelectCommand = 1;
ataCdb->write.CommandByte = ATA_COMMAND_MEDIA_EJECT;
} else if ((srb->cmnd[4] & 0x3) == 0x1) {
US_DEBUGP(" Get Media Status\n");
ataCdb->generic.SignatureByte0 = info->ConfigData.ATAMajorCommand;
ataCdb->generic.SignatureByte1 = info->ConfigData.ATAMinorCommand;
ataCdb->generic.TransferBlockSize = 1;
ataCdb->write.SelectCommand = 1;
ataCdb->write.CommandByte = ATA_COMMAND_GET_MEDIA_STATUS;
srb->request_bufflen = 0;
} else {
US_DEBUGP(" Nothing to do, just report okay\n");
srb->result = GOOD;
sendToTransport = FALSE;
}
break;
default:
US_DEBUGP("Unsupported SCSI command - 0x%X\n", srb->cmnd[0]);
srb->result = DID_ERROR << 16;
sendToTransport = FALSE;
break;
}
return(sendToTransport);
}
/**************************************************************************
* isd200_init_info
*
* Allocates (if necessary) and initializes the driver structure.
*
* RETURNS:
* ISD status code
*/
int isd200_init_info(struct us_data *us)
{
int retStatus = ISD200_GOOD;
if (!us->extra) {
us->extra = (void *) kmalloc(sizeof(struct isd200_info), GFP_KERNEL);
if (!us->extra) {
US_DEBUGP("ERROR - kmalloc failure\n");
retStatus = ISD200_ERROR;
}
}
if (retStatus == ISD200_GOOD) {
memset(us->extra, 0, sizeof(struct isd200_info));
}
return(retStatus);
}
/**************************************************************************
* Initialization for the ISD200
*/
int isd200_Initialization(struct us_data *us)
{
US_DEBUGP("ISD200 Initialization...\n");
/* Initialize ISD200 info struct */
if (isd200_init_info(us) == ISD200_ERROR) {
US_DEBUGP("ERROR Initializing ISD200 Info struct\n");
} else {
/* Get device specific data */
if (isd200_get_inquiry_data(us) != ISD200_GOOD)
US_DEBUGP("ISD200 Initialization Failure\n");
else
US_DEBUGP("ISD200 Initialization complete\n");
}
return 0;
}
/**************************************************************************
* Protocol and Transport for the ISD200 ASIC
*
* This protocol and transport are for ATA devices connected to an ISD200
* ASIC. An ATAPI device that is conected as a slave device will be
* detected in the driver initialization function and the protocol will
* be changed to an ATAPI protocol (Transparent SCSI).
*
*/
void isd200_ata_command(Scsi_Cmnd *srb, struct us_data *us)
{
int sendToTransport = TRUE;
union ata_cdb ataCdb;
/* Make sure driver was initialized */
if (us->extra == NULL)
US_DEBUGP("ERROR Driver not initialized\n");
/* Convert command */
sendToTransport = isd200_scsi_to_ata(srb, us, &ataCdb);
/* send the command to the transport layer */
if (sendToTransport)
isd200_invoke_transport(us, srb, &ataCdb);
}
/* 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(Scsi_Cmnd *srb, struct us_data *us);
extern int isd200_Initialization(struct us_data *us);
#endif
/* Driver for Lexar "Jumpshot" Compact Flash reader
*
* 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)
*
* 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. Basically this means
* setting mode_param_header[3] = 0x80.
*/
#include "transport.h"
#include "protocol.h"
#include "usb.h"
#include "debug.h"
#include "jumpshot.h"
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/malloc.h>
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);
extern int usb_stor_bulk_msg(struct us_data *us, void *data, int pipe,
unsigned int len, unsigned int *act_len);
#if 0
static void jumpshot_dump_data(unsigned char *data, int len)
{
unsigned char buf[80];
int sofar = 0;
if (!data)
return;
memset(buf, 0, sizeof(buf));
for (sofar = 0; sofar < len; sofar++) {
sprintf(buf + strlen(buf), "%02x ",
((unsigned int) data[sofar]) & 0xFF);
if (sofar % 16 == 15) {
US_DEBUGP("jumpshot: %s\n", buf);
memset(buf, 0, sizeof(buf));
}
}
if (strlen(buf) != 0)
US_DEBUGP("jumpshot: %s\n", buf);
}
#endif
/*
* Send a control message and wait for the response.
*
* us - the pointer to the us_data structure for the device to use
*
* request - the URB Setup Packet's first 6 bytes. The first byte always
* corresponds to the request type, and the second byte always corresponds
* to the request. The other 4 bytes do not correspond to value and index,
* since they are used in a custom way by the SCM protocol.
*
* xfer_data - a buffer from which to get, or to which to store, any data
* that gets send or received, respectively, with the URB. Even though
* it looks like we allocate a buffer in this code for the data, xfer_data
* must contain enough allocated space.
*
* xfer_len - the number of bytes to send or receive with the URB.
*
* This routine snarfed from the SanDisk SDDR-09 driver
*
*/
static int jumpshot_send_control(struct us_data *us,
int pipe,
unsigned char request,
unsigned char requesttype,
unsigned short value,
unsigned short index,
unsigned char *xfer_data,
unsigned int xfer_len)
{
int result;
// Send the URB to the device and wait for a response.
/* Why are request and request type reversed in this call? */
result = usb_stor_control_msg(us, pipe,
request, requesttype,
value, index, xfer_data, xfer_len);
// Check the return code for the command.
if (result < 0) {
/* if the command was aborted, indicate that */
if (result == -ENOENT)
return USB_STOR_TRANSPORT_ABORTED;
/* a stall is a fatal condition from the device */
if (result == -EPIPE) {
US_DEBUGP("jumpshot_send_control: -- Stall on control pipe. Clearing\n");
result = usb_clear_halt(us->pusb_dev, pipe);
US_DEBUGP("jumpshot_send_control: -- usb_clear_halt() returns %d\n", result);
return USB_STOR_TRANSPORT_FAILED;
}
/* Uh oh... serious problem here */
return USB_STOR_TRANSPORT_ERROR;
}
return USB_STOR_TRANSPORT_GOOD;
}
static int jumpshot_raw_bulk(int direction,
struct us_data *us,
unsigned char *data,
unsigned int len)
{
int result;
int act_len;
int pipe;
if (direction == SCSI_DATA_READ)
pipe = usb_rcvbulkpipe(us->pusb_dev, us->ep_in);
else
pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out);
result = usb_stor_bulk_msg(us, data, pipe, len, &act_len);
// if we stall, we need to clear it before we go on
if (result == -EPIPE) {
US_DEBUGP("jumpshot_raw_bulk: EPIPE. clearing endpoint halt for"
" pipe 0x%x, stalled at %d bytes\n", pipe, act_len);
usb_clear_halt(us->pusb_dev, pipe);
}
if (result) {
// NAK - that means we've retried a few times already
if (result == -ETIMEDOUT) {
US_DEBUGP("jumpshot_raw_bulk: device NAKed\n");
return US_BULK_TRANSFER_FAILED;
}
// -ENOENT -- we canceled this transfer
if (result == -ENOENT) {
US_DEBUGP("jumpshot_raw_bulk: transfer aborted\n");
return US_BULK_TRANSFER_ABORTED;
}
if (result == -EPIPE) {
US_DEBUGP("jumpshot_raw_bulk: output pipe stalled\n");
return USB_STOR_TRANSPORT_FAILED;
}
// the catch-all case
US_DEBUGP("jumpshot_raw_bulk: unknown error\n");
return US_BULK_TRANSFER_FAILED;
}
if (act_len != len) {
US_DEBUGP("jumpshot_raw_bulk: Warning. Transferred only %d bytes\n", act_len);
return US_BULK_TRANSFER_SHORT;
}
US_DEBUGP("jumpshot_raw_bulk: Transfered %d of %d bytes\n", act_len, len);
return US_BULK_TRANSFER_GOOD;
}
static inline int jumpshot_bulk_read(struct us_data *us,
unsigned char *data,
unsigned int len)
{
if (len == 0)
return USB_STOR_TRANSPORT_GOOD;
US_DEBUGP("jumpshot_bulk_read: len = %d\n", len);
return jumpshot_raw_bulk(SCSI_DATA_READ, us, data, len);
}
static inline int jumpshot_bulk_write(struct us_data *us,
unsigned char *data,
unsigned int len)
{
if (len == 0)
return USB_STOR_TRANSPORT_GOOD;
US_DEBUGP("jumpshot_bulk_write: len = %d\n", len);
return jumpshot_raw_bulk(SCSI_DATA_WRITE, us, data, len);
}
static int jumpshot_get_status(struct us_data *us)
{
unsigned char reply;
int rc;
if (!us)
return USB_STOR_TRANSPORT_ERROR;
// send the setup
rc = jumpshot_send_control(us,
usb_rcvctrlpipe(us->pusb_dev, 0),
0, 0xA0, 0, 7, &reply, 1);
if (rc != USB_STOR_TRANSPORT_GOOD)
return rc;
if (reply != 0x50) {
US_DEBUGP("jumpshot_get_status: 0x%2x\n",
(unsigned short) (reply));
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 *dest,
int use_sg)
{
unsigned char command[] = { 0, 0, 0, 0, 0, 0xe0, 0x20 };
unsigned char *buffer = NULL;
unsigned char *ptr;
unsigned char thistime;
struct scatterlist *sg = NULL;
int totallen, len, result;
int sg_idx = 0, current_sg_offset = 0;
int transferred;
// 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;
// If we're using scatter-gather, we have to create a new
// buffer to read all of the data in first, since a
// scatter-gather buffer could in theory start in the middle
// of a page, which would be bad. A developer who wants a
// challenge might want to write a limited-buffer
// version of this code.
totallen = sectors * info->ssize;
do {
// loop, never allocate or transfer more than 64k at once (min(128k, 255*info->ssize) is the real limit)
len = min(totallen, 65536);
if (use_sg) {
sg = (struct scatterlist *) dest;
buffer = kmalloc(len, GFP_KERNEL);
if (buffer == NULL)
return USB_STOR_TRANSPORT_ERROR;
ptr = buffer;
} else {
ptr = dest;
}
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] |= (sector >> 24) & 0x0F;
// send the setup + command
result = jumpshot_send_control(us,
usb_sndctrlpipe(us->pusb_dev, 0),
0, 0x20, 0, 1, command, 7);
if (result != USB_STOR_TRANSPORT_GOOD) {
if (use_sg)
kfree(buffer);
return result;
}
// read the result
result = jumpshot_bulk_read(us, ptr, len);
if (result != USB_STOR_TRANSPORT_GOOD) {
if (use_sg)
kfree(buffer);
return result;
}
US_DEBUGP("jumpshot_read_data: %d bytes\n", len);
//jumpshot_dump_data(ptr, len);
sectors -= thistime;
sector += thistime;
if (use_sg) {
transferred = 0;
while (sg_idx < use_sg && transferred < len) {
if (len - transferred >= sg[sg_idx].length - current_sg_offset) {
US_DEBUGP("jumpshot_read_data: adding %d bytes to %d byte sg buffer\n", sg[sg_idx].length - current_sg_offset, sg[sg_idx].length);
memcpy(sg[sg_idx].address + current_sg_offset,
buffer + transferred,
sg[sg_idx].length - current_sg_offset);
transferred += sg[sg_idx].length - current_sg_offset;
current_sg_offset = 0;
// on to the next sg buffer
++sg_idx;
} else {
US_DEBUGP("jumpshot_read_data: adding %d bytes to %d byte sg buffer\n", len - transferred, sg[sg_idx].length);
memcpy(sg[sg_idx].address + current_sg_offset,
buffer + transferred,
len - transferred);
current_sg_offset += len - transferred;
// this sg buffer is only partially full and we're out of data to copy in
break;
}
}
kfree(buffer);
} else {
dest += len;
}
totallen -= len;
} while (totallen > 0);
return USB_STOR_TRANSPORT_GOOD;
}
static int jumpshot_write_data(struct us_data *us,
struct jumpshot_info *info,
u32 sector,
u32 sectors,
unsigned char *src,
int use_sg)
{
unsigned char command[7] = { 0, 0, 0, 0, 0, 0xE0, 0x30 };
unsigned char *buffer = NULL;
unsigned char *ptr;
unsigned char thistime;
struct scatterlist *sg = NULL;
int totallen, len, result, waitcount;
int sg_idx = 0, current_sg_offset = 0;
int transferred;
// 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;
// If we're using scatter-gather, we have to create a new
// buffer to read all of the data in first, since a
// scatter-gather buffer could in theory start in the middle
// of a page, which would be bad. A developer who wants a
// challenge might want to write a limited-buffer
// version of this code.
totallen = sectors * info->ssize;
do {
// loop, never allocate or transfer more than 64k at once (min(128k, 255*info->ssize) is the real limit)
len = min(totallen, 65536);
if (use_sg) {
sg = (struct scatterlist *) src;
buffer = kmalloc(len, GFP_KERNEL);
if (buffer == NULL)
return USB_STOR_TRANSPORT_ERROR;
ptr = buffer;
memset(buffer, 0, len);
// copy the data from the sg bufs into the big contiguous buf
//
transferred = 0;
while (transferred < len) {
if (len - transferred >= sg[sg_idx].length - current_sg_offset) {
US_DEBUGP("jumpshot_write_data: getting %d bytes from %d byte sg buffer\n", sg[sg_idx].length - current_sg_offset, sg[sg_idx].length);
memcpy(ptr + transferred,
sg[sg_idx].address + current_sg_offset,
sg[sg_idx].length - current_sg_offset);
transferred += sg[sg_idx].length - current_sg_offset;
current_sg_offset = 0;
// on to the next sg buffer
++sg_idx;
} else {
US_DEBUGP("jumpshot_write_data: getting %d bytes from %d byte sg buffer\n", len - transferred, sg[sg_idx].length);
memcpy(ptr + transferred,
sg[sg_idx].address + current_sg_offset,
len - transferred);
current_sg_offset += len - transferred;
// we only copied part of this sg buffer
break;
}
}
} else {
ptr = src;
}
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] |= (sector >> 24) & 0x0F;
// send the setup + command
result = jumpshot_send_control(us,
usb_sndctrlpipe(us->pusb_dev, 0),
0, 0x20, 0, 1, command, 7);
// send the data
result = jumpshot_bulk_write(us, ptr, len);
if (result != USB_STOR_TRANSPORT_GOOD) {
if (use_sg)
kfree(buffer);
return result;
}
// 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.
//
wait_ms(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");
sectors -= thistime;
sector += thistime;
if (use_sg) {
kfree(buffer);
} else {
src += len;
}
totallen -= len;
} while (totallen > 0);
return result;
}
static int jumpshot_id_device(struct us_data *us,
struct jumpshot_info *info)
{
unsigned char command[2] = { 0xe0, 0xec };
unsigned char reply[512];
int rc;
if (!us || !info)
return USB_STOR_TRANSPORT_ERROR;
// send the setup
rc = jumpshot_send_control(us,
usb_sndctrlpipe(us->pusb_dev, 0),
0, 0x20, 0, 6, command, 2);
if (rc != USB_STOR_TRANSPORT_GOOD) {
US_DEBUGP("jumpshot_id_device: Gah! send_control for read_capacity failed\n");
return rc;
}
// read the reply
rc = jumpshot_bulk_read(us, reply, sizeof(reply));
if (rc != USB_STOR_TRANSPORT_GOOD)
return rc;
info->sectors = ((u32)(reply[117]) << 24) |
((u32)(reply[116]) << 16) |
((u32)(reply[115]) << 8) |
((u32)(reply[114]) );
return USB_STOR_TRANSPORT_GOOD;
}
static int jumpshot_handle_mode_sense(struct us_data *us,
Scsi_Cmnd * srb,
unsigned char *ptr,
int sense_6)
{
unsigned char mode_param_header[8] = {
0, 0, 0, 0, 0, 0, 0, 0
};
unsigned char rw_err_page[12] = {
0x1, 0xA, 0x21, 1, 0, 0, 0, 0, 1, 0, 0, 0
};
unsigned char cache_page[12] = {
0x8, 0xA, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
unsigned char rbac_page[12] = {
0x1B, 0xA, 0, 0x81, 0, 0, 0, 0, 0, 0, 0, 0
};
unsigned char timer_page[8] = {
0x1C, 0x6, 0, 0, 0, 0
};
unsigned char pc, page_code;
unsigned short total_len = 0;
unsigned short param_len, i = 0;
if (sense_6)
param_len = srb->cmnd[4];
else
param_len = ((u32) (srb->cmnd[7]) >> 8) | ((u32) (srb->cmnd[8]));
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;
}
mode_param_header[3] = 0x80; // write enable
switch (page_code) {
case 0x0:
// vendor-specific mode
return USB_STOR_TRANSPORT_ERROR;
case 0x1:
total_len = sizeof(rw_err_page);
mode_param_header[0] = total_len >> 8;
mode_param_header[1] = total_len & 0xFF;
mode_param_header[3] = 0x00; // WP enable: 0x80
memcpy(ptr, mode_param_header, sizeof(mode_param_header));
i += sizeof(mode_param_header);
memcpy(ptr + i, rw_err_page, sizeof(rw_err_page));
break;
case 0x8:
total_len = sizeof(cache_page);
mode_param_header[0] = total_len >> 8;
mode_param_header[1] = total_len & 0xFF;
mode_param_header[3] = 0x00; // WP enable: 0x80
memcpy(ptr, mode_param_header, sizeof(mode_param_header));
i += sizeof(mode_param_header);
memcpy(ptr + i, cache_page, sizeof(cache_page));
break;
case 0x1B:
total_len = sizeof(rbac_page);
mode_param_header[0] = total_len >> 8;
mode_param_header[1] = total_len & 0xFF;
mode_param_header[3] = 0x00; // WP enable: 0x80
memcpy(ptr, mode_param_header, sizeof(mode_param_header));
i += sizeof(mode_param_header);
memcpy(ptr + i, rbac_page, sizeof(rbac_page));
break;
case 0x1C:
total_len = sizeof(timer_page);
mode_param_header[0] = total_len >> 8;
mode_param_header[1] = total_len & 0xFF;
mode_param_header[3] = 0x00; // WP enable: 0x80
memcpy(ptr, mode_param_header, sizeof(mode_param_header));
i += sizeof(mode_param_header);
memcpy(ptr + i, timer_page, sizeof(timer_page));
break;
case 0x3F:
total_len = sizeof(timer_page) + sizeof(rbac_page) +
sizeof(cache_page) + sizeof(rw_err_page);
mode_param_header[0] = total_len >> 8;
mode_param_header[1] = total_len & 0xFF;
mode_param_header[3] = 0x00; // WP enable: 0x80
memcpy(ptr, mode_param_header, sizeof(mode_param_header));
i += sizeof(mode_param_header);
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));
break;
}
return USB_STOR_TRANSPORT_GOOD;
}
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(Scsi_Cmnd * srb, struct us_data *us)
{
struct jumpshot_info *info;
int rc;
unsigned long block, blocks;
unsigned char *ptr = NULL;
unsigned char inquiry_response[36] = {
0x00, 0x80, 0x00, 0x01, 0x1F, 0x00, 0x00, 0x00
};
if (!us->extra) {
us->extra = kmalloc(sizeof(struct jumpshot_info), GFP_KERNEL);
if (!us->extra) {
US_DEBUGP("jumpshot_transport: Gah! Can't allocate storage for jumpshot info struct!\n");
return USB_STOR_TRANSPORT_ERROR;
}
memset(us->extra, 0, sizeof(struct jumpshot_info));
us->extra_destructor = jumpshot_info_destructor;
}
info = (struct jumpshot_info *) (us->extra);
ptr = (unsigned char *) srb->request_buffer;
if (srb->cmnd[0] == INQUIRY) {
US_DEBUGP("jumpshot_transport: INQUIRY. Returning bogus response.\n");
memset(inquiry_response + 8, 0, 28);
fill_inquiry_response(us, inquiry_response, 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
//
ptr[0] = (info->sectors >> 24) & 0xFF;
ptr[1] = (info->sectors >> 16) & 0xFF;
ptr[2] = (info->sectors >> 8) & 0xFF;
ptr[3] = (info->sectors) & 0xFF;
ptr[4] = (info->ssize >> 24) & 0xFF;
ptr[5] = (info->ssize >> 16) & 0xFF;
ptr[6] = (info->ssize >> 8) & 0xFF;
ptr[7] = (info->ssize) & 0xFF;
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, ptr, srb->use_sg);
}
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, ptr, srb->use_sg);
}
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, ptr, srb->use_sg);
}
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, ptr, srb->use_sg);
}
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. Returning NO SENSE for now\n");
ptr[0] = 0xF0;
ptr[2] = info->sense_key;
ptr[7] = 11;
ptr[12] = info->sense_asc;
ptr[13] = info->sense_ascq;
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, ptr, TRUE);
}
if (srb->cmnd[0] == MODE_SENSE_10) {
US_DEBUGP("jumpshot_transport: MODE_SENSE_10 detected\n");
return jumpshot_handle_mode_sense(us, srb, ptr, FALSE);
}
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;
}
US_DEBUGP("jumpshot_transport: Gah! Unknown command: %d (0x%x)\n", srb->cmnd[0], srb->cmnd[0]);
return USB_STOR_TRANSPORT_ERROR;
}
/* 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
#define min(a,b) (((a)<(b))?(a):(b)) // this is defined in tons of header files, i wish it had a standar single definition...
extern int jumpshot_transport(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
/* Driver for USB Mass Storage compliant devices
*
* $Id: protocol.c,v 1.7 2000/11/13 22:28:33 mdharm Exp $
* $Id: protocol.c,v 1.10 2001/07/30 00:27:59 mdharm Exp $
*
* Current development and maintenance by:
* (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
......@@ -274,72 +274,68 @@ void usb_stor_ufi_command(Scsi_Cmnd *srb, struct us_data *us)
void usb_stor_transparent_scsi_command(Scsi_Cmnd *srb, struct us_data *us)
{
int old_cmnd = 0;
/* This code supports devices which do not support {READ|WRITE}_6
* Apparently, neither Windows or MacOS will use these commands,
* so some devices do not support them
*/
if (us->flags & US_FL_MODE_XLATE) {
US_DEBUGP("Invoking Mode Translation\n");
/* save the old command for later */
old_cmnd = srb->cmnd[0];
/* translate READ_6 to READ_10 */
if (srb->cmnd[0] == 0x08) {
/* get the control */
srb->cmnd[9] = us->srb->cmnd[5];
/* get the length */
srb->cmnd[8] = us->srb->cmnd[6];
switch (srb->cmnd[0]) {
/* change READ_6/WRITE_6 to READ_10/WRITE_10 */
case WRITE_6:
case READ_6:
srb->cmd_len = 12;
srb->cmnd[11] = 0;
srb->cmnd[10] = 0;
srb->cmnd[9] = 0;
srb->cmnd[8] = srb->cmnd[4];
srb->cmnd[7] = 0;
/* set the reserved area to 0 */
srb->cmnd[6] = 0;
/* get LBA */
srb->cmnd[5] = us->srb->cmnd[3];
srb->cmnd[4] = us->srb->cmnd[2];
srb->cmnd[3] = 0;
srb->cmnd[6] = 0;
srb->cmnd[5] = srb->cmnd[3];
srb->cmnd[4] = srb->cmnd[2];
srb->cmnd[3] = srb->cmnd[1] & 0x1F;
srb->cmnd[2] = 0;
/* LUN and other info in cmnd[1] can stay */
/* fix command code */
srb->cmnd[0] = 0x28;
US_DEBUGP("Changing READ_6 to READ_10\n");
US_DEBUG(usb_stor_show_command(srb));
}
/* translate WRITE_6 to WRITE_10 */
if (srb->cmnd[0] == 0x0A) {
/* get the control */
srb->cmnd[9] = us->srb->cmnd[5];
/* get the length */
srb->cmnd[8] = us->srb->cmnd[4];
srb->cmnd[1] = srb->cmnd[1] & 0xE0;
srb->cmnd[0] = srb->cmnd[0] | 0x20;
break;
/* convert MODE_SELECT data here */
case MODE_SENSE:
case MODE_SELECT:
srb->cmd_len = 12;
srb->cmnd[11] = 0;
srb->cmnd[10] = 0;
srb->cmnd[9] = 0;
srb->cmnd[8] = srb->cmnd[4];
srb->cmnd[7] = 0;
/* set the reserved area to 0 */
srb->cmnd[6] = 0;
/* get LBA */
srb->cmnd[5] = us->srb->cmnd[3];
srb->cmnd[4] = us->srb->cmnd[2];
srb->cmnd[6] = 0;
srb->cmnd[5] = 0;
srb->cmnd[4] = 0;
srb->cmnd[3] = 0;
srb->cmnd[2] = 0;
/* LUN and other info in cmnd[1] can stay */
/* fix command code */
srb->cmnd[0] = 0x2A;
US_DEBUGP("Changing WRITE_6 to WRITE_10\n");
US_DEBUG(usb_stor_show_command(us->srb));
}
srb->cmnd[2] = srb->cmnd[2];
srb->cmnd[1] = srb->cmnd[1];
srb->cmnd[0] = srb->cmnd[0] | 0x40;
break;
} /* switch (srb->cmnd[0]) */
} /* if (us->flags & US_FL_MODE_XLATE) */
/* convert MODE_SELECT data here */
if ((us->flags & US_FL_MODE_XLATE) && (old_cmnd == MODE_SELECT))
usb_stor_scsiSense6to10(srb);
/* send the command to the transport layer */
usb_stor_invoke_transport(srb, us);
/* Fix the MODE_SENSE data if we translated the command */
if ((us->flags & US_FL_MODE_XLATE) && (old_cmnd == MODE_SENSE)
&& (status_byte(srb->result) == GOOD))
usb_stor_scsiSense10to6(srb);
/* fix the INQUIRY data if necessary */
fix_inquiry_data(srb);
}
......
/* Driver for USB Mass Storage compliant devices
* Protocol Functions Header File
*
* $Id: protocol.h,v 1.3 2000/08/25 00:13:51 mdharm Exp $
* $Id: protocol.h,v 1.4 2001/02/13 07:10:03 mdharm Exp $
*
* Current development and maintenance by:
* (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
......@@ -53,8 +53,9 @@
#define US_SC_UFI 0x04 /* Floppy */
#define US_SC_8070 0x05 /* Removable media */
#define US_SC_SCSI 0x06 /* Transparent */
#define US_SC_ISD200 0x07 /* ISD200 ATA */
#define US_SC_MIN US_SC_RBC
#define US_SC_MAX US_SC_SCSI
#define US_SC_MAX US_SC_ISD200
extern void usb_stor_ATAPI_command(Scsi_Cmnd*, struct us_data*);
extern void usb_stor_qic157_command(Scsi_Cmnd*, struct us_data*);
......
/* Driver for USB Mass Storage compliant devices
* SCSI layer glue code
*
* $Id: scsiglue.c,v 1.19 2000/11/13 22:28:55 mdharm Exp $
* $Id: scsiglue.c,v 1.21 2001/07/29 23:41:52 mdharm Exp $
*
* Current development and maintenance by:
* (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
......@@ -49,7 +49,7 @@
#include "debug.h"
#include "transport.h"
#include <linux/slab.h>
#include <linux/malloc.h>
/*
* kernel thread actions
......@@ -122,8 +122,9 @@ static int release(struct Scsi_Host *psh)
*/
US_DEBUGP("-- sending US_ACT_EXIT command to thread\n");
us->action = US_ACT_EXIT;
wake_up(&(us->wqh));
down(&(us->notify));
up(&(us->sema));
wait_for_completion(&(us->notify));
/* remove the pointer to the data structure we were using */
(struct us_data*)psh->hostdata[0] = NULL;
......@@ -160,7 +161,7 @@ static int queuecommand( Scsi_Cmnd *srb , void (*done)(Scsi_Cmnd *))
up(&(us->queue_exclusion));
/* wake up the process task */
wake_up(&(us->wqh));
up(&(us->sema));
return 0;
}
......@@ -194,7 +195,7 @@ static int command_abort( Scsi_Cmnd *srb )
usb_unlink_urb(us->current_urb);
/* wait for us to be done */
down(&(us->notify));
wait_for_completion(&(us->notify));
return SUCCESS;
}
......@@ -248,7 +249,7 @@ static int bus_reset( Scsi_Cmnd *srb )
for (i = 0; i < us->pusb_dev->actconfig->bNumInterfaces; i++) {
struct usb_interface *intf =
&us->pusb_dev->actconfig->interface[i];
const struct usb_device_id *id;
struct usb_device_id *id;
/* if this is an unclaimed interface, skip it */
if (!intf->driver) {
......@@ -331,7 +332,7 @@ static int proc_info (char *buffer, char **start, off_t offset, int length,
return -ESRCH;
}
/* print the controler name */
/* print the controller name */
SPRINTF(" Host scsi%d: usb-storage\n", hostno);
/* print product, vendor, and serial number strings */
......
/* Driver for SanDisk SDDR-09 SmartMedia reader
*
* $Id: sddr09.c,v 1.14 2000/11/21 02:58:26 mdharm Exp $
* $Id: sddr09.c,v 1.18 2001/06/11 02:54:25 mdharm Exp $
*
* SDDR09 driver v0.1:
*
* First release
*
* Current development and maintenance by:
* (c) 2000 Robert Baruch (autophile@dol.net)
* (c) 2000, 2001 Robert Baruch (autophile@starband.net)
*
* The SanDisk SDDR-09 SmartMedia reader uses the Shuttle EUSB-01 chip.
* This chip is a programmable USB controller. In the SDDR-09, it has
......@@ -583,6 +583,10 @@ unsigned long sddr09_get_capacity(struct us_data *us,
*blocksize = 32;
return 0x04000000;
case 0x79: // 128MB
*blocksize = 32;
return 0x08000000;
default: // unknown
return 0;
......@@ -691,10 +695,17 @@ int sddr09_read_map(struct us_data *us) {
for (i=0; i<numblocks; i++) {
ptr = sg[i>>11].address+(i<<6);
if (ptr[0]!=0xFF || ptr[1]!=0xFF || ptr[2]!=0xFF ||
ptr[3]!=0xFF || ptr[4]!=0xFF || ptr[5]!=0xFF)
ptr[3]!=0xFF || ptr[4]!=0xFF || ptr[5]!=0xFF) {
US_DEBUGP("PBA %04X has no logical mapping: reserved area = "
"%02X%02X%02X%02X data status %02X block status %02X\n",
i, ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5]);
continue;
if ((ptr[6]>>4)!=0x01)
}
if ((ptr[6]>>4)!=0x01) {
US_DEBUGP("PBA %04X has invalid address field %02X%02X/%02X%02X\n",
i, ptr[6], ptr[7], ptr[11], ptr[12]);
continue;
}
/* ensure even parity */
......@@ -711,21 +722,23 @@ int sddr09_read_map(struct us_data *us) {
lba = (lba&0x07FF)>>1;
/* Every 1024 physical blocks, the LBA numbers
/* Every 1024 physical blocks ("zone"), the LBA numbers
* go back to zero, but are within a higher
* block of LBA's. In other words, in blocks
* 1024-2047 you will find LBA 0-1023 which are
* really LBA 1024-2047.
* 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.
*/
lba += (i&~0x3FF);
lba += 1000*(i/0x400);
if (lba>=numblocks) {
US_DEBUGP("Bad LBA %04X for block %04X\n", lba, i);
continue;
}
if (lba<0x10)
if (lba<0x10 || (lba>=0x3E0 && lba<0x3EF))
US_DEBUGP("LBA %04X <-> PBA %04X\n", lba, i);
info->pba_to_lba[i] = lba;
......@@ -812,8 +825,10 @@ int sddr09_transport(Scsi_Cmnd *srb, struct us_data *us)
unsigned char inquiry_response[36] = {
0x00, 0x80, 0x00, 0x02, 0x1F, 0x00, 0x00, 0x00
};
unsigned char mode_page_01[4] = { // write-protected for now
0x03, 0x00, 0x80, 0x00
unsigned char mode_page_01[16] = { // write-protected for now
0x03, 0x00, 0x80, 0x00,
0x01, 0x0A,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
unsigned char *ptr;
unsigned long capacity;
......@@ -890,10 +905,29 @@ int sddr09_transport(Scsi_Cmnd *srb, struct us_data *us)
// be a check for write-protect here
if ( (srb->cmnd[2] & 0x3F) == 0x01 ) {
if (ptr==NULL || srb->request_bufflen<4)
US_DEBUGP(
"SDDR09: Dummy up request for mode page 1\n");
if (ptr==NULL ||
srb->request_bufflen<sizeof(mode_page_01))
return USB_STOR_TRANSPORT_ERROR;
memcpy(ptr, mode_page_01, sizeof(mode_page_01));
return USB_STOR_TRANSPORT_GOOD;
} else if ( (srb->cmnd[2] & 0x3F) == 0x3F ) {
US_DEBUGP(
"SDDR09: Dummy up request for all mode pages\n");
if (ptr==NULL ||
srb->request_bufflen<sizeof(mode_page_01))
return USB_STOR_TRANSPORT_ERROR;
memcpy(ptr, mode_page_01, sizeof(mode_page_01));
return USB_STOR_TRANSPORT_GOOD;
}
// FIXME: sense buffer?
......@@ -901,6 +935,17 @@ int sddr09_transport(Scsi_Cmnd *srb, struct us_data *us)
return USB_STOR_TRANSPORT_ERROR;
}
if (srb->cmnd[0] == ALLOW_MEDIUM_REMOVAL) {
US_DEBUGP(
"SDDR09: %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) {
page = short_pack(srb->cmnd[3], srb->cmnd[2]);
......@@ -919,6 +964,10 @@ int sddr09_transport(Scsi_Cmnd *srb, struct us_data *us)
(info->capacity >>
(info->pageshift + info->blockshift) ) ) {
US_DEBUGP("Error: Requested LBA %04X exceeds maximum "
"block %04X\n", lba,
(info->capacity >> (info->pageshift + info->blockshift))-1);
// FIXME: sense buffer?
return USB_STOR_TRANSPORT_ERROR;
......@@ -934,6 +983,9 @@ int sddr09_transport(Scsi_Cmnd *srb, struct us_data *us)
// FIXME: sense buffer?
US_DEBUGP("Error: Requested LBA %04X has no physical block "
"mapping.\n", lba);
return USB_STOR_TRANSPORT_ERROR;
}
......
/* Driver for SCM Microsystems USB-ATAPI cable
*
* $Id: shuttle_usbat.c,v 1.11 2000/11/13 22:29:36 mdharm Exp $
* $Id: shuttle_usbat.c,v 1.14 2001/03/28 01:02:06 groovyjava Exp $
*
* Current development and maintenance by:
* (c) 2000 Robert Baruch (autophile@dol.net)
* (c) 2000, 2001 Robert Baruch (autophile@starband.net)
*
* Many originally ATAPI devices were slightly modified to meet the USB
* market by using some kind of translation from ATAPI to USB on the host,
......@@ -18,8 +18,8 @@
* as well. This driver is only guaranteed to work with the ATAPI
* translation.
*
* The only peripheral that I know of (as of 8 Sep 2000) that uses this
* device is the Hewlett-Packard 8200e/8210e CD-Writer Plus.
* The only peripheral that I know of (as of 27 Mar 2001) that uses this
* device is the Hewlett-Packard 8200e/8210e/8230e CD-Writer Plus.
*
* 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
......@@ -344,12 +344,14 @@ int usbat_wait_not_busy(struct us_data *us, int minutes) {
if (result!=USB_STOR_TRANSPORT_GOOD)
return result;
if (status&0x01) // check condition
if (status&0x01) { // check condition
result = usbat_read(us, USBAT_ATA, 0x10, &status);
return USB_STOR_TRANSPORT_FAILED;
}
if (status&0x20) // device fault
return USB_STOR_TRANSPORT_FAILED;
if ((status&0x80)!=0x80) { // not busy
if ((status&0x80)==0x00) { // not busy
US_DEBUGP("Waited not busy for %d steps\n", i);
return USB_STOR_TRANSPORT_GOOD;
}
......@@ -972,6 +974,9 @@ int hp8200e_transport(Scsi_Cmnd *srb, struct us_data *us)
data[i] = (i-7 >= srb->cmd_len) ? 0 : srb->cmnd[i-7];
}
result = usbat_read(us, USBAT_ATA, 0x17, &status);
US_DEBUGP("Status = %02X\n", status);
if (srb->cmnd[0] == TEST_UNIT_READY)
transferred = 0;
......
/* Driver for USB Mass Storage compliant devices
*
* $Id: transport.c,v 1.38 2000/11/21 00:52:10 mdharm Exp $
* $Id: transport.c,v 1.39 2001/03/10 16:46:28 zagor Exp $
*
* Current development and maintenance by:
* (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
......@@ -52,7 +52,7 @@
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/malloc.h>
/***********************************************************************
* Helper routines
......@@ -342,7 +342,7 @@ return len;
* the device -- this is because some devices crash their internal firmware
* when the status is requested after a halt
*/
static int clear_halt(struct usb_device *dev, int pipe)
int usb_stor_clear_halt(struct usb_device *dev, int pipe)
{
int result;
int endp = usb_pipeendpoint(pipe) | (usb_pipein(pipe) << 7);
......@@ -536,7 +536,7 @@ int usb_stor_transfer_partial(struct us_data *us, char *buf, int length)
/* if we stall, we need to clear it before we go on */
if (result == -EPIPE) {
US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
clear_halt(us->pusb_dev, pipe);
usb_stor_clear_halt(us->pusb_dev, pipe);
}
/* did we send all the data? */
......@@ -577,7 +577,7 @@ int usb_stor_transfer_partial(struct us_data *us, char *buf, int length)
* function simply determines if we're going to use scatter-gather or not,
* and acts appropriately. For now, it also re-interprets the error codes.
*/
static void us_transfer(Scsi_Cmnd *srb, struct us_data* us)
void usb_stor_transfer(Scsi_Cmnd *srb, struct us_data* us)
{
int i;
int result = -1;
......@@ -635,7 +635,7 @@ static void us_transfer(Scsi_Cmnd *srb, struct us_data* us)
/* Invoke the transport and basic error-handling/recovery methods
*
* This is used by the protocol layers to actually send the message to
* the device and receive the response.
* the device and recieve the response.
*/
void usb_stor_invoke_transport(Scsi_Cmnd *srb, struct us_data *us)
{
......@@ -821,7 +821,7 @@ void usb_stor_CBI_irq(struct urb *urb)
{
struct us_data *us = (struct us_data *)urb->context;
US_DEBUGP("USB IRQ received for device on host %d\n", us->host_no);
US_DEBUGP("USB IRQ recieved for device on host %d\n", us->host_no);
US_DEBUGP("-- IRQ data length is %d\n", urb->actual_length);
US_DEBUGP("-- IRQ state is %d\n", urb->status);
US_DEBUGP("-- Interrupt Status (0x%x, 0x%x)\n",
......@@ -895,10 +895,10 @@ int usb_stor_CBI_transport(Scsi_Cmnd *srb, struct us_data *us)
/* STALL must be cleared when they are detected */
if (result == -EPIPE) {
US_DEBUGP("-- Stall on control pipe. Clearing\n");
result = clear_halt(us->pusb_dev,
result = usb_stor_clear_halt(us->pusb_dev,
usb_sndctrlpipe(us->pusb_dev,
0));
US_DEBUGP("-- clear_halt() returns %d\n", result);
US_DEBUGP("-- usb_stor_clear_halt() returns %d\n", result);
return USB_STOR_TRANSPORT_FAILED;
}
......@@ -909,7 +909,7 @@ int usb_stor_CBI_transport(Scsi_Cmnd *srb, struct us_data *us)
/* DATA STAGE */
/* transfer the data payload for this command, if one exists*/
if (usb_stor_transfer_length(srb)) {
us_transfer(srb, us);
usb_stor_transfer(srb, us);
US_DEBUGP("CBI data stage result is 0x%x\n", srb->result);
/* if it was aborted, we need to indicate that */
......@@ -999,10 +999,10 @@ int usb_stor_CB_transport(Scsi_Cmnd *srb, struct us_data *us)
/* a stall is a fatal condition from the device */
if (result == -EPIPE) {
US_DEBUGP("-- Stall on control pipe. Clearing\n");
result = clear_halt(us->pusb_dev,
result = usb_stor_clear_halt(us->pusb_dev,
usb_sndctrlpipe(us->pusb_dev,
0));
US_DEBUGP("-- clear_halt() returns %d\n", result);
US_DEBUGP("-- usb_stor_clear_halt() returns %d\n", result);
return USB_STOR_TRANSPORT_FAILED;
}
......@@ -1013,7 +1013,7 @@ int usb_stor_CB_transport(Scsi_Cmnd *srb, struct us_data *us)
/* DATA STAGE */
/* transfer the data payload for this command, if one exists*/
if (usb_stor_transfer_length(srb)) {
us_transfer(srb, us);
usb_stor_transfer(srb, us);
US_DEBUGP("CB data stage result is 0x%x\n", srb->result);
/* if it was aborted, we need to indicate that */
......@@ -1057,7 +1057,7 @@ int usb_stor_Bulk_max_lun(struct us_data *us)
/* if we get a STALL, clear the stall */
if (result == -EPIPE) {
US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
clear_halt(us->pusb_dev, pipe);
usb_stor_clear_halt(us->pusb_dev, pipe);
}
/* return the default -- no LUNs */
......@@ -1111,17 +1111,17 @@ int usb_stor_Bulk_transport(Scsi_Cmnd *srb, struct us_data *us)
/* if we stall, we need to clear it before we go on */
if (result == -EPIPE) {
US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
clear_halt(us->pusb_dev, pipe);
usb_stor_clear_halt(us->pusb_dev, pipe);
} else if (result) {
/* unknown error -- we've got a problem */
return USB_STOR_TRANSPORT_ERROR;
}
/* if the command transferred well, then we go to the data stage */
/* if the command transfered well, then we go to the data stage */
if (result == 0) {
/* send/receive data payload, if there is any */
if (bcb.DataTransferLength) {
us_transfer(srb, us);
usb_stor_transfer(srb, us);
US_DEBUGP("Bulk data transfer result 0x%x\n",
srb->result);
......@@ -1150,7 +1150,7 @@ int usb_stor_Bulk_transport(Scsi_Cmnd *srb, struct us_data *us)
/* did the attempt to read the CSW fail? */
if (result == -EPIPE) {
US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
clear_halt(us->pusb_dev, pipe);
usb_stor_clear_halt(us->pusb_dev, pipe);
/* get the status again */
US_DEBUGP("Attempting to get CSW (2nd try)...\n");
......@@ -1164,7 +1164,7 @@ int usb_stor_Bulk_transport(Scsi_Cmnd *srb, struct us_data *us)
/* if it fails again, we need a reset and return an error*/
if (result == -EPIPE) {
US_DEBUGP("clearing halt for pipe 0x%x\n", pipe);
clear_halt(us->pusb_dev, pipe);
usb_stor_clear_halt(us->pusb_dev, pipe);
return USB_STOR_TRANSPORT_ERROR;
}
}
......@@ -1243,10 +1243,10 @@ int usb_stor_CB_reset(struct us_data *us)
set_current_state(TASK_RUNNING);
US_DEBUGP("CB_reset: clearing endpoint halt\n");
clear_halt(us->pusb_dev,
usb_rcvbulkpipe(us->pusb_dev, us->ep_in));
clear_halt(us->pusb_dev,
usb_rcvbulkpipe(us->pusb_dev, us->ep_out));
usb_stor_clear_halt(us->pusb_dev,
usb_rcvbulkpipe(us->pusb_dev, us->ep_in));
usb_stor_clear_halt(us->pusb_dev,
usb_rcvbulkpipe(us->pusb_dev, us->ep_out));
US_DEBUGP("CB_reset done\n");
/* return a result code based on the result of the control message */
......@@ -1282,10 +1282,10 @@ int usb_stor_Bulk_reset(struct us_data *us)
schedule_timeout(HZ*6);
set_current_state(TASK_RUNNING);
clear_halt(us->pusb_dev,
usb_rcvbulkpipe(us->pusb_dev, us->ep_in));
clear_halt(us->pusb_dev,
usb_sndbulkpipe(us->pusb_dev, us->ep_out));
usb_stor_clear_halt(us->pusb_dev,
usb_rcvbulkpipe(us->pusb_dev, us->ep_in));
usb_stor_clear_halt(us->pusb_dev,
usb_sndbulkpipe(us->pusb_dev, us->ep_out));
US_DEBUGP("Bulk soft reset completed\n");
return SUCCESS;
}
/* Driver for USB Mass Storage compliant devices
* Transport Functions Header File
*
* $Id: transport.h,v 1.13 2000/10/03 01:06:07 mdharm Exp $
* $Id: transport.h,v 1.15 2001/03/17 20:06:23 jrmayfield Exp $
*
* Current development and maintenance by:
* (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
......@@ -64,6 +64,14 @@
#define US_PR_FREECOM 0xf1 /* Freecom */
#endif
#ifdef CONFIG_USB_STORAGE_DATAFAB
#define US_PR_DATAFAB 0xf2 /* Datafab chipsets */
#endif
#ifdef CONFIG_USB_STORAGE_JUMPSHOT
#define US_PR_JUMPSHOT 0xf3 /* Lexar Jumpshot */
#endif
/*
* Bulk only data structures
*/
......@@ -107,7 +115,7 @@ struct bulk_cs_wrap {
* us_bulk_transfer() return codes
*/
#define US_BULK_TRANSFER_GOOD 0 /* good transfer */
#define US_BULK_TRANSFER_SHORT 1 /* transferred less than expected */
#define US_BULK_TRANSFER_SHORT 1 /* transfered less than expected */
#define US_BULK_TRANSFER_FAILED 2 /* transfer died in the middle */
#define US_BULK_TRANSFER_ABORTED 3 /* transfer canceled */
......@@ -143,4 +151,6 @@ extern int usb_stor_bulk_msg(struct us_data*, void*, int, unsigned int,
unsigned int*);
extern int usb_stor_control_msg(struct us_data*, unsigned int, u8, u8,
u16, u16, void*, u16);
extern void usb_stor_transfer(Scsi_Cmnd*, struct us_data*);
extern int usb_stor_clear_halt(struct usb_device*, int );
#endif
/* Driver for USB Mass Storage compliant devices
* Ununsual Devices File
*
* $Id: unusual_devs.h,v 1.1 2000/12/05 05:38:31 mdharm Exp $
* $Id: unusual_devs.h,v 1.16 2001/07/30 00:27:59 mdharm Exp $
*
* Current development and maintenance by:
* (c) 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
......@@ -42,6 +42,12 @@ UNUSUAL_DEV( 0x03ee, 0x0000, 0x0000, 0x0245,
"CD-R/RW Drive",
US_SC_8020, US_PR_CBI, NULL, 0),
UNUSUAL_DEV( 0x03ee, 0x6901, 0x0000, 0x0100,
"Mitsumi",
"USB FDD",
US_SC_UFI, US_PR_CBI, NULL,
US_FL_SINGLE_LUN ),
UNUSUAL_DEV( 0x03f0, 0x0107, 0x0200, 0x0200,
"HP",
"CD-Writer+",
......@@ -54,6 +60,22 @@ UNUSUAL_DEV( 0x03f0, 0x0207, 0x0001, 0x0001,
US_SC_8070, US_PR_SCM_ATAPI, init_8200e, 0),
#endif
#ifdef CONFIG_USB_STORAGE_DPCM
UNUSUAL_DEV( 0x0436, 0x0005, 0x0100, 0x0100,
"Microtech",
"CameraMate (DPCM_USB)",
US_SC_SCSI, US_PR_DPCM_USB, NULL,
US_FL_START_STOP ),
#endif
UNUSUAL_DEV( 0x04cb, 0x0100, 0x0000, 0x2210,
"Fujifilm",
"FinePix 1400Zoom",
US_SC_8070, US_PR_CBI, NULL, US_FL_FIX_INQUIRY),
/* Most of the following entries were developed with the help of
* Shuttle/SCM directly.
*/
UNUSUAL_DEV( 0x04e6, 0x0001, 0x0200, 0x0200,
"Matshita",
"LS-120",
......@@ -73,21 +95,14 @@ UNUSUAL_DEV( 0x04e6, 0x0003, 0x0000, 0x9999,
US_FL_SINGLE_LUN | US_FL_START_STOP ),
#endif
#ifdef CONFIG_USB_STORAGE_DPCM
UNUSUAL_DEV( 0x0436, 0x0005, 0x0100, 0x0100,
"Microtech",
"CameraMate (DPCM_USB)",
US_SC_SCSI, US_PR_DPCM_USB, NULL,
US_FL_START_STOP ),
UNUSUAL_DEV( 0x04e6, 0x0005, 0x0100, 0x0208,
"SCM Microsystems Inc",
/* This entry is from Andries.Brouwer@cwi.nl */
UNUSUAL_DEV( 0x04e6, 0x0005, 0x0100, 0x0205,
"SCM Microsystems",
"eUSB SmartMedia / CompactFlash Adapter",
US_SC_SCSI, US_PR_DPCM_USB, NULL,
US_FL_START_STOP ),
#endif
US_SC_SCSI, US_PR_DPCM_USB, NULL,
US_FL_START_STOP),
UNUSUAL_DEV( 0x04e6, 0x0006, 0x0100, 0x0200,
UNUSUAL_DEV( 0x04e6, 0x0006, 0x0100, 0x0205,
"Shuttle",
"eUSB MMC Adapter",
US_SC_SCSI, US_PR_CB, NULL,
......@@ -126,9 +141,17 @@ UNUSUAL_DEV( 0x04e6, 0x0101, 0x0200, 0x0200,
"CD-RW Device",
US_SC_8020, US_PR_CB, NULL, 0),
UNUSUAL_DEV( 0x054c, 0x0010, 0x0106, 0x0210,
/* Reported by Bob Sass <rls@vectordb.com> -- only rev 1.33 tested */
UNUSUAL_DEV( 0x050d, 0x0115, 0x0133, 0x0133,
"Belkin",
"USB SCSI Adaptor",
US_SC_SCSI, US_PR_BULK, usb_stor_euscsi_init,
US_FL_SCM_MULT_TARG ),
/* This entry is needed because the device reports Sub=ff */
UNUSUAL_DEV( 0x054c, 0x0010, 0x0106, 0x0322,
"Sony",
"DSC-S30/S70/505V/F505",
"DSC-S30/S70/S75/505V/F505",
US_SC_SCSI, US_PR_CB, NULL,
US_FL_SINGLE_LUN | US_FL_START_STOP | US_FL_MODE_XLATE ),
......@@ -138,6 +161,19 @@ UNUSUAL_DEV( 0x054c, 0x002d, 0x0100, 0x0100,
US_SC_UFI, US_PR_CB, NULL,
US_FL_SINGLE_LUN | US_FL_START_STOP ),
/* Submitted by Klaus Mueller <k.mueller@intership.de> */
UNUSUAL_DEV( 0x054c, 0x002e, 0x0106, 0x0310,
"Sony",
"Handycam",
US_SC_SCSI, US_PR_CB, NULL,
US_FL_SINGLE_LUN | US_FL_START_STOP | US_FL_MODE_XLATE),
UNUSUAL_DEV( 0x054c, 0x0032, 0x0000, 0x9999,
"Sony",
"Memorystick MSC-U01N",
US_SC_UFI, US_PR_CB, NULL,
US_FL_SINGLE_LUN | US_FL_START_STOP ),
UNUSUAL_DEV( 0x057b, 0x0000, 0x0000, 0x0299,
"Y-E Data",
"Flashbuster-U",
......@@ -155,10 +191,51 @@ UNUSUAL_DEV( 0x059f, 0xa601, 0x0200, 0x0200,
"USB Hard Disk",
US_SC_RBC, US_PR_CB, NULL, 0 ),
UNUSUAL_DEV( 0x05ab, 0x0031, 0x0100, 0x0100,
"In-System",
"USB/IDE Bridge (ATAPI ONLY!)",
US_SC_8070, US_PR_BULK, NULL, 0 ),
#ifdef CONFIG_USB_STORAGE_ISD200
UNUSUAL_DEV( 0x05ab, 0x0031, 0x0100, 0x0110,
"In-System",
"USB/IDE Bridge (ATA/ATAPI)",
US_SC_ISD200, US_PR_BULK, isd200_Initialization,
0 ),
UNUSUAL_DEV( 0x05ab, 0x0060, 0x0100, 0x0110,
"In-System",
"USB 2.0/IDE Bridge (ATA/ATAPI)",
US_SC_ISD200, US_PR_BULK, isd200_Initialization,
0 ),
UNUSUAL_DEV( 0x05ab, 0x0301, 0x0100, 0x0110,
"In-System",
"Portable USB Harddrive V2",
US_SC_ISD200, US_PR_BULK, isd200_Initialization,
0 ),
UNUSUAL_DEV( 0x05ab, 0x0351, 0x0100, 0x0110,
"In-System",
"Portable USB Harddrive V2",
US_SC_ISD200, US_PR_BULK, isd200_Initialization,
0 ),
UNUSUAL_DEV( 0x05ab, 0x5701, 0x0100, 0x0110,
"In-System",
"USB Storage Adapter V2",
US_SC_ISD200, US_PR_BULK, isd200_Initialization,
0 ),
UNUSUAL_DEV( 0x054c, 0x002b, 0x0100, 0x0110,
"Sony",
"Portable USB Harddrive V2",
US_SC_ISD200, US_PR_BULK, isd200_Initialization,
0 ),
#endif
#ifdef CONFIG_USB_STORAGE_JUMPSHOT
UNUSUAL_DEV( 0x05dc, 0x0001, 0x0000, 0x0001,
"Lexar",
"Jumpshot USB CF Reader",
US_SC_SCSI, US_PR_JUMPSHOT, NULL,
US_FL_MODE_XLATE | US_FL_START_STOP ),
#endif
UNUSUAL_DEV( 0x0644, 0x0000, 0x0100, 0x0100,
"TEAC",
......@@ -189,25 +266,32 @@ UNUSUAL_DEV( 0x0781, 0x0001, 0x0200, 0x0200,
US_SC_SCSI, US_PR_CB, NULL,
US_FL_SINGLE_LUN | US_FL_START_STOP),
UNUSUAL_DEV( 0x0781, 0x0100, 0x0100, 0x0100,
UNUSUAL_DEV( 0x0781, 0x0002, 0x0009, 0x0009,
"Sandisk",
"ImageMate SDDR-31",
US_SC_SCSI, US_PR_BULK, NULL,
US_FL_IGNORE_SER),
UNUSUAL_DEV( 0x0781, 0x0100, 0x0100, 0x0100,
"Sandisk",
"ImageMate SDDR-12",
US_SC_SCSI, US_PR_CB, NULL,
US_FL_SINGLE_LUN ),
#ifdef CONFIG_USB_STORAGE_SDDR09
UNUSUAL_DEV( 0x0781, 0x0200, 0x0100, 0x0100,
UNUSUAL_DEV( 0x0781, 0x0200, 0x0000, 0x9999,
"Sandisk",
"ImageMate SDDR-09",
US_SC_SCSI, US_PR_EUSB_SDDR09, NULL,
US_FL_SINGLE_LUN | US_FL_START_STOP ),
#endif
UNUSUAL_DEV( 0x0781, 0x0002, 0x0009, 0x0009,
"Sandisk",
"ImageMate SDDR-31",
US_SC_SCSI, US_PR_BULK, NULL,
US_FL_IGNORE_SER),
#ifdef CONFIG_USB_STORAGE_FREECOM
UNUSUAL_DEV( 0x07ab, 0xfc01, 0x0000, 0x9999,
"Freecom",
"USB-IDE",
US_SC_QIC, US_PR_FREECOM, freecom_init, 0),
#endif
UNUSUAL_DEV( 0x07af, 0x0004, 0x0100, 0x0100,
"Microtech",
......@@ -215,13 +299,6 @@ UNUSUAL_DEV( 0x07af, 0x0004, 0x0100, 0x0100,
US_SC_SCSI, US_PR_BULK, usb_stor_euscsi_init,
US_FL_SCM_MULT_TARG ),
#ifdef CONFIG_USB_STORAGE_FREECOM
UNUSUAL_DEV( 0x07ab, 0xfc01, 0x0000, 0x9999,
"Freecom",
"USB-IDE",
US_SC_QIC, US_PR_FREECOM, freecom_init, 0),
#endif
UNUSUAL_DEV( 0x07af, 0x0005, 0x0100, 0x0100,
"Microtech",
"USB-SCSI-HD50",
......@@ -236,3 +313,78 @@ UNUSUAL_DEV( 0x07af, 0x0006, 0x0100, 0x0100,
US_FL_START_STOP ),
#endif
#ifdef CONFIG_USB_STORAGE_DATAFAB
UNUSUAL_DEV( 0x07c4, 0xa000, 0x0000, 0x0015,
"Datafab",
"MDCFE-B USB CF Reader",
US_SC_SCSI, US_PR_DATAFAB, NULL,
US_FL_MODE_XLATE | US_FL_START_STOP ),
/*
* The following Datafab-based devices may or may not work
* using the current driver...the 0xffff is arbitrary since I
* don't know what device versions exist for these guys.
*
* The 0xa003 and 0xa004 devices in particular I'm curious about.
* I'm told they exist but so far nobody has come forward to say that
* they work with this driver. Given the success we've had getting
* other Datafab-based cards operational with this driver, I've decided
* to leave these two devices in the list.
*/
UNUSUAL_DEV( 0x07c4, 0xa001, 0x0000, 0xffff,
"SIIG/Datafab",
"SIIG/Datafab Memory Stick+CF Reader/Writer",
US_SC_SCSI, US_PR_DATAFAB, NULL,
US_FL_MODE_XLATE | US_FL_START_STOP ),
UNUSUAL_DEV( 0x07c4, 0xa003, 0x0000, 0xffff,
"Datafab/Unknown",
"Datafab-based Reader",
US_SC_SCSI, US_PR_DATAFAB, NULL,
US_FL_MODE_XLATE | US_FL_START_STOP ),
UNUSUAL_DEV( 0x07c4, 0xa004, 0x0000, 0xffff,
"Datafab/Unknown",
"Datafab-based Reader",
US_SC_SCSI, US_PR_DATAFAB, NULL,
US_FL_MODE_XLATE | US_FL_START_STOP ),
UNUSUAL_DEV( 0x07c4, 0xa005, 0x0000, 0xffff,
"PNY/Datafab",
"PNY/Datafab CF+SM Reader",
US_SC_SCSI, US_PR_DATAFAB, NULL,
US_FL_MODE_XLATE | US_FL_START_STOP ),
UNUSUAL_DEV( 0x07c4, 0xa006, 0x0000, 0xffff,
"Simple Tech/Datafab",
"Simple Tech/Datafab CF+SM Reader",
US_SC_SCSI, US_PR_DATAFAB, NULL,
US_FL_MODE_XLATE | US_FL_START_STOP ),
#endif
/* Casio QV 2x00/3x00/8000 digital still cameras are not conformant
* to the USB storage specification in two ways:
* - They tell us they are using transport protocol CBI. In reality they
* are using transport protocol CB.
* - They don't like the INQUIRY command. So we must handle this command
* of the SCSI layer ourselves.
*/
UNUSUAL_DEV( 0x07cf, 0x1001, 0x9009, 0x9009,
"Casio",
"QV DigitalCamera",
US_SC_8070, US_PR_CB, NULL,
US_FL_FIX_INQUIRY ),
UNUSUAL_DEV( 0x097a, 0x0001, 0x0000, 0x0001,
"Minds@Work",
"Digital Wallet",
US_SC_SCSI, US_PR_CB, NULL,
US_FL_MODE_XLATE ),
#ifdef CONFIG_USB_STORAGE_ISD200
UNUSUAL_DEV( 0x0bf6, 0xa001, 0x0100, 0x0110,
"ATI",
"USB Cable 205",
US_SC_ISD200, US_PR_BULK, isd200_Initialization,
0 ),
#endif
/* Driver for USB Mass Storage compliant devices
*
* $Id: usb.c,v 1.61 2001/01/13 00:10:59 mdharm Exp $
* $Id: usb.c,v 1.67 2001/07/29 23:41:52 mdharm Exp $
*
* Current development and maintenance by:
* (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
......@@ -53,6 +53,7 @@
#include "protocol.h"
#include "debug.h"
#include "initializers.h"
#ifdef CONFIG_USB_STORAGE_HP8200e
#include "shuttle_usbat.h"
#endif
......@@ -65,12 +66,22 @@
#ifdef CONFIG_USB_STORAGE_FREECOM
#include "freecom.h"
#endif
#ifdef CONFIG_USB_STORAGE_ISD200
#include "isd200.h"
#endif
#ifdef CONFIG_USB_STORAGE_DATAFAB
#include "datafab.h"
#endif
#ifdef CONFIG_USB_STORAGE_JUMPSHOT
#include "jumpshot.h"
#endif
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/malloc.h>
/* Some informational data */
MODULE_AUTHOR("Matthew Dharm <mdharm-usb@one-eyed-alien.net>");
......@@ -151,7 +162,7 @@ MODULE_DEVICE_TABLE (usb, storage_usb_ids);
/* The vendor name should be kept at eight characters or less, and
* the product name should be kept at 16 characters or less. If a device
* has the US_FL_DUMMY_INQUIRY flag, then the vendor and product names
* has the US_FL_FIX_INQUIRY flag, then the vendor and product names
* normally generated by a device thorugh the INQUIRY response will be
* taken from this list, and this is the reason for the above size
* restriction. However, if the flag is not present, then you
......@@ -281,7 +292,6 @@ void fill_inquiry_response(struct us_data *us, unsigned char *data,
static int usb_stor_control_thread(void * __us)
{
wait_queue_t wait;
struct us_data *us = (struct us_data *)__us;
int action;
......@@ -302,17 +312,17 @@ static int usb_stor_control_thread(void * __us)
unlock_kernel();
/* set up for wakeups by new commands */
init_waitqueue_entry(&wait, current);
init_waitqueue_head(&(us->wqh));
add_wait_queue(&(us->wqh), &wait);
init_MUTEX_LOCKED(&us->sema);
/* signal that we've started the thread */
up(&(us->notify));
complete(&(us->notify));
set_current_state(TASK_INTERRUPTIBLE);
for(;;) {
US_DEBUGP("*** thread sleeping.\n");
schedule();
if(down_interruptible(&us->sema))
break;
US_DEBUGP("*** thread awakened.\n");
/* lock access to the queue element */
......@@ -378,6 +388,24 @@ static int usb_stor_control_thread(void * __us)
break;
}
/* Handle those devices which need us to fake their
* inquiry data */
if ((us->srb->cmnd[0] == INQUIRY) &&
(us->flags & US_FL_FIX_INQUIRY)) {
unsigned char data_ptr[36] = {
0x00, 0x80, 0x02, 0x02,
0x1F, 0x00, 0x00, 0x00};
US_DEBUGP("Faking INQUIRY command\n");
fill_inquiry_response(us, data_ptr, 36);
us->srb->result = GOOD << 1;
set_current_state(TASK_INTERRUPTIBLE);
us->srb->scsi_done(us->srb);
us->srb = NULL;
break;
}
/* lock the device pointers */
down(&(us->dev_semaphore));
......@@ -417,7 +445,7 @@ static int usb_stor_control_thread(void * __us)
} else {
US_DEBUGP("scsi command aborted\n");
set_current_state(TASK_INTERRUPTIBLE);
up(&(us->notify));
complete(&(us->notify));
}
us->srb = NULL;
break;
......@@ -435,17 +463,16 @@ static int usb_stor_control_thread(void * __us)
/* exit if we get a signal to exit */
if (action == US_ACT_EXIT) {
US_DEBUGP("-- US_ACT_EXIT command recieved\n");
US_DEBUGP("-- US_ACT_EXIT command received\n");
break;
}
} /* for (;;) */
/* clean up after ourselves */
set_current_state(TASK_INTERRUPTIBLE);
remove_wait_queue(&(us->wqh), &wait);
/* notify the exit routine that we're actually exiting now */
up(&(us->notify));
complete(&(us->notify));
return 0;
}
......@@ -717,7 +744,7 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum,
}
/* Initialize the mutexes only when the struct is new */
init_MUTEX_LOCKED(&(ss->notify));
init_completion(&(ss->notify));
init_MUTEX_LOCKED(&(ss->ip_waitq));
init_MUTEX(&(ss->queue_exclusion));
init_MUTEX(&(ss->irq_urb_sem));
......@@ -831,6 +858,24 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum,
break;
#endif
#ifdef CONFIG_USB_STORAGE_DATAFAB
case US_PR_DATAFAB:
ss->transport_name = "Datafab Bulk-Only";
ss->transport = datafab_transport;
ss->transport_reset = usb_stor_Bulk_reset;
ss->max_lun = 1;
break;
#endif
#ifdef CONFIG_USB_STORAGE_JUMPSHOT
case US_PR_JUMPSHOT:
ss->transport_name = "Lexar Jumpshot Control/Bulk";
ss->transport = jumpshot_transport;
ss->transport_reset = usb_stor_Bulk_reset;
ss->max_lun = 1;
break;
#endif
default:
ss->transport_name = "Unknown";
kfree(ss->current_urb);
......@@ -879,6 +924,13 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum,
ss->proto_handler = usb_stor_ufi_command;
break;
#ifdef CONFIG_USB_STORAGE_ISD200
case US_SC_ISD200:
ss->protocol_name = "ISD200 ATA/ATAPI";
ss->proto_handler = isd200_ata_command;
break;
#endif
default:
ss->protocol_name = "Unknown";
kfree(ss->current_urb);
......@@ -907,7 +959,7 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum,
ss->host_number = my_host_number++;
/* We abuse this pointer so we can pass the ss pointer to
* the host controler thread in us_detect. But how else are
* the host controller thread in us_detect. But how else are
* we to do it?
*/
(struct us_data *)ss->htmplt.proc_dir = ss;
......@@ -930,7 +982,7 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum,
}
/* wait for the thread to start */
down(&(ss->notify));
wait_for_completion(&(ss->notify));
/* now register - our detect function will be called */
ss->htmplt.module = THIS_MODULE;
......
/* Driver for USB Mass Storage compliant devices
* Main Header File
*
* $Id: usb.h,v 1.12 2000/12/05 03:33:49 mdharm Exp $
* $Id: usb.h,v 1.18 2001/07/30 00:27:59 mdharm Exp $
*
* Current development and maintenance by:
* (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
......@@ -47,6 +47,7 @@
#include <linux/usb.h>
#include <linux/blk.h>
#include <linux/smp_lock.h>
#include <linux/completion.h>
#include "scsi.h"
#include "hosts.h"
......@@ -94,11 +95,12 @@ struct us_unusual_dev {
/* Flag definitions */
#define US_FL_SINGLE_LUN 0x00000001 /* allow access to only LUN 0 */
#define US_FL_MODE_XLATE 0x00000002 /* translate _6 to _10 comands for
#define US_FL_MODE_XLATE 0x00000002 /* translate _6 to _10 commands for
Win/MacOS compatibility */
#define US_FL_START_STOP 0x00000004 /* ignore START_STOP commands */
#define US_FL_IGNORE_SER 0x00000010 /* Ignore the serial number given */
#define US_FL_SCM_MULT_TARG 0x00000020 /* supports multiple targets */
#define US_FL_FIX_INQUIRY 0x00000040 /* INQUIRY response needs fixing */
#define USB_STOR_STRING_LEN 32
......@@ -165,11 +167,11 @@ struct us_data {
struct semaphore current_urb_sem; /* to protect irq_urb */
struct urb *current_urb; /* non-int USB requests */
/* the waitqueue for sleeping the control thread */
wait_queue_head_t wqh; /* to sleep thread on */
/* the semaphore for sleeping the control thread */
struct semaphore sema; /* to sleep thread on */
/* mutual exclusion structures */
struct semaphore notify; /* thread begin/end */
struct completion notify; /* thread begin/end */
struct semaphore queue_exclusion; /* to protect data structs */
struct us_unusual_dev *unusual_dev; /* If unusual device */
void *extra; /* Any extra data */
......
......@@ -28,8 +28,6 @@ do { \
unsigned long flags; \
\
__save_flags(flags); \
if (!(flags & (1 << 9))) \
BUG(); \
barrier(); \
if (!--*ptr) \
__asm__ __volatile__ ( \
......
......@@ -100,8 +100,6 @@ struct zone_t;
/* linux/mm/swap.c */
extern int memory_pressure;
extern void age_page_up(struct page *);
extern void age_page_up_nolock(struct page *);
extern void age_page_down(struct page *);
extern void age_page_down_nolock(struct page *);
extern void age_page_down_ageonly(struct page *);
......
......@@ -619,9 +619,9 @@ int lock_kiovec(int nr, struct kiobuf *iovec[], int wait)
if (TryLockPage(page)) {
while (j--) {
page = *(--ppage);
if (page)
UnlockPage(page);
struct page *tmp = *--ppage;
if (tmp)
UnlockPage(tmp);
}
goto retry;
}
......@@ -862,7 +862,7 @@ static inline void establish_pte(struct vm_area_struct * vma, unsigned long addr
/*
* We hold the mm semaphore for reading and vma->vm_mm->page_table_lock
*/
static inline void break_cow(struct vm_area_struct * vma, struct page * old_page, struct page * new_page, unsigned long address,
static inline void break_cow(struct vm_area_struct * vma, struct page * new_page, unsigned long address,
pte_t *page_table)
{
flush_page_to_ram(new_page);
......@@ -935,12 +935,14 @@ static int do_wp_page(struct mm_struct *mm, struct vm_area_struct * vma,
/*
* Ok, we need to copy. Oh, well..
*/
page_cache_get(old_page);
spin_unlock(&mm->page_table_lock);
new_page = alloc_page(GFP_HIGHUSER);
if (!new_page)
goto no_mem;
copy_cow_page(old_page,new_page,address);
page_cache_release(old_page);
/*
* Re-check the pte - we dropped the lock
......@@ -949,7 +951,7 @@ static int do_wp_page(struct mm_struct *mm, struct vm_area_struct * vma,
if (pte_same(*page_table, pte)) {
if (PageReserved(old_page))
++mm->rss;
break_cow(vma, old_page, new_page, address, page_table);
break_cow(vma, new_page, address, page_table);
/* Free the old page.. */
new_page = old_page;
......@@ -961,6 +963,7 @@ static int do_wp_page(struct mm_struct *mm, struct vm_area_struct * vma,
printk("do_wp_page: bogus page at address %08lx (page 0x%lx)\n",address,(unsigned long)old_page);
return -1;
no_mem:
page_cache_release(old_page);
spin_lock(&mm->page_table_lock);
return -1;
}
......
......@@ -74,31 +74,6 @@ pager_daemon_t pager_daemon = {
8, /* do swap I/O in clusters of this size */
};
/**
* age_page_{up,down} - page aging helper functions
* @page - the page we want to age
* @nolock - are we already holding the pagelist_lru_lock?
*
* If the page is on one of the lists (active, inactive_dirty or
* inactive_clean), we will grab the pagelist_lru_lock as needed.
* If you're already holding the lock, call this function with the
* nolock argument non-zero.
*/
void age_page_up_nolock(struct page * page)
{
/*
* We're dealing with an inactive page, move the page
* to the active list.
*/
if (!page->age)
activate_page_nolock(page);
/* The actual page aging bit */
page->age += PAGE_AGE_ADV;
if (page->age > PAGE_AGE_MAX)
page->age = PAGE_AGE_MAX;
}
/*
* We use this (minimal) function in the case where we
* know we can't deactivate the page (yet).
......@@ -121,21 +96,6 @@ void age_page_down_nolock(struct page * page)
deactivate_page_nolock(page);
}
void age_page_up(struct page * page)
{
/*
* We're dealing with an inactive page, move the page
* to the active list.
*/
if (!page->age)
activate_page(page);
/* The actual page aging bit */
page->age += PAGE_AGE_ADV;
if (page->age > PAGE_AGE_MAX)
page->age = PAGE_AGE_MAX;
}
void age_page_down(struct page * page)
{
/* The actual page aging bit */
......
......@@ -678,6 +678,13 @@ int page_launder(int gfp_mask, int sync)
return ret;
}
static inline void age_page_up(struct page *page)
{
unsigned age = page->age + PAGE_AGE_ADV;
if (age > PAGE_AGE_MAX)
age = PAGE_AGE_MAX;
page->age = age;
}
/**
......@@ -728,7 +735,7 @@ int refill_inactive_scan(zone_t *zone, unsigned int priority, int target)
/* Do aging on the pages. */
if (PageTestandClearReferenced(page)) {
age_page_up_nolock(page);
age_page_up(page);
page_active = 1;
} else {
age_page_down_ageonly(page);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment