📄 sddr55.c
字号:
/* Driver for SanDisk SDDR-55 SmartMedia reader * * $Id:$ * * SDDR55 driver v0.1: * * First release * * Current development and maintenance by: * (c) 2002 Simon Munton * * 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, write to the Free Software Foundation, Inc., * 675 Mass Ave, Cambridge, MA 02139, USA. */#include "transport.h"#include "protocol.h"#include "usb.h"#include "debug.h"#include "sddr55.h"#include <linux/sched.h>#include <linux/errno.h>#include <linux/slab.h>#define short_pack(lsb,msb) ( ((u16)(lsb)) | ( ((u16)(msb))<<8 ) )#define LSB_of(s) ((s)&0xFF)#define MSB_of(s) ((s)>>8)#define PAGESIZE 512#define set_sense_info(sk, asc, ascq) \ do { \ info->sense_data[2] = sk; \ info->sense_data[12] = asc; \ info->sense_data[13] = ascq; \ } while (0)struct sddr55_card_info { unsigned long capacity; /* Size of card in bytes */ int max_log_blks; /* maximum number of logical blocks */ int pageshift; /* log2 of pagesize */ int smallpageshift; /* 1 if pagesize == 256 */ int blocksize; /* Size of block in pages */ int blockshift; /* log2 of blocksize */ int blockmask; /* 2^blockshift - 1 */ int read_only; /* non zero if card is write protected */ int force_read_only; /* non zero if we find a map error*/ int *lba_to_pba; /* logical to physical map */ int *pba_to_lba; /* physical to logical map */ int fatal_error; /* set if we detect something nasty */ unsigned long last_access; /* number of jiffies since we last talked to device */ unsigned char sense_data[18];};#define NOT_ALLOCATED 0xffffffff#define BAD_BLOCK 0xffff#define CIS_BLOCK 0x400#define UNUSED_BLOCK 0x3ffstatic int sddr55_raw_bulk(struct us_data *us, int direction, unsigned char *data, unsigned int len) { int result; int act_len; int pipe; if (direction == SCSI_DATA_READ) pipe = usb_rcvbulkpipe(us->pusb_dev, us->ep_in); else pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out); result = usb_stor_bulk_msg(us, data, pipe, len, &act_len); /* if we stall, we need to clear it before we go on */ if (result == -EPIPE) { US_DEBUGP("EPIPE: clearing endpoint halt for" " pipe 0x%x, stalled at %d bytes\n", pipe, act_len); usb_clear_halt(us->pusb_dev, pipe); } if (result) { /* NAK - that means we've retried a few times already */ if (result == -ETIMEDOUT) { US_DEBUGP("usbat_raw_bulk():" " device NAKed\n"); return US_BULK_TRANSFER_FAILED; } /* -ECONNRESET -- we canceled this transfer */ if (result == -ECONNRESET) { US_DEBUGP("usbat_raw_bulk():" " transfer aborted\n"); return US_BULK_TRANSFER_ABORTED; } if (result == -EPIPE) { US_DEBUGP("usbat_raw_bulk():" " output pipe stalled\n"); return US_BULK_TRANSFER_FAILED; } /* the catch-all case */ US_DEBUGP("us_transfer_partial(): unknown error\n"); return US_BULK_TRANSFER_FAILED; } if (act_len != len) { US_DEBUGP("Warning: Transferred only %d bytes\n", act_len); return US_BULK_TRANSFER_SHORT; } US_DEBUGP("Transferred %d of %d bytes\n", act_len, len); return US_BULK_TRANSFER_GOOD;}/* * Note: direction must be set if command_len == 0. */static int sddr55_bulk_transport(struct us_data *us, int direction, unsigned char *data, unsigned int len) { int result = USB_STOR_TRANSPORT_GOOD; struct sddr55_card_info *info = (struct sddr55_card_info *)us->extra; if (len==0) return USB_STOR_TRANSPORT_GOOD; info->last_access = jiffies;#ifdef CONFIG_USB_STORAGE_DEBUG if (direction == SCSI_DATA_WRITE) { int i; char string[64]; /* Debug-print the first 48 bytes of the write transfer */ strcpy(string, "wr: "); for (i=0; i<len && i<48; i++) { sprintf(string+strlen(string), "%02X ", data[i]); if ((i%16)==15) { US_DEBUGP("%s\n", string); strcpy(string, "wr: "); } } if ((i%16)!=0) US_DEBUGP("%s\n", string); }#endif /* transfer the data */ US_DEBUGP("SCM data %s transfer %d\n", ( direction==SCSI_DATA_READ ? "in" : "out"), len); result = sddr55_raw_bulk(us, direction, data, len);#ifdef CONFIG_USB_STORAGE_DEBUG if (direction == SCSI_DATA_READ) { int i; char string[64]; /* Debug-print the first 48 bytes of the read transfer */ strcpy(string, "rd: "); for (i=0; i<len && i<48; i++) { sprintf(string+strlen(string), "%02X ", data[i]); if ((i%16)==15) { US_DEBUGP("%s\n", string); strcpy(string, "rd: "); } } if ((i%16)!=0) US_DEBUGP("%s\n", string); }#endif return result;}/* check if card inserted, if there is, update read_only status * return non zero if no card */static int sddr55_status(struct us_data *us){ int result; unsigned char command[8] = { 0, 0, 0, 0, 0, 0xb0, 0, 0x80 }; unsigned char status[8]; struct sddr55_card_info *info = (struct sddr55_card_info *)us->extra; /* send command */ result = sddr55_bulk_transport(us, SCSI_DATA_WRITE, command, 8); US_DEBUGP("Result for send_command in status %d\n", result); if (result != US_BULK_TRANSFER_GOOD) { set_sense_info (4, 0, 0); /* hardware error */ return result; } result = sddr55_bulk_transport(us, SCSI_DATA_READ, status, 4); /* expect to get short transfer if no card fitted */ if (result == US_BULK_TRANSFER_SHORT) { /* had a short transfer, no card inserted, free map memory */ if (info->lba_to_pba) kfree(info->lba_to_pba); if (info->pba_to_lba) kfree(info->pba_to_lba); info->lba_to_pba = NULL; info->pba_to_lba = NULL; info->fatal_error = 0; info->force_read_only = 0; set_sense_info (2, 0x3a, 0); /* not ready, medium not present */ return result; } if (result != US_BULK_TRANSFER_GOOD) { set_sense_info (4, 0, 0); /* hardware error */ return result; } /* check write protect status */ info->read_only = (status[0] & 0x20); /* now read status */ result = sddr55_bulk_transport(us, SCSI_DATA_READ, status, 2); if (result != US_BULK_TRANSFER_GOOD) { set_sense_info (4, 0, 0); /* hardware error */ } return result;}static int sddr55_read_data(struct us_data *us, unsigned int lba, unsigned int page, unsigned short sectors, unsigned char *content, int use_sg) { int result; unsigned char command[8] = { 0, 0, 0, 0, 0, 0xb0, 0, 0x85 }; unsigned char status[8]; struct sddr55_card_info *info = (struct sddr55_card_info *)us->extra; unsigned int pba; unsigned long address; unsigned short pages; unsigned char *buffer = NULL; unsigned char *ptr; struct scatterlist *sg = NULL; int i; int len; int transferred; // If we're using scatter-gather, we have to create a new // buffer to read all of the data in first, since a // scatter-gather buffer could in theory start in the middle // of a page, which would be bad. A developer who wants a // challenge might want to write a limited-buffer // version of this code. len = sectors * PAGESIZE; if (use_sg) { sg = (struct scatterlist *)content; buffer = kmalloc(len, GFP_NOIO); if (buffer == NULL) return USB_STOR_TRANSPORT_ERROR; ptr = buffer; } else ptr = content; // This could be made much more efficient by checking for // contiguous LBA's. Another exercise left to the student. while (sectors>0) { /* have we got to end? */ if (lba >= info->max_log_blks) break; pba = info->lba_to_pba[lba]; // Read as many sectors as possible in this block pages = info->blocksize - page; if (pages > (sectors << info->smallpageshift)) pages = (sectors << info->smallpageshift); US_DEBUGP("Read %02X pages, from PBA %04X" " (LBA %04X) page %02X\n", pages, pba, lba, page); if (pba == NOT_ALLOCATED) { /* no pba for this lba, fill with zeroes */ memset (ptr, 0, pages << info->pageshift); } else { address = (pba << info->blockshift) + page; command[1] = LSB_of(address>>16); command[2] = LSB_of(address>>8); command[3] = LSB_of(address); command[6] = LSB_of(pages << (1 - info->smallpageshift)); /* send command */ result = sddr55_bulk_transport(us, SCSI_DATA_WRITE, command, 8); US_DEBUGP("Result for send_command in read_data %d\n", result); if (result != US_BULK_TRANSFER_GOOD) { if (use_sg) kfree(buffer); return result; } /* read data */ result = sddr55_bulk_transport(us, SCSI_DATA_READ, ptr, pages<<info->pageshift); if (result != US_BULK_TRANSFER_GOOD) { if (use_sg) kfree(buffer); return result; } /* now read status */ result = sddr55_bulk_transport(us, SCSI_DATA_READ, status, 2); if (result != US_BULK_TRANSFER_GOOD) { if (use_sg) kfree(buffer); return result; } /* check status for error */ if (status[0] == 0xff && status[1] == 0x4) { set_sense_info (3, 0x11, 0); if (use_sg) kfree(buffer); return USB_STOR_TRANSPORT_FAILED; } } page = 0; lba++; sectors -= pages >> info->smallpageshift; ptr += (pages << info->pageshift); } if (use_sg) { transferred = 0; for (i=0; i<use_sg && transferred<len; i++) { memcpy(sg[i].address, buffer+transferred, len-transferred > sg[i].length ? sg[i].length : len-transferred); transferred += sg[i].length; } kfree(buffer); } return USB_STOR_TRANSPORT_GOOD;}static int sddr55_write_data(struct us_data *us, unsigned int lba, unsigned int page, unsigned short sectors, unsigned char *content, int use_sg) { int result; unsigned char command[8] = { 0, 0, 0, 0, 0, 0xb0, 0, 0x86 }; unsigned char status[8]; struct sddr55_card_info *info = (struct sddr55_card_info *)us->extra; unsigned int pba; unsigned int new_pba; unsigned long address; unsigned short pages; unsigned char *buffer = NULL; unsigned char *ptr; struct scatterlist *sg = NULL; int i; int len; int transferred; /* check if we are allowed to write */ if (info->read_only || info->force_read_only) { set_sense_info (7, 0x27, 0); /* read only */ return USB_STOR_TRANSPORT_FAILED; } // If we're using scatter-gather, we have to create a new // buffer to write all of the data in first, since a // scatter-gather buffer could in theory start in the middle // of a page, which would be bad. A developer who wants a // challenge might want to write a limited-buffer // version of this code. len = sectors * PAGESIZE; if (use_sg) { sg = (struct scatterlist *)content; buffer = kmalloc(len, GFP_NOIO); if (buffer == NULL) return USB_STOR_TRANSPORT_ERROR; transferred = 0; for (i=0; i<use_sg && transferred<len; i++) { memcpy(buffer+transferred, sg[i].address, len-transferred > sg[i].length ? sg[i].length : len-transferred); transferred += sg[i].length; } ptr = buffer; } else ptr = content; while (sectors > 0) { /* have we got to end? */ if (lba >= info->max_log_blks) break; pba = info->lba_to_pba[lba]; // Write as many sectors as possible in this block pages = info->blocksize - page; if (pages > (sectors << info->smallpageshift)) pages = (sectors << info->smallpageshift); US_DEBUGP("Write %02X pages, to PBA %04X" " (LBA %04X) page %02X\n", pages, pba, lba, page); command[4] = 0; if (pba == NOT_ALLOCATED) { /* no pba allocated for this lba, find a free pba to use */ int max_pba = (info->max_log_blks / 250 ) * 256; int found_count = 0; int found_pba = -1; /* set pba to first block in zone lba is in */ pba = (lba / 1000) * 1024; US_DEBUGP("No PBA for LBA %04X\n",lba); if (max_pba > 1024) max_pba = 1024; /* scan through the map lookiong for an unused block * leave 16 unused blocks at start (or as many as possible) * since the sddr55 seems to reuse a used block when it shouldn't * if we don't leave space */ for (i = 0; i < max_pba; i++, pba++) { if (info->pba_to_lba[pba] == UNUSED_BLOCK) { found_pba = pba; if (found_count++ > 16) break; } } pba = found_pba; if (pba == -1) { /* oh dear, couldn't find an unallocated block */ US_DEBUGP("Couldn't find unallocated block\n"); set_sense_info (3, 0x31, 0); /* medium error */ if (use_sg) kfree(buffer); return USB_STOR_TRANSPORT_FAILED; } US_DEBUGP("Allocating PBA %04X for LBA %04X\n", pba, lba); /* set writing to unallocated block flag */ command[4] = 0x40; } address = (pba << info->blockshift) + page; command[1] = LSB_of(address>>16); command[2] = LSB_of(address>>8); command[3] = LSB_of(address); /* set the lba into the command, modulo 1000 */ command[0] = LSB_of(lba % 1000); command[6] = MSB_of(lba % 1000); command[4] |= LSB_of(pages >> info->smallpageshift); /* send command */ result = sddr55_bulk_transport(us, SCSI_DATA_WRITE, command, 8); if (result != US_BULK_TRANSFER_GOOD) { US_DEBUGP("Result for send_command in write_data %d\n", result); set_sense_info (3, 0x3, 0); /* peripheral write error */ if (use_sg) kfree(buffer); return result; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -