patch-2.4.11-dontuse linux/drivers/usb/hpusbscsi.c
Next file: linux/drivers/usb/hpusbscsi.h
Previous file: linux/drivers/usb/dsbr100.c
Back to the patch index
Back to the overall index
- Lines: 520
- Date:
Tue Oct 9 15:15:02 2001
- Orig file:
v2.4.10/linux/drivers/usb/hpusbscsi.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.4.10/linux/drivers/usb/hpusbscsi.c linux/drivers/usb/hpusbscsi.c
@@ -0,0 +1,519 @@
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/smp_lock.h>
+#include <linux/usb.h>
+#include <asm/atomic.h>
+#include <linux/blk.h>
+#include "../scsi/scsi.h"
+#include "../scsi/hosts.h"
+#include "../scsi/sd.h"
+
+#include "hpusbscsi.h"
+
+#define DEBUG(x...) \
+ printk( KERN_DEBUG x )
+
+static char *states[]={"FREE", "BEGINNING", "WORKING", "ERROR", "WAIT", "PREMATURE"};
+
+#define TRACE_STATE printk(KERN_DEBUG"hpusbscsi->state = %s at line %d\n", states[hpusbscsi->state], __LINE__)
+
+/* global variables */
+
+struct list_head hpusbscsi_devices;
+//LIST_HEAD(hpusbscsi_devices);
+
+/* USB related parts */
+
+static void *
+hpusbscsi_usb_probe (struct usb_device *dev, unsigned int interface,
+ const struct usb_device_id *id)
+{
+ struct hpusbscsi *new;
+ struct usb_interface_descriptor *altsetting =
+ &(dev->actconfig->interface[interface].altsetting[0]);
+
+ int i, result;
+
+ /* basic check */
+
+ if (altsetting->bNumEndpoints != 3) {
+ printk (KERN_ERR "Wrong number of endpoints\n");
+ return NULL;
+ }
+
+ /* descriptor allocation */
+
+ new =
+ (struct hpusbscsi *) kmalloc (sizeof (struct hpusbscsi),
+ GFP_KERNEL);
+ if (new == NULL)
+ return NULL;
+ DEBUG ("Allocated memory\n");
+ memset (new, 0, sizeof (struct hpusbscsi));
+ spin_lock_init (&new->dataurb.lock);
+ spin_lock_init (&new->controlurb.lock);
+ new->dev = dev;
+ init_waitqueue_head (&new->pending);
+ init_waitqueue_head (&new->deathrow);
+ INIT_LIST_HEAD (&new->lh);
+
+
+
+ /* finding endpoints */
+
+ for (i = 0; i < altsetting->bNumEndpoints; i++) {
+ if (
+ (altsetting->endpoint[i].
+ bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+ USB_ENDPOINT_XFER_BULK) {
+ if (altsetting->endpoint[i].
+ bEndpointAddress & USB_DIR_IN) {
+ new->ep_in =
+ altsetting->endpoint[i].
+ bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK;
+ } else {
+ new->ep_out =
+ altsetting->endpoint[i].
+ bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK;
+ }
+ } else {
+ new->ep_int =
+ altsetting->endpoint[i].
+ bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+ new->interrupt_interval= altsetting->endpoint[i].bInterval;
+ }
+ }
+
+ /* USB initialisation magic for the simple case */
+
+ result = usb_set_interface (dev, altsetting->bInterfaceNumber, 0);
+
+ switch (result) {
+ case 0: /* no error */
+ break;
+
+ case -EPIPE:
+ usb_clear_halt (dev, usb_sndctrlpipe (dev, 0));
+ break;
+
+ default:
+ printk (KERN_ERR "unknown error %d from usb_set_interface\n",
+ result);
+ goto err_out;
+ }
+
+ /* making a template for the scsi layer to fake detection of a scsi device */
+
+ memcpy (&(new->ctempl), &hpusbscsi_scsi_host_template,
+ sizeof (hpusbscsi_scsi_host_template));
+ (struct hpusbscsi *) new->ctempl.proc_dir = new;
+ new->ctempl.module = THIS_MODULE;
+
+ if (scsi_register_module (MODULE_SCSI_HA, &(new->ctempl)))
+ goto err_out;
+
+ /* adding to list for module unload */
+ list_add (&hpusbscsi_devices, &new->lh);
+
+ return new;
+
+ err_out:
+ kfree (new);
+ return NULL;
+}
+
+static void
+hpusbscsi_usb_disconnect (struct usb_device *dev, void *ptr)
+{
+ usb_unlink_urb(&(((struct hpusbscsi *) ptr)->controlurb));
+ ((struct hpusbscsi *) ptr)->dev = NULL;
+}
+
+static struct usb_device_id hpusbscsi_usb_ids[] = {
+ {USB_DEVICE (0x03f0, 0x0701)}, /* HP 53xx */
+ {USB_DEVICE (0x03f0, 0x0801)}, /* HP 7400 */
+ {USB_DEVICE (0x0638, 0x026a)}, /*Scan Dual II */
+ {} /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, hpusbscsi_usb_ids);
+MODULE_LICENSE("GPL");
+
+
+static struct usb_driver hpusbscsi_usb_driver = {
+ name:"hpusbscsi",
+ probe:hpusbscsi_usb_probe,
+ disconnect:hpusbscsi_usb_disconnect,
+ id_table:hpusbscsi_usb_ids,
+};
+
+/* module initialisation */
+
+int __init
+hpusbscsi_init (void)
+{
+ int result;
+
+ INIT_LIST_HEAD (&hpusbscsi_devices);
+ DEBUG ("Driver loaded\n");
+
+ if ((result = usb_register (&hpusbscsi_usb_driver)) < 0) {
+ printk (KERN_ERR "hpusbscsi: driver registration failed\n");
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+void __exit
+hpusbscsi_exit (void)
+{
+ struct list_head *tmp;
+ struct list_head *old;
+ struct hpusbscsi * o;
+
+ for (tmp = hpusbscsi_devices.next; tmp != &hpusbscsi_devices;/*nothing */) {
+ old = tmp;
+ tmp = tmp->next;
+ o = (struct hpusbscsi *)old;
+ usb_unlink_urb(&o->controlurb);
+ scsi_unregister_module(MODULE_SCSI_HA,&o->ctempl);
+ kfree(old);
+ }
+
+ usb_deregister (&hpusbscsi_usb_driver);
+}
+
+module_init (hpusbscsi_init);
+module_exit (hpusbscsi_exit);
+
+/* interface to the scsi layer */
+
+static int
+hpusbscsi_scsi_detect (struct SHT *sht)
+{
+ /* Whole function stolen from usb-storage */
+
+ struct hpusbscsi *desc = (struct hpusbscsi *) sht->proc_dir;
+ /* What a hideous hack! */
+
+ char local_name[48];
+
+
+ /* set up the name of our subdirectory under /proc/scsi/ */
+ sprintf (local_name, "hpusbscsi-%d", desc->number);
+ sht->proc_name = kmalloc (strlen (local_name) + 1, GFP_KERNEL);
+ /* FIXME: where is this freed ? */
+
+ if (!sht->proc_name) {
+ return 0;
+ }
+
+ strcpy (sht->proc_name, local_name);
+
+ sht->proc_dir = NULL;
+
+ /* build and submit an interrupt URB for status byte handling */
+ FILL_INT_URB(&desc->controlurb,
+ desc->dev,
+ usb_rcvintpipe(desc->dev,desc->ep_int),
+ &desc->scsi_state_byte,
+ 1,
+ control_interrupt_callback,
+ desc,
+ desc->interrupt_interval
+ );
+
+ if ( 0 > usb_submit_urb(&desc->controlurb)) {
+ kfree(sht->proc_name);
+ return 0;
+ }
+
+ /* In host->hostdata we store a pointer to desc */
+ desc->host = scsi_register (sht, sizeof (desc));
+ if (desc->host == NULL) {
+ kfree (sht->proc_name);
+ usb_unlink_urb(&desc->controlurb);
+ return 0;
+ }
+ desc->host->hostdata[0] = (unsigned long) desc;
+
+
+ return 1;
+}
+
+static int hpusbscsi_scsi_queuecommand (Scsi_Cmnd *srb, scsi_callback callback)
+{
+ struct hpusbscsi* hpusbscsi = (struct hpusbscsi*)(srb->host->hostdata[0]);
+ usb_urb_callback usb_callback;
+ int res;
+
+ hpusbscsi->use_count++;
+
+ /* we don't answer for anything but our single device on any faked host controller */
+ if ( srb->device->lun || srb->device->id || srb->device->channel ) {
+ if (callback) {
+ srb->result = DID_BAD_TARGET;
+ callback(srb);
+ }
+ goto out;
+ }
+
+ /* Now we need to decide which callback to give to the urb we send the command with */
+
+ if (!srb->bufflen) {
+ usb_callback = simple_command_callback;
+ } else {
+ if (srb->use_sg) {
+ usb_callback = scatter_gather_callback;
+ hpusbscsi->fragment = 0;
+ } else {
+ usb_callback = simple_payload_callback;
+ }
+ /* Now we find out which direction data is to be transfered in */
+ hpusbscsi->current_data_pipe = DIRECTION_IS_IN(srb->cmnd[0]) ?
+ usb_rcvbulkpipe(hpusbscsi->dev, hpusbscsi->ep_in)
+ :
+ usb_sndbulkpipe(hpusbscsi->dev, hpusbscsi->ep_out)
+ ;
+ }
+
+
+ TRACE_STATE;
+ if (hpusbscsi->state != HP_STATE_FREE) {
+ printk(KERN_CRIT"hpusbscsi - Ouch: queueing violation!\n");
+ return 1; /* This must not happen */
+ }
+
+ /* We zero the sense buffer to avoid confusing user space */
+ memset(srb->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
+
+ hpusbscsi->state = HP_STATE_BEGINNING;
+ TRACE_STATE;
+
+ /* We prepare the urb for writing out the scsi command */
+ FILL_BULK_URB(
+ &hpusbscsi->dataurb,
+ hpusbscsi->dev,
+ usb_sndbulkpipe(hpusbscsi->dev,hpusbscsi->ep_out),
+ srb->cmnd,
+ srb->cmd_len,
+ usb_callback,
+ hpusbscsi
+ );
+ hpusbscsi->scallback = callback;
+ hpusbscsi->srb = srb;
+
+ res = usb_submit_urb(&hpusbscsi->dataurb);
+ if (res) {
+ hpusbscsi->state = HP_STATE_FREE;
+ TRACE_STATE;
+ if (callback) {
+ srb->result = DID_ERROR;
+ callback(srb);
+ }
+ }
+
+out:
+ hpusbscsi->use_count--;
+ return 0;
+}
+
+static int hpusbscsi_scsi_host_reset (Scsi_Cmnd *srb)
+{
+ struct hpusbscsi* hpusbscsi = (struct hpusbscsi*)(srb->host->hostdata[0]);
+
+ printk(KERN_DEBUG"SCSI reset requested.\n");
+ usb_reset_device(hpusbscsi->dev);
+ printk(KERN_DEBUG"SCSI reset completed.\n");
+ hpusbscsi->state = HP_STATE_FREE;
+
+ return 0;
+}
+
+static int hpusbscsi_scsi_abort (Scsi_Cmnd *srb)
+{
+ struct hpusbscsi* hpusbscsi = (struct hpusbscsi*)(srb->host->hostdata[0]);
+ printk(KERN_DEBUG"Requested is canceled.\n");
+
+ usb_unlink_urb(&hpusbscsi->dataurb);
+ usb_unlink_urb(&hpusbscsi->controlurb);
+ hpusbscsi->state = HP_STATE_FREE;
+
+ return SCSI_ABORT_PENDING;
+}
+
+/* usb interrupt handlers - they are all running IN INTERRUPT ! */
+
+static void handle_usb_error (struct hpusbscsi *hpusbscsi)
+{
+ if (hpusbscsi->scallback != NULL) {
+ hpusbscsi->srb->result = DID_ERROR;
+ hpusbscsi->scallback(hpusbscsi->srb);
+ }
+ hpusbscsi->state = HP_STATE_FREE;
+}
+
+static void control_interrupt_callback (struct urb *u)
+{
+ struct hpusbscsi * hpusbscsi = (struct hpusbscsi *)u->context;
+
+DEBUG("Getting status byte %d \n",hpusbscsi->scsi_state_byte);
+ if(u->status < 0) {
+ if (hpusbscsi->state != HP_STATE_FREE)
+ handle_usb_error(hpusbscsi);
+ return;
+ }
+ hpusbscsi->srb->result &= SCSI_ERR_MASK;
+ hpusbscsi->srb->result |= hpusbscsi->scsi_state_byte<<1;
+
+ if (hpusbscsi->scallback != NULL && hpusbscsi->state == HP_STATE_WAIT)
+ /* we do a callback to the scsi layer if and only if all data has been transfered */
+ hpusbscsi->scallback(hpusbscsi->srb);
+
+ TRACE_STATE;
+ switch (hpusbscsi->state) {
+ case HP_STATE_WAIT:
+ hpusbscsi->state = HP_STATE_FREE;
+ TRACE_STATE;
+ break;
+ case HP_STATE_WORKING:
+ case HP_STATE_BEGINNING:
+ hpusbscsi->state = HP_STATE_PREMATURE;
+ TRACE_STATE;
+ break;
+ default:
+ printk(KERN_ERR"hpusbscsi: Unexpected status report.\n");
+ TRACE_STATE;
+ hpusbscsi->state = HP_STATE_FREE;
+ TRACE_STATE;
+ break;
+ }
+}
+
+static void simple_command_callback(struct urb *u)
+{
+ struct hpusbscsi * hpusbscsi = (struct hpusbscsi *)u->context;
+ if (u->status<0) {
+ handle_usb_error(hpusbscsi);
+ return;
+ }
+ TRACE_STATE;
+ if (hpusbscsi->state != HP_STATE_PREMATURE) {
+ TRACE_STATE;
+ hpusbscsi->state = HP_STATE_WAIT;
+ } else {
+ if (hpusbscsi->scallback != NULL)
+ hpusbscsi->scallback(hpusbscsi->srb);
+ hpusbscsi->state = HP_STATE_FREE;
+ TRACE_STATE;
+ }
+}
+
+static void scatter_gather_callback(struct urb *u)
+{
+ struct hpusbscsi * hpusbscsi = (struct hpusbscsi *)u->context;
+ struct scatterlist *sg = hpusbscsi->srb->buffer;
+ usb_urb_callback callback;
+ int res;
+
+ DEBUG("Going through scatter/gather\n");
+ if (u->status < 0) {
+ handle_usb_error(hpusbscsi);
+ return;
+ }
+
+ if (hpusbscsi->fragment + 1 != hpusbscsi->srb->use_sg)
+ callback = scatter_gather_callback;
+ else
+ callback = simple_done;
+
+ TRACE_STATE;
+ if (hpusbscsi->state != HP_STATE_PREMATURE)
+ hpusbscsi->state = HP_STATE_WORKING;
+ TRACE_STATE;
+
+ FILL_BULK_URB(
+ u,
+ hpusbscsi->dev,
+ hpusbscsi->current_data_pipe,
+ sg[hpusbscsi->fragment].address,
+ sg[hpusbscsi->fragment++].length,
+ callback,
+ hpusbscsi
+ );
+
+ res = usb_submit_urb(u);
+ if (res)
+ hpusbscsi->state = HP_STATE_ERROR;
+ TRACE_STATE;
+}
+
+static void simple_done (struct urb *u)
+{
+ struct hpusbscsi * hpusbscsi = (struct hpusbscsi *)u->context;
+
+ if (u->status < 0) {
+ handle_usb_error(hpusbscsi);
+ return;
+ }
+ DEBUG("Data transfer done\n");
+ TRACE_STATE;
+ if (hpusbscsi->state != HP_STATE_PREMATURE) {
+ if (u->status < 0)
+ hpusbscsi->state = HP_STATE_ERROR;
+ else
+ hpusbscsi->state = HP_STATE_WAIT;
+ TRACE_STATE;
+ } else {
+ if (hpusbscsi->scallback != NULL)
+ hpusbscsi->scallback(hpusbscsi->srb);
+ hpusbscsi->state = HP_STATE_FREE;
+ }
+}
+
+static void simple_payload_callback (struct urb *u)
+{
+ struct hpusbscsi * hpusbscsi = (struct hpusbscsi *)u->context;
+ int res;
+
+ if (u->status<0) {
+ handle_usb_error(hpusbscsi);
+ return;
+ }
+
+ FILL_BULK_URB(
+ u,
+ hpusbscsi->dev,
+ hpusbscsi->current_data_pipe,
+ hpusbscsi->srb->buffer,
+ hpusbscsi->srb->bufflen,
+ simple_done,
+ hpusbscsi
+ );
+
+ res = usb_submit_urb(u);
+ if (res) {
+ handle_usb_error(hpusbscsi);
+ return;
+ }
+ TRACE_STATE;
+ if (hpusbscsi->state != HP_STATE_PREMATURE) {
+ hpusbscsi->state = HP_STATE_WORKING;
+ TRACE_STATE;
+ } else {
+ if (hpusbscsi->scallback != NULL)
+ hpusbscsi->scallback(hpusbscsi->srb);
+ hpusbscsi->state = HP_STATE_FREE;
+ TRACE_STATE;
+ }
+}
+
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)