diff options
Diffstat (limited to 'drivers/staging/rts5139/rts51x_transport.c')
-rw-r--r-- | drivers/staging/rts5139/rts51x_transport.c | 736 |
1 files changed, 0 insertions, 736 deletions
diff --git a/drivers/staging/rts5139/rts51x_transport.c b/drivers/staging/rts5139/rts51x_transport.c deleted file mode 100644 index 74588d249b32..000000000000 --- a/drivers/staging/rts5139/rts51x_transport.c +++ /dev/null @@ -1,736 +0,0 @@ -/* 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; -} - -static 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; -} - -static int rts51x_clear_halt(struct rts51x_chip *chip, unsigned int pipe); - -/* - * 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); -} - -static 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; -} - -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; - } - io->dev = NULL; -} - -static 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); -} - -static 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 the retry 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); -} - -static 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); - } 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 */ - /* Set interval to 10 here to match the endpoint descriptor, - * the polling interval is controlled by the polling thread */ - usb_fill_int_urb(chip->usb->intr_urb, chip->usb->pusb_dev, pipe, - status, 2, urb_done_completion, &urb_done, 10); - - result = rts51x_msg_common(chip, chip->usb->intr_urb, 100); - - return interpret_urb_result(chip, pipe, 2, result, - chip->usb->intr_urb->actual_length); -} - -static u8 media_not_present[] = { - 0x70, 0, 0x02, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0x3A, 0, 0, 0, 0, 0 }; -static 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; -} |