| /* 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 */ |
| /* 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; |
| } |