/drivers/scsi/ps3rom.c
C | 459 lines | 339 code | 83 blank | 37 comment | 27 complexity | d3840a15a030834d49b0f9b8a52f1658 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0, AGPL-1.0
- /*
- * PS3 BD/DVD/CD-ROM Storage Driver
- *
- * Copyright (C) 2007 Sony Computer Entertainment Inc.
- * Copyright 2007 Sony Corp.
- *
- * 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; version 2 of the License.
- *
- * 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, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
- #include <linux/cdrom.h>
- #include <linux/highmem.h>
- #include <linux/slab.h>
- #include <scsi/scsi.h>
- #include <scsi/scsi_cmnd.h>
- #include <scsi/scsi_dbg.h>
- #include <scsi/scsi_device.h>
- #include <scsi/scsi_host.h>
- #include <scsi/scsi_eh.h>
- #include <asm/lv1call.h>
- #include <asm/ps3stor.h>
- #define DEVICE_NAME "ps3rom"
- #define BOUNCE_SIZE (64*1024)
- #define PS3ROM_MAX_SECTORS (BOUNCE_SIZE >> 9)
- struct ps3rom_private {
- struct ps3_storage_device *dev;
- struct scsi_cmnd *curr_cmd;
- };
- #define LV1_STORAGE_SEND_ATAPI_COMMAND (1)
- struct lv1_atapi_cmnd_block {
- u8 pkt[32]; /* packet command block */
- u32 pktlen; /* should be 12 for ATAPI 8020 */
- u32 blocks;
- u32 block_size;
- u32 proto; /* transfer mode */
- u32 in_out; /* transfer direction */
- u64 buffer; /* parameter except command block */
- u32 arglen; /* length above */
- };
- enum lv1_atapi_proto {
- NON_DATA_PROTO = 0,
- PIO_DATA_IN_PROTO = 1,
- PIO_DATA_OUT_PROTO = 2,
- DMA_PROTO = 3
- };
- enum lv1_atapi_in_out {
- DIR_WRITE = 0, /* memory -> device */
- DIR_READ = 1 /* device -> memory */
- };
- static int ps3rom_slave_configure(struct scsi_device *scsi_dev)
- {
- struct ps3rom_private *priv = shost_priv(scsi_dev->host);
- struct ps3_storage_device *dev = priv->dev;
- dev_dbg(&dev->sbd.core, "%s:%u: id %u, lun %u, channel %u\n", __func__,
- __LINE__, scsi_dev->id, scsi_dev->lun, scsi_dev->channel);
- /*
- * ATAPI SFF8020 devices use MODE_SENSE_10,
- * so we can prohibit MODE_SENSE_6
- */
- scsi_dev->use_10_for_ms = 1;
- /* we don't support {READ,WRITE}_6 */
- scsi_dev->use_10_for_rw = 1;
- return 0;
- }
- static int ps3rom_atapi_request(struct ps3_storage_device *dev,
- struct scsi_cmnd *cmd)
- {
- struct lv1_atapi_cmnd_block atapi_cmnd;
- unsigned char opcode = cmd->cmnd[0];
- int res;
- u64 lpar;
- dev_dbg(&dev->sbd.core, "%s:%u: send ATAPI command 0x%02x\n", __func__,
- __LINE__, opcode);
- memset(&atapi_cmnd, 0, sizeof(struct lv1_atapi_cmnd_block));
- memcpy(&atapi_cmnd.pkt, cmd->cmnd, 12);
- atapi_cmnd.pktlen = 12;
- atapi_cmnd.block_size = 1; /* transfer size is block_size * blocks */
- atapi_cmnd.blocks = atapi_cmnd.arglen = scsi_bufflen(cmd);
- atapi_cmnd.buffer = dev->bounce_lpar;
- switch (cmd->sc_data_direction) {
- case DMA_FROM_DEVICE:
- if (scsi_bufflen(cmd) >= CD_FRAMESIZE)
- atapi_cmnd.proto = DMA_PROTO;
- else
- atapi_cmnd.proto = PIO_DATA_IN_PROTO;
- atapi_cmnd.in_out = DIR_READ;
- break;
- case DMA_TO_DEVICE:
- if (scsi_bufflen(cmd) >= CD_FRAMESIZE)
- atapi_cmnd.proto = DMA_PROTO;
- else
- atapi_cmnd.proto = PIO_DATA_OUT_PROTO;
- atapi_cmnd.in_out = DIR_WRITE;
- scsi_sg_copy_to_buffer(cmd, dev->bounce_buf, dev->bounce_size);
- break;
- default:
- atapi_cmnd.proto = NON_DATA_PROTO;
- break;
- }
- lpar = ps3_mm_phys_to_lpar(__pa(&atapi_cmnd));
- res = lv1_storage_send_device_command(dev->sbd.dev_id,
- LV1_STORAGE_SEND_ATAPI_COMMAND,
- lpar, sizeof(atapi_cmnd),
- atapi_cmnd.buffer,
- atapi_cmnd.arglen, &dev->tag);
- if (res == LV1_DENIED_BY_POLICY) {
- dev_dbg(&dev->sbd.core,
- "%s:%u: ATAPI command 0x%02x denied by policy\n",
- __func__, __LINE__, opcode);
- return DID_ERROR << 16;
- }
- if (res) {
- dev_err(&dev->sbd.core,
- "%s:%u: ATAPI command 0x%02x failed %d\n", __func__,
- __LINE__, opcode, res);
- return DID_ERROR << 16;
- }
- return 0;
- }
- static inline unsigned int srb10_lba(const struct scsi_cmnd *cmd)
- {
- return cmd->cmnd[2] << 24 | cmd->cmnd[3] << 16 | cmd->cmnd[4] << 8 |
- cmd->cmnd[5];
- }
- static inline unsigned int srb10_len(const struct scsi_cmnd *cmd)
- {
- return cmd->cmnd[7] << 8 | cmd->cmnd[8];
- }
- static int ps3rom_read_request(struct ps3_storage_device *dev,
- struct scsi_cmnd *cmd, u32 start_sector,
- u32 sectors)
- {
- int res;
- dev_dbg(&dev->sbd.core, "%s:%u: read %u sectors starting at %u\n",
- __func__, __LINE__, sectors, start_sector);
- res = lv1_storage_read(dev->sbd.dev_id,
- dev->regions[dev->region_idx].id, start_sector,
- sectors, 0, dev->bounce_lpar, &dev->tag);
- if (res) {
- dev_err(&dev->sbd.core, "%s:%u: read failed %d\n", __func__,
- __LINE__, res);
- return DID_ERROR << 16;
- }
- return 0;
- }
- static int ps3rom_write_request(struct ps3_storage_device *dev,
- struct scsi_cmnd *cmd, u32 start_sector,
- u32 sectors)
- {
- int res;
- dev_dbg(&dev->sbd.core, "%s:%u: write %u sectors starting at %u\n",
- __func__, __LINE__, sectors, start_sector);
- scsi_sg_copy_to_buffer(cmd, dev->bounce_buf, dev->bounce_size);
- res = lv1_storage_write(dev->sbd.dev_id,
- dev->regions[dev->region_idx].id, start_sector,
- sectors, 0, dev->bounce_lpar, &dev->tag);
- if (res) {
- dev_err(&dev->sbd.core, "%s:%u: write failed %d\n", __func__,
- __LINE__, res);
- return DID_ERROR << 16;
- }
- return 0;
- }
- static int ps3rom_queuecommand_lck(struct scsi_cmnd *cmd,
- void (*done)(struct scsi_cmnd *))
- {
- struct ps3rom_private *priv = shost_priv(cmd->device->host);
- struct ps3_storage_device *dev = priv->dev;
- unsigned char opcode;
- int res;
- #ifdef DEBUG
- scsi_print_command(cmd);
- #endif
- priv->curr_cmd = cmd;
- cmd->scsi_done = done;
- opcode = cmd->cmnd[0];
- /*
- * While we can submit READ/WRITE SCSI commands as ATAPI commands,
- * it's recommended for various reasons (performance, error handling,
- * ...) to use lv1_storage_{read,write}() instead
- */
- switch (opcode) {
- case READ_10:
- res = ps3rom_read_request(dev, cmd, srb10_lba(cmd),
- srb10_len(cmd));
- break;
- case WRITE_10:
- res = ps3rom_write_request(dev, cmd, srb10_lba(cmd),
- srb10_len(cmd));
- break;
- default:
- res = ps3rom_atapi_request(dev, cmd);
-