diff options
author | edwin_rong <edwin_rong@realsil.com.cn> | 2011-07-19 17:10:35 +0800 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2011-08-23 12:11:46 -0700 |
commit | 1dac4186bcc663cb8c2bcc59481aea8fe9124a6c (patch) | |
tree | 6313380d862a0a128ddd4f05ef1be3b5f74b9002 /drivers/staging/rts5139/rts51x_transport.c | |
parent | dd89e20d7e9d507b3122de2f79661b5fc2c2198e (diff) |
Staging: add driver for Realtek RTS5139 cardreader
This driver is used for Realtek RTS5139 USB cardreader, which
supports many cards, such as SD, MS, XD series cards.
Signed-off-by: edwin_rong <edwin_rong@realsil.com.cn>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/staging/rts5139/rts51x_transport.c')
-rw-r--r-- | drivers/staging/rts5139/rts51x_transport.c | 1000 |
1 files changed, 1000 insertions, 0 deletions
diff --git a/drivers/staging/rts5139/rts51x_transport.c b/drivers/staging/rts5139/rts51x_transport.c new file mode 100644 index 00000000000..e11467acc57 --- /dev/null +++ b/drivers/staging/rts5139/rts51x_transport.c @@ -0,0 +1,1000 @@ +/* Driver for Realtek RTS51xx USB card reader + * + * Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved. + * + * 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, see <http://www.gnu.org/licenses/>. + * + * Author: + * wwang (wei_wang@realsil.com.cn) + * No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China + * Maintainer: + * Edwin Rong (edwin_rong@realsil.com.cn) + * No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China + */ + +#include <linux/blkdev.h> +#include <linux/kthread.h> +#include <linux/sched.h> +#include <linux/slab.h> + +#include <scsi/scsi.h> +#include <scsi/scsi_eh.h> +#include <scsi/scsi_device.h> + +#include "debug.h" +#include "rts51x.h" +#include "rts51x_chip.h" +#include "rts51x_card.h" +#include "rts51x_scsi.h" +#include "rts51x_transport.h" +#include "trace.h" + +/*********************************************************************** + * Scatter-gather transfer buffer access routines + ***********************************************************************/ + +/* Copy a buffer of length buflen to/from the srb's transfer buffer. + * Update the **sgptr and *offset variables so that the next copy will + * pick up from where this one left off. + */ + +unsigned int rts51x_access_sglist(unsigned char *buffer, + unsigned int buflen, void *sglist, + void **sgptr, unsigned int *offset, + enum xfer_buf_dir dir) +{ + unsigned int cnt; + struct scatterlist *sg = (struct scatterlist *)*sgptr; + + /* We have to go through the list one entry + * at a time. Each s-g entry contains some number of pages, and + * each page has to be kmap()'ed separately. If the page is already + * in kernel-addressable memory then kmap() will return its address. + * If the page is not directly accessible -- such as a user buffer + * located in high memory -- then kmap() will map it to a temporary + * position in the kernel's virtual address space. + */ + + if (!sg) + sg = (struct scatterlist *)sglist; + + /* This loop handles a single s-g list entry, which may + * include multiple pages. Find the initial page structure + * and the starting offset within the page, and update + * the *offset and **sgptr values for the next loop. + */ + cnt = 0; + while (cnt < buflen && sg) { + struct page *page = sg_page(sg) + + ((sg->offset + *offset) >> PAGE_SHIFT); + unsigned int poff = (sg->offset + *offset) & (PAGE_SIZE - 1); + unsigned int sglen = sg->length - *offset; + + if (sglen > buflen - cnt) { + + /* Transfer ends within this s-g entry */ + sglen = buflen - cnt; + *offset += sglen; + } else { + + /* Transfer continues to next s-g entry */ + *offset = 0; + sg = sg_next(sg); + } + + /* Transfer the data for all the pages in this + * s-g entry. For each page: call kmap(), do the + * transfer, and call kunmap() immediately after. */ + while (sglen > 0) { + unsigned int plen = min(sglen, (unsigned int) + PAGE_SIZE - poff); + unsigned char *ptr = kmap(page); + + if (dir == TO_XFER_BUF) + memcpy(ptr + poff, buffer + cnt, plen); + else + memcpy(buffer + cnt, ptr + poff, plen); + kunmap(page); + + /* Start at the beginning of the next page */ + poff = 0; + ++page; + cnt += plen; + sglen -= plen; + } + } + *sgptr = sg; + + /* Return the amount actually transferred */ + return cnt; +} + +unsigned int rts51x_access_xfer_buf(unsigned char *buffer, + unsigned int buflen, struct scsi_cmnd *srb, + struct scatterlist **sgptr, + unsigned int *offset, enum xfer_buf_dir dir) +{ + return rts51x_access_sglist(buffer, buflen, (void *)scsi_sglist(srb), + (void **)sgptr, offset, dir); +} + +/* Store the contents of buffer into srb's transfer buffer and set the + * SCSI residue. + */ +void rts51x_set_xfer_buf(unsigned char *buffer, + unsigned int buflen, struct scsi_cmnd *srb) +{ + unsigned int offset = 0; + struct scatterlist *sg = NULL; + + buflen = min(buflen, scsi_bufflen(srb)); + buflen = rts51x_access_xfer_buf(buffer, buflen, srb, &sg, &offset, + TO_XFER_BUF); + if (buflen < scsi_bufflen(srb)) + scsi_set_resid(srb, scsi_bufflen(srb) - buflen); +} + +void rts51x_get_xfer_buf(unsigned char *buffer, + unsigned int buflen, struct scsi_cmnd *srb) +{ + unsigned int offset = 0; + struct scatterlist *sg = NULL; + + buflen = min(buflen, scsi_bufflen(srb)); + buflen = rts51x_access_xfer_buf(buffer, buflen, srb, &sg, &offset, + FROM_XFER_BUF); + if (buflen < scsi_bufflen(srb)) + scsi_set_resid(srb, scsi_bufflen(srb) - buflen); +} + +/* This is the completion handler which will wake us up when an URB + * completes. + */ +static void urb_done_completion(struct urb *urb) +{ + struct completion *urb_done_ptr = urb->context; + + if (urb_done_ptr) + complete(urb_done_ptr); +} + +/* This is the common part of the URB message submission code + * + * All URBs from the driver involved in handling a queued scsi + * command _must_ pass through this function (or something like it) for the + * abort mechanisms to work properly. + */ +static int rts51x_msg_common(struct rts51x_chip *chip, struct urb *urb, + int timeout) +{ + struct rts51x_usb *rts51x = chip->usb; + struct completion urb_done; + long timeleft; + int status; + + /* don't submit URBs during abort processing */ + if (test_bit(FLIDX_ABORTING, &rts51x->dflags)) + TRACE_RET(chip, -EIO); + + /* set up data structures for the wakeup system */ + init_completion(&urb_done); + + /* fill the common fields in the URB */ + urb->context = &urb_done; + urb->actual_length = 0; + urb->error_count = 0; + urb->status = 0; + + /* we assume that if transfer_buffer isn't us->iobuf then it + * hasn't been mapped for DMA. Yes, this is clunky, but it's + * easier than always having the caller tell us whether the + * transfer buffer has already been mapped. */ + urb->transfer_flags = URB_NO_SETUP_DMA_MAP; + if (urb->transfer_buffer == rts51x->iobuf) { + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + urb->transfer_dma = rts51x->iobuf_dma; + } + urb->setup_dma = rts51x->cr_dma; + + /* submit the URB */ + status = usb_submit_urb(urb, GFP_NOIO); + if (status) { + /* something went wrong */ + TRACE_RET(chip, status); + } + + /* since the URB has been submitted successfully, it's now okay + * to cancel it */ + set_bit(FLIDX_URB_ACTIVE, &rts51x->dflags); + + /* did an abort occur during the submission? */ + if (test_bit(FLIDX_ABORTING, &rts51x->dflags)) { + + /* cancel the URB, if it hasn't been cancelled already */ + if (test_and_clear_bit(FLIDX_URB_ACTIVE, &rts51x->dflags)) { + RTS51X_DEBUGP("-- cancelling URB\n"); + usb_unlink_urb(urb); + } + } + + /* wait for the completion of the URB */ + timeleft = + wait_for_completion_interruptible_timeout(&urb_done, + (timeout * HZ / + 1000) ? : + MAX_SCHEDULE_TIMEOUT); + + clear_bit(FLIDX_URB_ACTIVE, &rts51x->dflags); + + if (timeleft <= 0) { + RTS51X_DEBUGP("%s -- cancelling URB\n", + timeleft == 0 ? "Timeout" : "Signal"); + usb_kill_urb(urb); + if (timeleft == 0) + status = -ETIMEDOUT; + else + status = -EINTR; + } else { + status = urb->status; + } + + return status; +} + +/* + * Interpret the results of a URB transfer + */ +static int interpret_urb_result(struct rts51x_chip *chip, unsigned int pipe, + unsigned int length, int result, + unsigned int partial) +{ + int retval = STATUS_SUCCESS; + + /* RTS51X_DEBUGP("Status code %d; transferred %u/%u\n", + result, partial, length); */ + switch (result) { + /* no error code; did we send all the data? */ + case 0: + if (partial != length) { + RTS51X_DEBUGP("-- short transfer\n"); + TRACE_RET(chip, STATUS_TRANS_SHORT); + } + /* RTS51X_DEBUGP("-- transfer complete\n"); */ + return STATUS_SUCCESS; + /* stalled */ + case -EPIPE: + /* for control endpoints, (used by CB[I]) a stall indicates + * a failed command */ + if (usb_pipecontrol(pipe)) { + RTS51X_DEBUGP("-- stall on control pipe\n"); + TRACE_RET(chip, STATUS_STALLED); + } + /* for other sorts of endpoint, clear the stall */ + RTS51X_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe); + if (rts51x_clear_halt(chip, pipe) < 0) + TRACE_RET(chip, STATUS_ERROR); + retval = STATUS_STALLED; + TRACE_GOTO(chip, Exit); + + /* babble - the device tried to send more than + * we wanted to read */ + case -EOVERFLOW: + RTS51X_DEBUGP("-- babble\n"); + retval = STATUS_TRANS_LONG; + TRACE_GOTO(chip, Exit); + + /* the transfer was cancelled by abort, + * disconnect, or timeout */ + case -ECONNRESET: + RTS51X_DEBUGP("-- transfer cancelled\n"); + retval = STATUS_ERROR; + TRACE_GOTO(chip, Exit); + + /* short scatter-gather read transfer */ + case -EREMOTEIO: + RTS51X_DEBUGP("-- short read transfer\n"); + retval = STATUS_TRANS_SHORT; + TRACE_GOTO(chip, Exit); + + /* abort or disconnect in progress */ + case -EIO: + RTS51X_DEBUGP("-- abort or disconnect in progress\n"); + retval = STATUS_ERROR; + TRACE_GOTO(chip, Exit); + + case -ETIMEDOUT: + RTS51X_DEBUGP("-- time out\n"); + retval = STATUS_TIMEDOUT; + TRACE_GOTO(chip, Exit); + + /* the catch-all error case */ + default: + RTS51X_DEBUGP("-- unknown error\n"); + retval = STATUS_ERROR; + TRACE_GOTO(chip, Exit); + } + +Exit: + if ((retval != STATUS_SUCCESS) && !usb_pipecontrol(pipe)) + rts51x_clear_hw_error(chip); + + return retval; +} + +int rts51x_ctrl_transfer(struct rts51x_chip *chip, unsigned int pipe, + u8 request, u8 requesttype, u16 value, u16 index, + void *data, u16 size, int timeout) +{ + struct rts51x_usb *rts51x = chip->usb; + int result; + + RTS51X_DEBUGP("%s: rq=%02x rqtype=%02x value=%04x index=%02x len=%u\n", + __func__, request, requesttype, value, index, size); + + /* fill in the devrequest structure */ + rts51x->cr->bRequestType = requesttype; + rts51x->cr->bRequest = request; + rts51x->cr->wValue = cpu_to_le16(value); + rts51x->cr->wIndex = cpu_to_le16(index); + rts51x->cr->wLength = cpu_to_le16(size); + + /* fill and submit the URB */ + usb_fill_control_urb(rts51x->current_urb, rts51x->pusb_dev, pipe, + (unsigned char *)rts51x->cr, data, size, + urb_done_completion, NULL); + result = rts51x_msg_common(chip, rts51x->current_urb, timeout); + + return interpret_urb_result(chip, pipe, size, result, + rts51x->current_urb->actual_length); +} + +int rts51x_clear_halt(struct rts51x_chip *chip, unsigned int pipe) +{ + int result; + int endp = usb_pipeendpoint(pipe); + + if (usb_pipein(pipe)) + endp |= USB_DIR_IN; + + result = rts51x_ctrl_transfer(chip, SND_CTRL_PIPE(chip), + USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT, + USB_ENDPOINT_HALT, endp, NULL, 0, 3000); + if (result != STATUS_SUCCESS) + TRACE_RET(chip, STATUS_FAIL); + + usb_reset_endpoint(chip->usb->pusb_dev, endp); + + return STATUS_SUCCESS; +} + +int rts51x_reset_pipe(struct rts51x_chip *chip, char pipe) +{ + return rts51x_clear_halt(chip, pipe); +} + +static void rts51x_sg_clean(struct usb_sg_request *io) +{ + if (io->urbs) { + while (io->entries--) + usb_free_urb(io->urbs[io->entries]); + kfree(io->urbs); + io->urbs = NULL; + } +#if 0 /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) */ + if (io->dev->dev.dma_mask != NULL) + usb_buffer_unmap_sg(io->dev, usb_pipein(io->pipe), + io->sg, io->nents); +#endif + io->dev = NULL; +} +#if 0 +static void rts51x_sg_complete(struct urb *urb) +{ + struct usb_sg_request *io = urb->context; + int status = urb->status; + + spin_lock(&io->lock); + + /* In 2.5 we require hcds' endpoint queues not to progress after fault + * reports, until the completion callback (this!) returns. That lets + * device driver code (like this routine) unlink queued urbs first, + * if it needs to, since the HC won't work on them at all. So it's + * not possible for page N+1 to overwrite page N, and so on. + * + * That's only for "hard" faults; "soft" faults (unlinks) sometimes + * complete before the HCD can get requests away from hardware, + * though never during cleanup after a hard fault. + */ + if (io->status + && (io->status != -ECONNRESET + || status != -ECONNRESET) + && urb->actual_length) { + dev_err(io->dev->bus->controller, + "dev %s ep%d%s scatterlist error %d/%d\n", + io->dev->devpath, + usb_endpoint_num(&urb->ep->desc), + usb_urb_dir_in(urb) ? "in" : "out", + status, io->status); + /* BUG (); */ + } + + if (io->status == 0 && status && status != -ECONNRESET) { + int i, found, retval; + + io->status = status; + + /* the previous urbs, and this one, completed already. + * unlink pending urbs so they won't rx/tx bad data. + * careful: unlink can sometimes be synchronous... + */ + spin_unlock(&io->lock); + for (i = 0, found = 0; i < io->entries; i++) { + if (!io->urbs[i] || !io->urbs[i]->dev) + continue; + if (found) { + retval = usb_unlink_urb(io->urbs[i]); + if (retval != -EINPROGRESS && + retval != -ENODEV && + retval != -EBUSY) + dev_err(&io->dev->dev, + "%s, unlink --> %d\n", + __func__, retval); + } else if (urb == io->urbs[i]) + found = 1; + } + spin_lock(&io->lock); + } + urb->dev = NULL; + + /* on the last completion, signal usb_sg_wait() */ + io->bytes += urb->actual_length; + io->count--; + if (!io->count) + complete(&io->complete); + + spin_unlock(&io->lock); +} + +/* This function is ported from usb_sg_init, which can transfer + * sg list partially */ +int rts51x_sg_init_partial(struct usb_sg_request *io, struct usb_device *dev, + unsigned pipe, unsigned period, void *buf, struct scatterlist **sgptr, + unsigned int *offset, int nents, size_t length, gfp_t mem_flags) +{ + int i; + int urb_flags; + int dma; + struct scatterlist *sg = *sgptr, *first_sg; + + first_sg = (struct scatterlist *)buf; + if (!sg) + sg = first_sg; + + if (!io || !dev || !sg + || usb_pipecontrol(pipe) + || usb_pipeisoc(pipe) + || (nents <= 0)) + return -EINVAL; + + spin_lock_init(&io->lock); + io->dev = dev; + io->pipe = pipe; + io->sg = first_sg; /* used by unmap */ + io->nents = nents; + + RTS51X_DEBUGP("Before map, sg address: 0x%x\n", (unsigned int)sg); + RTS51X_DEBUGP("Before map, dev address: 0x%x\n", (unsigned int)dev); + + /* not all host controllers use DMA (like the mainstream pci ones); + * they can use PIO (sl811) or be software over another transport. + */ + dma = (dev->dev.dma_mask != NULL); + if (dma) { + /* map the whole sg list, because here we only know the + * total nents */ + io->entries = usb_buffer_map_sg(dev, usb_pipein(pipe), + first_sg, nents); + } else { + io->entries = nents; + } + + /* initialize all the urbs we'll use */ + if (io->entries <= 0) + return io->entries; + + io->urbs = kmalloc(io->entries * sizeof *io->urbs, mem_flags); + if (!io->urbs) + goto nomem; + + urb_flags = URB_NO_INTERRUPT; + if (dma) + urb_flags |= URB_NO_TRANSFER_DMA_MAP; + if (usb_pipein(pipe)) + urb_flags |= URB_SHORT_NOT_OK; + + RTS51X_DEBUGP("io->entries = %d\n", io->entries); + + for (i = 0; (sg != NULL) && (length > 0); i++) { + unsigned len; + + RTS51X_DEBUGP("sg address: 0x%x\n", (unsigned int)sg); + RTS51X_DEBUGP("length = %d, *offset = %d\n", length, *offset); + + io->urbs[i] = usb_alloc_urb(0, mem_flags); + if (!io->urbs[i]) { + io->entries = i; + goto nomem; + } + + io->urbs[i]->dev = NULL; + io->urbs[i]->pipe = pipe; + io->urbs[i]->interval = period; + io->urbs[i]->transfer_flags = urb_flags; + + io->urbs[i]->complete = rts51x_sg_complete; + io->urbs[i]->context = io; + + if (dma) { + io->urbs[i]->transfer_dma = + sg_dma_address(sg) + *offset; + len = sg_dma_len(sg) - *offset; + io->urbs[i]->transfer_buffer = NULL; + RTS51X_DEBUGP(" -- sg entry dma length = %d\n", + sg_dma_len(sg)); + } else { + /* hc may use _only_ transfer_buffer */ + io->urbs[i]->transfer_buffer = sg_virt(sg) + *offset; + len = sg->length - *offset; + RTS51X_DEBUGP(" -- sg entry length = %d\n", + sg->length); + } + + if (length >= len) { + *offset = 0; + io->urbs[i]->transfer_buffer_length = len; + length -= len; + sg = sg_next(sg); + } else { + *offset += length; + io->urbs[i]->transfer_buffer_length = length; + length = 0; + } + if (length == 0) + io->entries = i + 1; +#if 0 + if (length) { + len = min_t(unsigned, len, length); + length -= len; + if (length == 0) { + io->entries = i + 1; + *offset += len; + } else { + *offset = 0; + } + } +#endif + } + RTS51X_DEBUGP("In %s, urb count: %d\n", __func__, i); + io->urbs[--i]->transfer_flags &= ~URB_NO_INTERRUPT; + + RTS51X_DEBUGP("sg address stored in sgptr: 0x%x\n", (unsigned int)sg); + *sgptr = sg; + + /* transaction state */ + io->count = io->entries; + io->status = 0; + io->bytes = 0; + init_completion(&io->complete); + return 0; + +nomem: + rts51x_sg_clean(io); + return -ENOMEM; +} +#endif +int rts51x_sg_init(struct usb_sg_request *io, struct usb_device *dev, + unsigned pipe, unsigned period, struct scatterlist *sg, + int nents, size_t length, gfp_t mem_flags) +{ + return usb_sg_init(io, dev, pipe, period, sg, nents, length, mem_flags); +} + +int rts51x_sg_wait(struct usb_sg_request *io, int timeout) +{ + long timeleft; + int i; + int entries = io->entries; + + /* queue the urbs. */ + spin_lock_irq(&io->lock); + i = 0; + while (i < entries && !io->status) { + int retval; + + io->urbs[i]->dev = io->dev; + retval = usb_submit_urb(io->urbs[i], GFP_ATOMIC); + + /* after we submit, let completions or cancelations fire; + * we handshake using io->status. + */ + spin_unlock_irq(&io->lock); + switch (retval) { + /* maybe we retrying will recover */ + case -ENXIO: /* hc didn't queue this one */ + case -EAGAIN: + case -ENOMEM: + io->urbs[i]->dev = NULL; + retval = 0; + yield(); + break; + + /* no error? continue immediately. + * + * NOTE: to work better with UHCI (4K I/O buffer may + * need 3K of TDs) it may be good to limit how many + * URBs are queued at once; N milliseconds? + */ + case 0: + ++i; + cpu_relax(); + break; + + /* fail any uncompleted urbs */ + default: + io->urbs[i]->dev = NULL; + io->urbs[i]->status = retval; + dev_dbg(&io->dev->dev, "%s, submit --> %d\n", + __func__, retval); + usb_sg_cancel(io); + } + spin_lock_irq(&io->lock); + if (retval && (io->status == 0 || io->status == -ECONNRESET)) + io->status = retval; + } + io->count -= entries - i; + if (io->count == 0) + complete(&io->complete); + spin_unlock_irq(&io->lock); + + timeleft = + wait_for_completion_interruptible_timeout(&io->complete, + (timeout * HZ / + 1000) ? : + MAX_SCHEDULE_TIMEOUT); + if (timeleft <= 0) { + RTS51X_DEBUGP("%s -- cancelling SG request\n", + timeleft == 0 ? "Timeout" : "Signal"); + usb_sg_cancel(io); + if (timeleft == 0) + io->status = -ETIMEDOUT; + else + io->status = -EINTR; + } + + rts51x_sg_clean(io); + return io->status; +} + +/* + * Transfer a scatter-gather list via bulk transfer + * + * This function does basically the same thing as usb_stor_bulk_transfer_buf() + * above, but it uses the usbcore scatter-gather library. + */ +static int rts51x_bulk_transfer_sglist(struct rts51x_chip *chip, + unsigned int pipe, + struct scatterlist *sg, int num_sg, + unsigned int length, + unsigned int *act_len, int timeout) +{ + int result; + + /* don't submit s-g requests during abort processing */ + if (test_bit(FLIDX_ABORTING, &chip->usb->dflags)) + TRACE_RET(chip, STATUS_ERROR); + + /* initialize the scatter-gather request block */ + RTS51X_DEBUGP("%s: xfer %u bytes, %d entries\n", __func__, + length, num_sg); + result = + rts51x_sg_init(&chip->usb->current_sg, chip->usb->pusb_dev, pipe, 0, + sg, num_sg, length, GFP_NOIO); + if (result) { + RTS51X_DEBUGP("rts51x_sg_init returned %d\n", result); + TRACE_RET(chip, STATUS_ERROR); + } + + /* since the block has been initialized successfully, it's now + * okay to cancel it */ + set_bit(FLIDX_SG_ACTIVE, &chip->usb->dflags); + + /* did an abort occur during the submission? */ + if (test_bit(FLIDX_ABORTING, &chip->usb->dflags)) { + + /* cancel the request, if it hasn't been cancelled already */ + if (test_and_clear_bit(FLIDX_SG_ACTIVE, &chip->usb->dflags)) { + RTS51X_DEBUGP("-- cancelling sg request\n"); + usb_sg_cancel(&chip->usb->current_sg); + } + } + + /* wait for the completion of the transfer */ + result = rts51x_sg_wait(&chip->usb->current_sg, timeout); + + clear_bit(FLIDX_SG_ACTIVE, &chip->usb->dflags); + + /* result = us->current_sg.status; */ + if (act_len) + *act_len = chip->usb->current_sg.bytes; + return interpret_urb_result(chip, pipe, length, result, + chip->usb->current_sg.bytes); +} +#if 0 +static int rts51x_bulk_transfer_sglist_partial(struct rts51x_chip *chip, + unsigned int pipe, void *buf, struct scatterlist **sgptr, + unsigned int *offset, int num_sg, unsigned int length, + unsigned int *act_len, int timeout) +{ + int result; + + /* don't submit s-g requests during abort processing */ + if (test_bit(FLIDX_ABORTING, &chip->usb->dflags)) + TRACE_RET(chip, STATUS_ERROR); + + /* initialize the scatter-gather request block */ + RTS51X_DEBUGP("%s: xfer %u bytes, %d entries\n", __func__, + length, num_sg); + result = rts51x_sg_init_partial(&chip->usb->current_sg, + chip->usb->pusb_dev, pipe, 0, buf, sgptr, offset, + num_sg, length, GFP_NOIO); + if (result) { + RTS51X_DEBUGP("rts51x_sg_init_partial returned %d\n", result); + TRACE_RET(chip, STATUS_ERROR); + } + + /* since the block has been initialized successfully, it's now + * okay to cancel it */ + set_bit(FLIDX_SG_ACTIVE, &chip->usb->dflags); + + /* did an abort occur during the submission? */ + if (test_bit(FLIDX_ABORTING, &chip->usb->dflags)) { + + /* cancel the request, if it hasn't been cancelled already */ + if (test_and_clear_bit(FLIDX_SG_ACTIVE, &chip->usb->dflags)) { + RTS51X_DEBUGP("-- cancelling sg request\n"); + usb_sg_cancel(&chip->usb->current_sg); + } + } + + /* wait for the completion of the transfer */ + result = rts51x_sg_wait(&chip->usb->current_sg, timeout); + + clear_bit(FLIDX_SG_ACTIVE, &chip->usb->dflags); + + /* result = us->current_sg.status; */ + if (act_len) + *act_len = chip->usb->current_sg.bytes; + return interpret_urb_result(chip, pipe, length, result, + chip->usb->current_sg.bytes); +} +#endif +int rts51x_bulk_transfer_buf(struct rts51x_chip *chip, unsigned int pipe, + void *buf, unsigned int length, + unsigned int *act_len, int timeout) +{ + int result; + + /* fill and submit the URB */ + usb_fill_bulk_urb(chip->usb->current_urb, chip->usb->pusb_dev, pipe, + buf, length, urb_done_completion, NULL); + result = rts51x_msg_common(chip, chip->usb->current_urb, timeout); + + /* store the actual length of the data transferred */ + if (act_len) + *act_len = chip->usb->current_urb->actual_length; + return interpret_urb_result(chip, pipe, length, result, + chip->usb->current_urb->actual_length); +} + +int rts51x_transfer_data(struct rts51x_chip *chip, unsigned int pipe, + void *buf, unsigned int len, int use_sg, + unsigned int *act_len, int timeout) +{ + int result; + + if (timeout < 600) + timeout = 600; + + if (use_sg) { + result = + rts51x_bulk_transfer_sglist(chip, pipe, + (struct scatterlist *)buf, + use_sg, len, act_len, timeout); + } else { + result = + rts51x_bulk_transfer_buf(chip, pipe, buf, len, act_len, + timeout); + } + + return result; +} + +int rts51x_transfer_data_partial(struct rts51x_chip *chip, unsigned int pipe, + void *buf, void **ptr, unsigned int *offset, + unsigned int len, int use_sg, + unsigned int *act_len, int timeout) +{ + int result; + + if (timeout < 600) + timeout = 600; + + if (use_sg) { + void *tmp_buf = kmalloc(len, GFP_KERNEL); + if (!tmp_buf) + TRACE_RET(chip, STATUS_NOMEM); + + if (usb_pipeout(pipe)) { + rts51x_access_sglist(tmp_buf, len, buf, ptr, offset, + FROM_XFER_BUF); + } + result = + rts51x_bulk_transfer_buf(chip, pipe, tmp_buf, len, act_len, + timeout); + if (result == STATUS_SUCCESS) { + if (usb_pipein(pipe)) { + rts51x_access_sglist(tmp_buf, len, buf, ptr, + offset, TO_XFER_BUF); + } + } + + kfree(tmp_buf); +#if 0 + result = rts51x_bulk_transfer_sglist_partial(chip, pipe, buf, + (struct scatterlist **)ptr, offset, + use_sg, len, act_len, timeout); +#endif + } else { + unsigned int step = 0; + if (offset) + step = *offset; + result = + rts51x_bulk_transfer_buf(chip, pipe, buf + step, len, + act_len, timeout); + if (act_len) + step += *act_len; + else + step += len; + if (offset) + *offset = step; + } + + return result; +} + +int rts51x_get_epc_status(struct rts51x_chip *chip, u16 * status) +{ + unsigned int pipe = RCV_INTR_PIPE(chip); + struct usb_host_endpoint *ep; + struct completion urb_done; + int result; + + if (!status) + TRACE_RET(chip, STATUS_ERROR); + + /* set up data structures for the wakeup system */ + init_completion(&urb_done); + + ep = chip->usb->pusb_dev->ep_in[usb_pipeendpoint(pipe)]; + + /* fill and submit the URB */ + /* We set interval to 1 here, so the polling interval is controlled + * by our polling thread */ + usb_fill_int_urb(chip->usb->intr_urb, chip->usb->pusb_dev, pipe, + status, 2, urb_done_completion, &urb_done, 1); + + result = rts51x_msg_common(chip, chip->usb->intr_urb, 50); + + return interpret_urb_result(chip, pipe, 2, result, + chip->usb->intr_urb->actual_length); +} + +u8 media_not_present[] = { + 0x70, 0, 0x02, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0x3A, 0, 0, 0, 0, 0 }; +u8 invalid_cmd_field[] = { + 0x70, 0, 0x05, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0x24, 0, 0, 0, 0, 0 }; + +void rts51x_invoke_transport(struct scsi_cmnd *srb, struct rts51x_chip *chip) +{ + int result; + +#ifdef CONFIG_PM + if (chip->option.ss_en) { + if (srb->cmnd[0] == TEST_UNIT_READY) { + if (RTS51X_CHK_STAT(chip, STAT_SS)) { + if (check_fake_card_ready(chip, + SCSI_LUN(srb))) { + srb->result = SAM_STAT_GOOD; + } else { + srb->result = SAM_STAT_CHECK_CONDITION; + memcpy(srb->sense_buffer, + media_not_present, SENSE_SIZE); + } + return; + } + } else if (srb->cmnd[0] == ALLOW_MEDIUM_REMOVAL) { + if (RTS51X_CHK_STAT(chip, STAT_SS)) { + int prevent = srb->cmnd[4] & 0x1; + + if (prevent) { + srb->result = SAM_STAT_CHECK_CONDITION; + memcpy(srb->sense_buffer, + invalid_cmd_field, SENSE_SIZE); + } else { + srb->result = SAM_STAT_GOOD; + } + return; + } + } else { + if (RTS51X_CHK_STAT(chip, STAT_SS) + || RTS51X_CHK_STAT(chip, STAT_SS_PRE)) { + /* Wake up device */ + RTS51X_DEBUGP("Try to wake up device\n"); + chip->resume_from_scsi = 1; + + rts51x_try_to_exit_ss(chip); + + if (RTS51X_CHK_STAT(chip, STAT_SS)) { + wait_timeout(3000); + + rts51x_init_chip(chip); + rts51x_init_cards(chip); + } + } + } + } +#endif + + result = rts51x_scsi_handler(srb, chip); + + /* if there is a transport error, reset and don't auto-sense */ + if (result == TRANSPORT_ERROR) { + RTS51X_DEBUGP("-- transport indicates error, resetting\n"); + srb->result = DID_ERROR << 16; + goto Handle_Errors; + } + + srb->result = SAM_STAT_GOOD; + + /* + * If we have a failure, we're going to do a REQUEST_SENSE + * automatically. Note that we differentiate between a command + * "failure" and an "error" in the transport mechanism. + */ + if (result == TRANSPORT_FAILED) { + /* set the result so the higher layers expect this data */ + srb->result = SAM_STAT_CHECK_CONDITION; + memcpy(srb->sense_buffer, + (unsigned char *)&(chip->sense_buffer[SCSI_LUN(srb)]), + sizeof(struct sense_data_t)); + } + + return; + + /* Error and abort processing: try to resynchronize with the device + * by issuing a port reset. If that fails, try a class-specific + * device reset. */ +Handle_Errors: + return; +} |