📄 scsi-linux-sg.c
字号:
int f; int tmo;{ tmo *= HZ; if (tmo) tmo += HZ/2; if (ioctl(f, SG_SET_TIMEOUT, &tmo) < 0) comerr("Cannot set SG_SET_TIMEOUT.\n");}/* * Get misaligned int. * Needed for all recent processors (sparc/ppc/alpha) * because the /dev/sg design forces us to do misaligned * reads of integers. */#ifdef MISALIGNLOCAL intscsi_getint(ip) int *ip;{ int ret; register char *cp = (char *)ip; register char *tp = (char *)&ret; register int i; for (i = sizeof(int); --i >= 0; ) *tp++ = *cp++; return (ret);}#define GETINT(a) scsi_getint(&(a))#else#define GETINT(a) (a)#endif#ifdef SG_IOLOCAL intscsi_send(scgp, f, sp) SCSI *scgp; int f; struct scg_cmd *sp;{ int ret; sg_io_hdr_t sg_io; if (f < 0) { sp->error = SCG_FATAL; sp->ux_errno = EIO; return (0); } if (scglocal(scgp)->isold > 0) { return (scsi_rwsend(scgp, f, sp)); } fillbytes((caddr_t)&sg_io, sizeof(sg_io), '\0'); sg_io.interface_id = 'S'; if (sp->flags & SCG_RECV_DATA) { sg_io.dxfer_direction = SG_DXFER_FROM_DEV; } else if (sp->size > 0) { sg_io.dxfer_direction = SG_DXFER_TO_DEV; } else { sg_io.dxfer_direction = SG_DXFER_NONE; } sg_io.cmd_len = sp->cdb_len; if (sp->sense_len > SG_MAX_SENSE) sg_io.mx_sb_len = SG_MAX_SENSE; else sg_io.mx_sb_len = sp->sense_len; sg_io.dxfer_len = sp->size; sg_io.dxferp = sp->addr; sg_io.cmdp = sp->cdb.cmd_cdb; sg_io.sbp = sp->u_sense.cmd_sense; sg_io.timeout = sp->timeout*1000; sg_io.flags |= SG_FLAG_DIRECT_IO; ret = ioctl(f, SG_IO, &sg_io); if (scgp->debug) printf("ioctl ret: %d\n", ret); if (ret < 0) { sp->ux_errno = geterrno(); /* * Check if SCSI command cound not be send at all. */ if (sp->ux_errno == EINVAL && scglocal(scgp)->isold < 0) { scglocal(scgp)->isold = 1; return (scsi_rwsend(scgp, f, sp)); } if (sp->ux_errno == ENOTTY || sp->ux_errno == ENXIO || sp->ux_errno == EINVAL || sp->ux_errno == EACCES) { return (-1); } } sp->u_scb.cmd_scb[0] = sg_io.status; sp->sense_count = sg_io.sb_len_wr; if (scgp->debug) printf("host_status: %02X driver_status: %02X\n", sg_io.host_status, sg_io.driver_status); switch (sg_io.host_status) { case DID_OK: if ((sg_io.driver_status & DRIVER_SENSE) != 0) sp->error = SCG_RETRYABLE; break; case DID_NO_CONNECT: /* Arbitration won, retry NO_CONNECT? */ case DID_BAD_TARGET: sp->error = SCG_FATAL; break; case DID_TIME_OUT: sp->error = SCG_TIMEOUT; break; default: sp->error = SCG_RETRYABLE; break; } if (sp->error && sp->ux_errno == 0) sp->ux_errno = EIO; sp->resid = sg_io.resid; return (0);}#else# define scsi_rwsend scsi_send#endifLOCAL intscsi_rwsend(scgp, f, sp) SCSI *scgp; int f; struct scg_cmd *sp;{ struct sg_rq *sgp; struct sg_rq *sgp2; int i; int pack_len; int reply_len; int amt = sp->cdb_len; struct sg_rq { struct sg_header hd; unsigned char buf[MAX_DMA_LINUX+SCG_MAX_CMD]; } sg_rq;#ifdef SG_GET_BUFSIZE /* We may use a 'sg' version 2 driver */ char driver_byte; char host_byte; char msg_byte; char status_byte;#endif if (f < 0) { sp->error = SCG_FATAL; sp->ux_errno = EIO; return (0); }#ifdef USE_PG if (scgp->scsibus == scglocal(scgp)->pgbus) return (pg_send(scgp, f, sp));#endif if (sp->timeout != scgp->deftimeout) sg_settimeout(f, sp->timeout); sgp2 = sgp = &sg_rq; if (sp->addr == scglocal(scgp)->SCSIbuf) { sgp = (struct sg_rq *) (scglocal(scgp)->SCSIbuf - (sizeof(struct sg_header) + amt)); sgp2 = (struct sg_rq *) (scglocal(scgp)->SCSIbuf - (sizeof(struct sg_header))); } else { if (scgp->debug) { printf("DMA addr: 0x%8.8lX size: %d - using copy buffer\n", (long)sp->addr, sp->size); } if (sp->size > (int)(sizeof(sg_rq.buf) - SCG_MAX_CMD)) { if (scglocal(scgp)->xbuf == NULL) { scglocal(scgp)->xbufsize = scgp->maxbuf; scglocal(scgp)->xbuf = malloc(scglocal(scgp)->xbufsize + SCG_MAX_CMD + sizeof(struct sg_header)); if (scgp->debug) { printf("Allocted DMA copy buffer, addr: 0x%8.8lX size: %ld\n", (long)scglocal(scgp)->xbuf, scgp->maxbuf); } } if (scglocal(scgp)->xbuf == NULL || sp->size > scglocal(scgp)->xbufsize) { errno = ENOMEM; return (-1); } sgp2 = sgp = (struct sg_rq *)scglocal(scgp)->xbuf; } } /* * This is done to avoid misaligned access of sgp->some_int */ pack_len = sizeof(struct sg_header) + amt; reply_len = sizeof(struct sg_header); if (sp->flags & SCG_RECV_DATA) { reply_len += sp->size; } else { pack_len += sp->size; }#ifdef MISALIGN /* * sgp->some_int may be misaligned if (sp->addr == SCSIbuf) * This is no problem on Intel porocessors, however * all other processors don't like it. * sizeof(struct sg_header) + amt is usually not a multiple of * sizeof(int). For this reason, we fill in the values into sg_rq * which is always corectly aligned and then copy it to the real * location if this location differs from sg_rq. * Never read/write directly to sgp->some_int !!!!! */ fillbytes((caddr_t)&sg_rq, sizeof(struct sg_header), '\0'); sg_rq.hd.pack_len = pack_len; sg_rq.hd.reply_len = reply_len; sg_rq.hd.pack_id = scglocal(scgp)->pack_id++;/* sg_rq.hd.result = 0; not needed because of fillbytes() */ if ((caddr_t)&sg_rq != (caddr_t)sgp) movebytes((caddr_t)&sg_rq, (caddr_t)sgp, sizeof(struct sg_header));#else fillbytes((caddr_t)sgp, sizeof(struct sg_header), '\0'); sgp->hd.pack_len = pack_len; sgp->hd.reply_len = reply_len; sgp->hd.pack_id = scglocal(scgp)->pack_id++;/* sgp->hd.result = 0; not needed because of fillbytes() */#endif if (amt == 12) sgp->hd.twelve_byte = 1; for (i = 0; i < amt; i++ ) { sgp->buf[i] = sp->cdb.cmd_cdb[i];; } if (!(sp->flags & SCG_RECV_DATA)) { if ((void *)sp->addr != (void *)&sgp->buf[amt]) movebytes(sp->addr, &sgp->buf[amt], sp->size); amt += sp->size; }#ifdef SG_GET_BUFSIZE sgp->hd.want_new = 1; /* Order new behaviour */ sgp->hd.cdb_len = sp->cdb_len; /* Set CDB length */ if (sp->sense_len > SG_MAX_SENSE) sgp->hd.sense_len = SG_MAX_SENSE; else sgp->hd.sense_len = sp->sense_len;#endif i = sizeof(struct sg_header) + amt; if ((amt = write(f, sgp, i)) < 0) { /* write */ sg_settimeout(f, scgp->deftimeout); return (-1); } else if (amt != i) { errmsg("scsi_send(%s) wrote %d bytes (expected %d).\n", scgp->cmdname, amt, i); } if (sp->addr == scglocal(scgp)->SCSIbuf) { movebytes(sgp, sgp2, sizeof(struct sg_header)); sgp = sgp2; } sgp->hd.sense_buffer[0] = 0; if ((amt = read(f, sgp, reply_len)) < 0) { /* read */ sg_settimeout(f, scgp->deftimeout); return (-1); } if (sp->flags & SCG_RECV_DATA && ((void *)sgp->buf != (void *)sp->addr)) { movebytes(sgp->buf, sp->addr, sp->size); } sp->ux_errno = GETINT(sgp->hd.result); /* Unaligned read */ sp->error = SCG_NO_ERROR;#ifdef SG_GET_BUFSIZE if (sgp->hd.grant_new) { sp->sense_count = sgp->hd.sense_len; pack_len = GETINT(sgp->hd.sg_cmd_status); /* Unaligned read */ driver_byte = (pack_len >> 24) & 0xFF; host_byte = (pack_len >> 16) & 0xFF; msg_byte = (pack_len >> 8) & 0xFF; status_byte = pack_len & 0xFF; switch (host_byte) { case DID_OK: if ((driver_byte & DRIVER_SENSE || sgp->hd.sense_buffer[0] != 0) && status_byte == 0) { /* * The Linux SCSI system up to 2.1.xx * trashes the status byte in the * kernel. This is true at least for * ide-scsi emulation. Until this gets * fixed, we need this hack. */ sp->error = SCG_RETRYABLE; status_byte = ST_CHK_COND; if (sgp->hd.sense_len == 0) sgp->hd.sense_len = SG_MAX_SENSE; } break; case DID_NO_CONNECT: /* Arbitration won, retry NO_CONNECT? */ case DID_BAD_TARGET: sp->error = SCG_FATAL; break; case DID_TIME_OUT: sp->error = SCG_TIMEOUT; break; default: sp->error = SCG_RETRYABLE; if ((driver_byte & DRIVER_SENSE || sgp->hd.sense_buffer[0] != 0) && status_byte == 0) status_byte = ST_CHK_COND; if (status_byte != 0 && sgp->hd.sense_len == 0) sgp->hd.sense_len = SG_MAX_SENSE; break; } if ((host_byte != DID_OK || status_byte != 0) && sp->ux_errno == 0) sp->ux_errno = EIO; sp->u_scb.cmd_scb[0] = status_byte; if (status_byte & ST_CHK_COND) { sp->sense_count = sgp->hd.sense_len; movebytes(sgp->hd.sense_buffer, sp->u_sense.cmd_sense, sp->sense_count); } } else#endif { if (GETINT(sgp->hd.result) == EBUSY) { /* Unaligned read */ struct timeval to; to.tv_sec = sp->timeout; to.tv_usec = 500000; scsitimes(scgp); if (scgp->cmdstop->tv_sec < to.tv_sec || (scgp->cmdstop->tv_sec == to.tv_sec && scgp->cmdstop->tv_usec < to.tv_usec)) { sp->ux_errno = 0; sp->error = SCG_TIMEOUT; /* a timeout */ } else { sp->error = SCG_RETRYABLE; /* may be BUS_BUSY */ } } if (sp->flags & SCG_RECV_DATA) sp->resid = (sp->size + sizeof(struct sg_header)) - amt; else sp->resid = 0; /* sg version1 cannot return DMA resid count */ if (sgp->hd.sense_buffer[0] != 0) { sp->error = SCG_RETRYABLE; sp->scb.chk = 1; sp->sense_count = SG_MAX_SENSE; movebytes(sgp->hd.sense_buffer, sp->u_sense.cmd_sense, sp->sense_count); if (sp->ux_errno == 0) sp->ux_errno = EIO; } } if (scgp->verbose > 0 && scgp->debug) {#ifdef SG_GET_BUFSIZE printf("status: 0x%08X pack_len: %d, reply_len: %d pack_id: %d result: %d wn: %d gn: %d cdb_len: %d sense_len: %d sense[0]: %02X\n", GETINT(sgp->hd.sg_cmd_status), GETINT(sgp->hd.pack_len), GETINT(sgp->hd.reply_len), GETINT(sgp->hd.pack_id), GETINT(sgp->hd.result), sgp->hd.want_new, sgp->hd.grant_new, sgp->hd.cdb_len, sgp->hd.sense_len, sgp->hd.sense_buffer[0]);#else printf("pack_len: %d, reply_len: %d pack_id: %d result: %d sense[0]: %02X\n", GETINT(sgp->hd.pack_len), GETINT(sgp->hd.reply_len), GETINT(sgp->hd.pack_id), GETINT(sgp->hd.result), sgp->hd.sense_buffer[0]);#endif#ifdef DEBUG printf("sense: "); for (i=0; i < 16; i++) printf("%02X ", sgp->hd.sense_buffer[i]); printf("\n");#endif } if (sp->timeout != scgp->deftimeout) sg_settimeout(f, scgp->deftimeout); return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -