📄 microtek.c
字号:
static void mts_get_status( struct urb *transfer )
/* Interrupt context! */
{
MTS_INT_INIT();
mts_int_submit_urb(transfer,
usb_rcvbulkpipe(context->instance->usb_dev,
context->instance->ep_response),
&context->status,
1,
mts_transfer_done );
}
static void mts_data_done( struct urb* transfer )
/* Interrupt context! */
{
MTS_INT_INIT();
if ( context->data_length != transfer->actual_length ) {
context->srb->resid = context->data_length - transfer->actual_length;
} else if ( unlikely(transfer->status) ) {
context->srb->result = (transfer->status == -ENOENT ? DID_ABORT : DID_ERROR)<<16;
}
mts_get_status(transfer);
return;
}
static void mts_command_done( struct urb *transfer )
/* Interrupt context! */
{
MTS_INT_INIT();
if ( unlikely(transfer->status) ) {
if (transfer->status == -ENOENT) {
/* We are being killed */
MTS_DEBUG_GOT_HERE();
context->srb->result = DID_ABORT<<16;
} else {
/* A genuine error has occured */
MTS_DEBUG_GOT_HERE();
context->srb->result = DID_ERROR<<16;
}
mts_transfer_cleanup(transfer);
return;
}
if ( context->data ) {
mts_int_submit_urb(transfer,
context->data_pipe,
context->data,
context->data_length,
context->srb->use_sg ? mts_do_sg : mts_data_done);
} else mts_get_status(transfer);
return;
}
static void mts_do_sg (struct urb* transfer)
{
struct scatterlist * sg;
MTS_INT_INIT();
MTS_DEBUG("Processing fragment %d of %d\n", context->fragment,context->srb->use_sg);
if (unlikely(transfer->status)) {
context->srb->result = (transfer->status == -ENOENT ? DID_ABORT : DID_ERROR)<<16;
mts_transfer_cleanup(transfer);
}
sg = context->srb->buffer;
context->fragment++;
mts_int_submit_urb(transfer,
context->data_pipe,
page_address(sg[context->fragment].page) +
sg[context->fragment].offset,
sg[context->fragment].length,
context->fragment + 1 == context->srb->use_sg ? mts_data_done : mts_do_sg);
return;
}
static const u8 mts_read_image_sig[] = { 0x28, 00, 00, 00 };
static const u8 mts_read_image_sig_len = 4;
static const unsigned char mts_direction[256/8] = {
0x28, 0x81, 0x14, 0x14, 0x20, 0x01, 0x90, 0x77,
0x0C, 0x20, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
#define MTS_DIRECTION_IS_IN(x) ((mts_direction[x>>3] >> (x & 7)) & 1)
static void
mts_build_transfer_context( Scsi_Cmnd *srb, struct mts_desc* desc )
{
int pipe;
struct scatterlist * sg;
MTS_DEBUG_GOT_HERE();
desc->context.instance = desc;
desc->context.srb = srb;
desc->context.fragment = 0;
if (!srb->use_sg) {
if ( !srb->bufflen ){
desc->context.data = 0;
desc->context.data_length = 0;
return;
} else {
desc->context.data = srb->buffer;
desc->context.data_length = srb->bufflen;
MTS_DEBUG("length = %d or %d\n",
srb->request_bufflen, srb->bufflen);
}
} else {
MTS_DEBUG("Using scatter/gather\n");
sg = srb->buffer;
desc->context.data = page_address(sg[0].page) + sg[0].offset;
desc->context.data_length = sg[0].length;
}
/* can't rely on srb->sc_data_direction */
/* Brutally ripped from usb-storage */
if ( !memcmp( srb->cmnd, mts_read_image_sig, mts_read_image_sig_len )
) { pipe = usb_rcvbulkpipe(desc->usb_dev,desc->ep_image);
MTS_DEBUG( "transfering from desc->ep_image == %d\n",
(int)desc->ep_image );
} else if ( MTS_DIRECTION_IS_IN(srb->cmnd[0]) ) {
pipe = usb_rcvbulkpipe(desc->usb_dev,desc->ep_response);
MTS_DEBUG( "transfering from desc->ep_response == %d\n",
(int)desc->ep_response);
} else {
MTS_DEBUG("transfering to desc->ep_out == %d\n",
(int)desc->ep_out);
pipe = usb_sndbulkpipe(desc->usb_dev,desc->ep_out);
}
desc->context.data_pipe = pipe;
}
static
int mts_scsi_queuecommand( Scsi_Cmnd *srb, mts_scsi_cmnd_callback callback )
{
struct mts_desc* desc = (struct mts_desc*)(srb->host->hostdata[0]);
int err = 0;
int res;
MTS_DEBUG_GOT_HERE();
mts_show_command(srb);
mts_debug_dump(desc);
if ( srb->device->lun || srb->device->id || srb->device->channel ) {
MTS_DEBUG("Command to LUN=%d ID=%d CHANNEL=%d from SCSI layer\n",(int)srb->device->lun,(int)srb->device->id, (int)srb->device->channel );
MTS_DEBUG("this device doesn't exist\n");
srb->result = DID_BAD_TARGET << 16;
if(likely(callback != NULL))
callback(srb);
goto out;
}
FILL_BULK_URB(desc->urb,
desc->usb_dev,
usb_sndbulkpipe(desc->usb_dev,desc->ep_out),
srb->cmnd,
srb->cmd_len,
mts_command_done,
&desc->context
);
mts_build_transfer_context( srb, desc );
desc->context.final_callback = callback;
/* here we need ATOMIC as we are called with the iolock */
res=usb_submit_urb(desc->urb, GFP_ATOMIC);
if(unlikely(res)){
MTS_ERROR("error %d submitting URB\n",(int)res);
srb->result = DID_ERROR << 16;
if(likely(callback != NULL))
callback(srb);
}
out:
return err;
}
/*
* this defines our 'host'
*/
/* NOTE: This is taken from usb-storage, should be right. */
static Scsi_Host_Template mts_scsi_host_template = {
name: "microtekX6",
detect: mts_scsi_detect,
release: mts_scsi_release,
queuecommand: mts_scsi_queuecommand,
eh_abort_handler: mts_scsi_abort,
eh_host_reset_handler: mts_scsi_host_reset,
sg_tablesize: SG_ALL,
can_queue: 1,
this_id: -1,
cmd_per_lun: 1,
present: 0,
unchecked_isa_dma: FALSE,
use_clustering: TRUE,
emulated: TRUE
};
/* USB layer driver interface implementation */
static void mts_usb_disconnect (struct usb_device *dev, void *ptr)
{
struct mts_desc* to_remove = (struct mts_desc*)ptr;
MTS_DEBUG_GOT_HERE();
/* leave the list - lock it */
down(&mts_list_semaphore);
mts_remove_nolock(to_remove);
up(&mts_list_semaphore);
}
struct vendor_product
{
char* name;
enum
{
mts_sup_unknown=0,
mts_sup_alpha,
mts_sup_full
}
support_status;
} ;
/* These are taken from the msmUSB.inf file on the Windows driver CD */
const static struct vendor_product mts_supported_products[] =
{
{ "Phantom 336CX", mts_sup_unknown},
{ "Phantom 336CX", mts_sup_unknown},
{ "Scanmaker X6", mts_sup_alpha},
{ "Phantom C6", mts_sup_unknown},
{ "Phantom 336CX", mts_sup_unknown},
{ "ScanMaker V6USL", mts_sup_unknown},
{ "ScanMaker V6USL", mts_sup_unknown},
{ "Scanmaker V6UL", mts_sup_unknown},
{ "Scanmaker V6UPL", mts_sup_alpha},
};
/* The entries of microtek_table must correspond, line-by-line to
the entries of mts_supported_products[]. */
static struct usb_device_id mts_usb_ids [] =
{
{ USB_DEVICE(0x4ce, 0x0300) },
{ USB_DEVICE(0x5da, 0x0094) },
{ USB_DEVICE(0x5da, 0x0099) },
{ USB_DEVICE(0x5da, 0x009a) },
{ USB_DEVICE(0x5da, 0x00a0) },
{ USB_DEVICE(0x5da, 0x00a3) },
{ USB_DEVICE(0x5da, 0x80a3) },
{ USB_DEVICE(0x5da, 0x80ac) },
{ USB_DEVICE(0x5da, 0x00b6) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, mts_usb_ids);
static void * mts_usb_probe (struct usb_device *dev, unsigned int interface,
const struct usb_device_id *id)
{
int i;
int result;
int ep_out = -1;
int ep_in_set[3]; /* this will break if we have more than three endpoints
which is why we check */
int *ep_in_current = ep_in_set;
struct mts_desc * new_desc;
struct vendor_product const* p;
/* the altsettting 0 on the interface we're probing */
struct usb_interface_descriptor *altsetting;
MTS_DEBUG_GOT_HERE();
MTS_DEBUG( "usb-device descriptor at %x\n", (int)dev );
MTS_DEBUG( "product id = 0x%x, vendor id = 0x%x\n",
(int)dev->descriptor.idProduct,
(int)dev->descriptor.idVendor );
MTS_DEBUG_GOT_HERE();
p = &mts_supported_products[id - mts_usb_ids];
MTS_DEBUG_GOT_HERE();
MTS_DEBUG( "found model %s\n", p->name );
if ( p->support_status != mts_sup_full )
MTS_MESSAGE( "model %s is not known to be fully supported, reports welcome!\n",
p->name );
/* the altsettting 0 on the interface we're probing */
altsetting =
&(dev->actconfig->interface[interface].altsetting[0]);
/* Check if the config is sane */
if ( altsetting->bNumEndpoints != MTS_EP_TOTAL ) {
MTS_WARNING( "expecting %d got %d endpoints! Bailing out.\n",
(int)MTS_EP_TOTAL, (int)altsetting->bNumEndpoints );
return NULL;
}
for( i = 0; i < altsetting->bNumEndpoints; i++ ) {
if ((altsetting->endpoint[i].bmAttributes &
USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK) {
MTS_WARNING( "can only deal with bulk endpoints; endpoint %d is not bulk.\n",
(int)altsetting->endpoint[i].bEndpointAddress );
} else {
if (altsetting->endpoint[i].bEndpointAddress &
USB_DIR_IN)
*ep_in_current++
= altsetting->endpoint[i].bEndpointAddress &
USB_ENDPOINT_NUMBER_MASK;
else {
if ( ep_out != -1 ) {
MTS_WARNING( "can only deal with one output endpoints. Bailing out." );
return NULL;
}
ep_out = altsetting->endpoint[i].bEndpointAddress &
USB_ENDPOINT_NUMBER_MASK;
}
}
}
if ( ep_out == -1 ) {
MTS_WARNING( "couldn't find an output bulk endpoint. Bailing out.\n" );
return NULL;
}
/* I don't understand the following fully (it's from usb-storage) -- John */
/* set the interface -- STALL is an acceptable response here */
result = usb_set_interface(dev, altsetting->bInterfaceNumber, 0);
MTS_DEBUG("usb_set_interface returned %d.\n",result);
switch( result )
{
case 0: /* no error */
break;
case -EPIPE:
usb_clear_halt(dev, usb_sndctrlpipe(dev, 0));
MTS_DEBUG( "clearing clearing stall on control interface\n" );
break;
default:
MTS_DEBUG( "unknown error %d from usb_set_interface\n",
(int)result );
return NULL;
}
/* allocating a new descriptor */
new_desc = (struct mts_desc *)kmalloc(sizeof(struct mts_desc), GFP_KERNEL);
if (new_desc == NULL)
{
MTS_ERROR("couldn't allocate scanner desc, bailing out!\n");
return NULL;
}
memset( new_desc, 0, sizeof(*new_desc) );
new_desc->urb = usb_alloc_urb(0, GFP_KERNEL);
if (!new_desc->urb) {
kfree(new_desc);
return NULL;
}
/* initialising that descriptor */
new_desc->usb_dev = dev;
new_desc->interface = interface;
init_MUTEX(&new_desc->lock);
if(mts_list){
new_desc->host_number = mts_list->host_number+1;
} else {
new_desc->host_number = 0;
}
/* endpoints */
new_desc->ep_out = ep_out;
new_desc->ep_response = ep_in_set[0];
new_desc->ep_image = ep_in_set[1];
if ( new_desc->ep_out != MTS_EP_OUT )
MTS_WARNING( "will this work? Command EP is not usually %d\n",
(int)new_desc->ep_out );
if ( new_desc->ep_response != MTS_EP_RESPONSE )
MTS_WARNING( "will this work? Response EP is not usually %d\n",
(int)new_desc->ep_response );
if ( new_desc->ep_image != MTS_EP_IMAGE )
MTS_WARNING( "will this work? Image data EP is not usually %d\n",
(int)new_desc->ep_image );
/* Initialize the host template based on the default one */
memcpy(&(new_desc->ctempl), &mts_scsi_host_template, sizeof(mts_scsi_host_template));
/* HACK from usb-storage - this is needed for scsi detection */
(struct mts_desc *)new_desc->ctempl.proc_dir = new_desc; /* FIXME */
MTS_DEBUG("registering SCSI module\n");
new_desc->ctempl.module = THIS_MODULE;
result = scsi_register_host(&new_desc->ctempl);
/* Will get hit back in microtek_detect by this func */
if ( result )
{
MTS_ERROR( "error %d from scsi_register_host! Help!\n",
(int)result );
/* FIXME: need more cleanup? */
kfree( new_desc );
return NULL;
}
MTS_DEBUG_GOT_HERE();
/* FIXME: the bomb is armed, must the host be registered under lock ? */
/* join the list - lock it */
down(&mts_list_semaphore);
mts_add_nolock( new_desc );
up(&mts_list_semaphore);
MTS_DEBUG("completed probe and exiting happily\n");
return (void *)new_desc;
}
/* get us noticed by the rest of the kernel */
int __init microtek_drv_init(void)
{
int result;
MTS_DEBUG_GOT_HERE();
init_MUTEX(&mts_list_semaphore);
if ((result = usb_register(&mts_usb_driver)) < 0) {
MTS_DEBUG("usb_register returned %d\n", result );
return -1;
} else {
MTS_DEBUG("driver registered.\n");
}
info(DRIVER_VERSION ":" DRIVER_DESC);
return 0;
}
void __exit microtek_drv_exit(void)
{
struct mts_desc* next;
MTS_DEBUG_GOT_HERE();
usb_deregister(&mts_usb_driver);
down(&mts_list_semaphore);
while (mts_list) {
/* keep track of where the next one is */
next = mts_list->next;
mts_remove_nolock( mts_list );
/* advance the list pointer */
mts_list = next;
}
up(&mts_list_semaphore);
}
module_init(microtek_drv_init);
module_exit(microtek_drv_exit);
MODULE_AUTHOR( DRIVER_AUTHOR );
MODULE_DESCRIPTION( DRIVER_DESC );
MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -