/****************************************/ /* MES/Copyleft Yukio Mituiwa,2005 */ /* */ /* 2006/6/16 first release */ /* */ /****************************************/ #include #include #ifdef _KERNEL_ #include "mes.h" #define device_main(arg1,arg2) init_scsi(void) #endif #include "usb.h" #include "scsi.h" #define CACHENUM 10 #define BLOCKNUM 8 #define BLOCKMSK 0xfffffff8 typedef struct { int usbfd; int drivesize; int offset; int lba[CACHENUM]; int total; char *blkcache; short epsize; char in, out; char lun; char use; char id; char desc[24]; } Drive; typedef struct { int lba __attribute__((packed)); int size __attribute__((packed)); } Capacity; #define SCSINUM 8 static Drive *scsiinfo; static CBW command; static CSW status; static char getmaxlun[8] = {TRANS_TO_HOST+TYPE_CLASS+TRANS_TO_IF,0xfe,0,0,0,0,1,0}; #define SCSI_RETRY 4 #define SECTOR_RETRY 500 static int set_command(int minor, int code, int rcvlen, int sendlen, int lba, char *buf); static void scsi_free(void); static void scsi_probe(int chip, int flag); static int seek_scsi(int minor, int position); static int read_scsi(int minor, char *buf, int size); static int write_flush(int minor); static int write_scsi(int minor, char *buf, int size); static int write_device(int minor, char *buf, int size, int lba); static int open_scsi(int minor, int opt); static int close_scsi(int minor); static int ioctl_scsi(int minor, int data, int op); static void wait_us(unsigned int times) { volatile int w; for(w = 0;w < times * WAITCONST;w++); } static int set_command(int minor, int code, int rcvlen, int sendlen, int lba, char *buf) { int none, cnt, size, ret; char cmdstr[8]; scsiinfo[minor].id++; switch(code) { case DEVICE_READY: memcpy(cmdstr, "USBCRDY-", 8); break; case READ_CAPACITY: memcpy(cmdstr, "USBCRCA-", 8); break; case READ_FORM_CAPA: memcpy(cmdstr, "USBCFCA-", 8); break; case READ_SECTOR: memcpy(cmdstr, "USBCRSE-", 8); break; case WRITE_SECTOR: memcpy(cmdstr, "USBCWSE-", 8); break; case WRITE_VERIFY: memcpy(cmdstr, "USBCWVR-", 8); break; case REQUEST_SENSE: memcpy(cmdstr, "USBCRQS-", 8); break; case INQUIRY: memcpy(cmdstr, "USBCINF-", 8); break; case MODE_SENSE: memcpy(cmdstr, "USBCMDS-", 8); break; } cmdstr[7] = scsiinfo[minor].id; none = 0; command.rcvlen = htole32(rcvlen); command.flag = TRANS_TO_HOST; command.lun = scsiinfo[minor].lun; command.sendlen = sendlen; switch(code) { case REQUEST_SENSE: case MODE_SENSE: case INQUIRY: case DEVICE_READY: command.cb.ufi.size = 0; command.cb.reqsense.code = code; command.cb.reqsense.size = rcvlen; command.cb.reqsense.pagecode = lba; command.cb.reqsense.nop1 = 0; command.cb.reqsense.nop2 = 0; command.cb.reqsense.nop3 = 0; break; case WRITE_SECTOR: command.flag = TRANS_TO_DEV; default: command.cb.ufi.code = code; command.cb.ufi.lun = 0; command.cb.ufi.lba = htobe32(lba); command.cb.ufi.size = htobe16(rcvlen); command.cb.ufi.nop1 = 0; command.cb.ufi.nop2 = 0; } switch(code) { case READ_CAPACITY: command.cb.ufi.size = 0; break; case READ_SECTOR: case WRITE_SECTOR: if(lba >= scsiinfo[minor].total) return -1; command.cb.ufi.size = htobe16(rcvlen >> 9); break; case DEVICE_READY: command.flag = TRANS_TO_DEV; none = 1; break; } memcpy(command.header, cmdstr, 8); cmdstr[3] = 'S'; usb_write(scsiinfo[minor].usbfd, (char*)&command, sizeof(command), scsiinfo[minor].out); size = 0; if(none == 0) { if(command.flag == TRANS_TO_HOST) { ret = usb_read(scsiinfo[minor].usbfd, buf, rcvlen, scsiinfo[minor].in); if(ret == sizeof(status) && memcmp(buf, cmdstr, 8) == 0) return 0; } else { ret = usb_write(scsiinfo[minor].usbfd, buf, rcvlen, scsiinfo[minor].out); } size = ret; } for(cnt = 0;cnt < SCSI_RETRY;cnt++) { ret = usb_read(scsiinfo[minor].usbfd, (char*)&status, sizeof(status), scsiinfo[minor].in); if(memcmp(status.header, cmdstr, 8) == 0) break; } if(ret < 0 || cnt == SCSI_RETRY) return -2; if(size < 0 || cnt > 0 || status.status != 0) return -1; return size; } static void scsi_probe(int chip, int flag) { int usb, drive, epnum, lunnum; int i, fd, lun, class, sub, addr, attr, ret; int retry, *lbanum; char c, *temp, driveinfo[8]; char addrs[MAX_EP], attrs[MAX_EP], epsizs[MAX_EP]; Drive cur; Capacity drivesize; temp = device_malloc(SuperTask, SECSIZ); if(temp == 0) return; drive = 0; for(usb = 1 + chip * USBNUM;usb < (chip + 1) * USBNUM;usb++) { if(usb_get_desc(usb, &fd, &class, &sub) == 0) { if(flag == 0) continue; } usb_get_conf(usb, &epnum, addrs, attrs, epsizs); if(class != MSSCLASS || sub != 6) continue; cur.epsize = device_ioctl(SuperTask, fd, 0, USB_EPSIZE); cur.in = cur.out = 0; for(i = 0;i < epnum;i++) { addr = addrs[i] & 0x0f; attr = attrs[i] & 0x03; if(attr == 2) { if(addrs[i] & 0x80) { cur.in = addr; } else { cur.out = addr; } } } if(cur.in == 0 || cur.out == 0) continue; device_ioctl(SuperTask, fd, 1, USB_SETCONF); device_ioctl(SuperTask, fd, (int)getmaxlun, USB_SETUP); device_read(SuperTask, fd, &c, 1); device_write(SuperTask, fd, 0, 0); lunnum = c + 1; for(lun = 0;lun < lunnum;lun++) { scsiinfo[drive].usbfd = fd; scsiinfo[drive].in = cur.in; scsiinfo[drive].out = cur.out; scsiinfo[drive].epsize = cur.epsize; scsiinfo[drive].drivesize = 0; scsiinfo[drive].lun = lun; for(retry = 0;retry < 3;retry++) { if(set_command(drive, INQUIRY, 36, sizeof(ReqSense), 0, temp) >= 0) { memcpy(&(scsiinfo[drive].desc[0]), &temp[8], 24); memcpy(driveinfo, temp, 8); break; } set_command(drive, REQUEST_SENSE, 18, 12, 0, temp); wait_ms(200); } if(driveinfo[0] != 0) { scsiinfo[drive].usbfd = 0; continue; } scsiinfo[drive].total = 0; for(retry = 0;retry < 6;retry++) { ret = set_command(drive, READ_FORM_CAPA, 252, sizeof(UFI), 0, temp); if(ret >= 0) { lbanum = (int*)&temp[4]; scsiinfo[drive].total = be32toh(*lbanum); //---------------------------------------------------------------- break; // see mes25r19 scsi.c scsi_probe() //---------------------------------------------------------------- } if(ret == -2) usb_read(scsiinfo[drive].usbfd, temp, 13, scsiinfo[drive].in); set_command(drive, REQUEST_SENSE, 18, 12, 0, temp); wait_ms(200); } for(retry = 0;retry < 6;retry++) { if(retry < 3) { if(set_command(drive, READ_CAPACITY, sizeof(Capacity), sizeof(UFI), 0, (char*)&drivesize) >= 0) { scsiinfo[drive].drivesize = be32toh(drivesize.lba); break; } } else { if(set_command(drive, READ_SECTOR, SECSIZ, sizeof(UFI), 0, temp) >= 0) break; } set_command(drive, REQUEST_SENSE, 18, 12, 0, temp); wait_ms(200); } set_command(drive, MODE_SENSE, 192, sizeof(ReqSense), 0x1c, temp); set_command(drive, MODE_SENSE, 192, sizeof(ReqSense), 0x3f, temp); if(scsiinfo[drive].drivesize == 0) { if(set_command(drive, READ_CAPACITY, sizeof(Capacity), sizeof(UFI), 0, (char*)&drivesize) >= 0) { scsiinfo[drive].drivesize = be32toh(drivesize.lba); } } if(scsiinfo[drive].drivesize > 0) { device_ioctl(SuperTask, scsiinfo[drive].usbfd, SECTOR_RETRY, USB_TIMEOUT); ret = set_command(drive, READ_SECTOR, SECSIZ, sizeof(UFI), 0, temp); device_ioctl(SuperTask, scsiinfo[drive].usbfd, 0, USB_TIMEOUT); if(flag == 0) { if(ret >= 0) { set_command(drive, DEVICE_READY, 0, 6, 0, 0); if(status.status == 0) { usb_alloc(usb); } else { scsiinfo[drive].usbfd = 0; } } else { scsiinfo[drive].usbfd = 0; } } } else { if(flag == 0) scsiinfo[drive].usbfd = 0; } if(++drive >= SCSINUM) { device_free(SuperTask, temp); return; } } } device_free(SuperTask, temp); } static void scsi_free(void) { int i; for(i = 0;i < SCSINUM;i++) { if(scsiinfo[i].usbfd != 0 && scsiinfo[i].lun == 0) { device_close(SuperTask, scsiinfo[i].usbfd); scsiinfo[i].usbfd = 0; } } } static int seek_scsi(int minor, int position) { if(minor >= SCSINUM) return -1; if(scsiinfo[minor].usbfd == 0) return -1; scsiinfo[minor].offset = position / SECSIZ; } static int read_scsi(int minor, char *buf, int size) { int i, stat, retry, err; char tmp[128]; if(minor >= SCSINUM) return -1; if(scsiinfo[minor].usbfd == 0) return -1; if(size == SECSIZ) { for(i = 0;i < CACHENUM;i++) { if(scsiinfo[minor].lba[i] == scsiinfo[minor].offset) { memcpy(buf, &scsiinfo[minor].blkcache[SECSIZ * i], SECSIZ); return SECSIZ; } } } device_ioctl(SuperTask, scsiinfo[minor].usbfd, SECTOR_RETRY, USB_TIMEOUT); device_ioctl(SuperTask, scsiinfo[minor].usbfd, SECTOR_RETRY, USB_WRITE_TIMEOUT); for(retry = 0;retry < SCSI_RETRY;retry++) { err = device_ioctl(SuperTask, scsiinfo[minor].usbfd, 0, USB_ERROR_STATUS); /* stat = set_command(minor, DEVICE_READY, 0, 6, 0, 0); if(stat < 0) { wait_ms(200); continue; }*/ stat = set_command(minor, READ_SECTOR, size, sizeof(UFI), scsiinfo[minor].offset, buf); err = device_ioctl(SuperTask, scsiinfo[minor].usbfd, 0, USB_ERROR_STATUS); if(err == USB_DEV_DOWN) { device_ioctl(SuperTask, scsiinfo[minor].usbfd, 0, USB_TIMEOUT); device_ioctl(SuperTask, scsiinfo[minor].usbfd, 0, USB_WRITE_TIMEOUT); device_ioctl(SuperTask, scsiinfo[minor].usbfd, 0, USB_RESET); wait_ms(2000); device_ioctl(SuperTask, scsiinfo[minor].usbfd, 0, DEV_RESET); device_ioctl(SuperTask, scsiinfo[minor].usbfd, (int)tmp, USB_INFO); scsi_probe(minor / USBNUM, 1); continue; } if(stat < 0) { retry = SCSI_RETRY; break; } if(status.status == 0 && stat == size) break; wait_ms(200); } device_ioctl(SuperTask, scsiinfo[minor].usbfd, 0, USB_TIMEOUT); device_ioctl(SuperTask, scsiinfo[minor].usbfd, 0, USB_WRITE_TIMEOUT); if(retry == SCSI_RETRY) return -1; return size; } static int write_device(int minor, char *buf, int size, int lba) { int stat, retry, err; char tmp[128]; device_ioctl(SuperTask, scsiinfo[minor].usbfd, SECTOR_RETRY, USB_TIMEOUT); device_ioctl(SuperTask, scsiinfo[minor].usbfd, SECTOR_RETRY, USB_WRITE_TIMEOUT); for(retry = 0;retry < SCSI_RETRY;retry++) { err = device_ioctl(SuperTask, scsiinfo[minor].usbfd, 0, USB_ERROR_STATUS); /* stat = set_command(minor, DEVICE_READY, 0, 6, 0, 0); if(stat < 0) { wait_ms(200); continue; }*/ stat = set_command(minor, WRITE_SECTOR, size, sizeof(UFI), lba, buf); err = device_ioctl(SuperTask, scsiinfo[minor].usbfd, 0, USB_ERROR_STATUS); if(err == USB_DEV_DOWN) { device_ioctl(SuperTask, scsiinfo[minor].usbfd, 0, USB_TIMEOUT); device_ioctl(SuperTask, scsiinfo[minor].usbfd, 0, USB_WRITE_TIMEOUT); device_ioctl(SuperTask, scsiinfo[minor].usbfd, 0, USB_RESET); wait_ms(2000); device_ioctl(SuperTask, scsiinfo[minor].usbfd, 0, DEV_RESET); device_ioctl(SuperTask, scsiinfo[minor].usbfd, (int)tmp, USB_INFO); scsi_probe(minor / USBNUM, 1); continue; } if(stat < 0) { retry = SCSI_RETRY; break; } if(err == USB_NO_ERROR && status.status == 0 && stat == size) break; wait_ms(200); } wait_ms(20); device_ioctl(SuperTask, scsiinfo[minor].usbfd, 0, USB_TIMEOUT); device_ioctl(SuperTask, scsiinfo[minor].usbfd, 0, USB_WRITE_TIMEOUT); if(retry == SCSI_RETRY) return -1; return size; } static int write_flush(int minor) { int ret, stat, i; ret = 0; for(i = 0;i < CACHENUM;i++) { if(scsiinfo[minor].lba[i] != -1) { stat = write_device(minor, &scsiinfo[minor].blkcache[SECSIZ * i], SECSIZ, scsiinfo[minor].lba[i]); scsiinfo[minor].lba[i] = -1; if(stat < 0) ret = -1; } } return ret; } static void write_block(int minor) { int i, j, stat; int cur, offset, count, mod; char *buffer; for(i = 0;i < CACHENUM;i++) { if(scsiinfo[minor].lba[i] == -1) continue; offset = scsiinfo[minor].lba[i] & BLOCKMSK; count = 0; for(j = i;j < CACHENUM;j++) { if(scsiinfo[minor].lba[j] == -1) continue; cur = scsiinfo[minor].lba[j] & BLOCKMSK; if(cur == offset) count++; } if(count == BLOCKNUM) { buffer = device_malloc(SuperTask, SECSIZ * BLOCKNUM); for(j = i;j < CACHENUM;j++) { cur = scsiinfo[minor].lba[j] & BLOCKMSK; if(cur == offset) { mod = scsiinfo[minor].lba[j] % BLOCKNUM; memcpy(&buffer[mod * SECSIZ], &scsiinfo[minor].blkcache[SECSIZ * j], SECSIZ); scsiinfo[minor].lba[j] = -1; } } stat = write_device(minor, buffer, SECSIZ * BLOCKNUM, offset); device_free(SuperTask, buffer); write_flush(minor); return; } } } static int write_scsi(int minor, char *buf, int size) { int ret, i; if(minor >= SCSINUM) return -1; if(scsiinfo[minor].usbfd == 0) return -1; if(size == SECSIZ) { for(i = 0;i < CACHENUM;i++) { if(scsiinfo[minor].lba[i] == scsiinfo[minor].offset) { memcpy(&scsiinfo[minor].blkcache[SECSIZ * i], buf, SECSIZ); return SECSIZ; } } for(i = 0;i < CACHENUM;i++) { if(scsiinfo[minor].lba[i] == -1) { scsiinfo[minor].lba[i] = scsiinfo[minor].offset; memcpy(&scsiinfo[minor].blkcache[SECSIZ * i], buf, SECSIZ); write_block(minor); return SECSIZ; } } write_flush(minor); scsiinfo[minor].lba[0] = scsiinfo[minor].offset; memcpy(scsiinfo[minor].blkcache, buf, SECSIZ); return SECSIZ; } return write_device(minor, buf, size, scsiinfo[minor].offset); } static int open_scsi(int minor, int opt) { int i, chip; if(opt == -1) { for(chip = 0;chip < SLNUM;chip++) scsi_probe(chip, 0); scsiinfo[minor].use = 2; return 0; } if(minor >= SCSINUM) return -1; if(scsiinfo[minor].usbfd == 0) return -1; if(scsiinfo[minor].use != 0) return -1; scsiinfo[minor].use = 1; for(i = 0;i < CACHENUM;i++) scsiinfo[minor].lba[i] = -1; scsiinfo[minor].blkcache = device_malloc(SuperTask, SECSIZ * CACHENUM); scsiinfo[minor].id = 0; return 0; } static int close_scsi(int minor) { if(scsiinfo[minor].use == 2) { scsiinfo[minor].use = 0; return 0; } if(minor >= SCSINUM) return -1; if(scsiinfo[minor].usbfd == 0) return -1; if(scsiinfo[minor].use == 0) return -1; write_flush(minor); scsiinfo[minor].use = 0; device_free(SuperTask, scsiinfo[minor].blkcache); return 0; } static int ioctl_scsi(int minor, int data, int op) { int ret; if(minor >= SCSINUM) return -1; if(scsiinfo[minor].usbfd == 0) return -1; if(scsiinfo[minor].use != 1) return -1; ret = 0; switch(op) { case DEV_INFO: break; case DEV_FLUSH: write_flush(minor); break; default: ret = -1; } return ret; } static Functions func; int device_main(int argc, char **argv) { int minor; scsiinfo = (Drive *)malloc(sizeof(Drive) * SCSINUM); for(minor = 0;minor < SCSINUM;minor++) { scsiinfo[minor].usbfd = 0; scsiinfo[minor].use = 0; } command.nop1 = 0; command.nop2 = 0; func.open_dev = open_scsi; func.close_dev = close_scsi; func.write_dev = write_scsi; func.read_dev = read_scsi; func.seek_dev = seek_scsi; func.ioctl_dev = ioctl_scsi; func.poll_dev = 0; strcpy(&(func.name[0]), "spc"); device_return(&func); }