📄 scsi.c
字号:
/* Now reset things so that req looks OK */ SCpnt->request.nr_sectors -= req->nr_sectors; req->current_nr_sectors = bh->b_size >> 9; req->buffer = bh->b_data; SCpnt->request.waiting = NULL; /* Wait until whole thing done */ } else req->dev = -1; } else { SCpnt->request.dev = 0xffff; /* Busy, but no request */ SCpnt->request.waiting = NULL; /* And no one is waiting for the device either */ }; SCpnt->use_sg = 0; /* Reset the scatter-gather flag */ SCpnt->old_use_sg = 0; SCpnt->transfersize = 0; SCpnt->underflow = 0; return SCpnt;}/* This function returns a structure pointer that will be valid forthe device. The wait parameter tells us whether we should wait forthe unit to become free or not. We are also able to tell this routinenot to return a descriptor if the host is unable to accept any morecommands for the time being. We need to keep in mind that there is noguarantee that the host remain not busy. Keep in mind therequest_queueable function also knows the internal allocation schemeof the packets for each device */Scsi_Cmnd * allocate_device (struct request ** reqp, int index, int wait){ int dev = -1; struct request * req = NULL; int tablesize; struct buffer_head * bh; struct Scsi_Host * host; Scsi_Cmnd * SCpnt = NULL; Scsi_Cmnd * SCwait = NULL; if ((index < 0) || (index > NR_SCSI_DEVICES)) panic ("Index number in allocate_device() is out of range.\n"); if (reqp) req = *reqp; /* See if this request has already been queued by an interrupt routine */ if (req && (dev = req->dev) <= 0) return NULL; host = scsi_devices[index].host; while (1==1){ SCpnt = host->host_queue; while(SCpnt){ if(SCpnt->target == scsi_devices[index].id && SCpnt->lun == scsi_devices[index].lun) { SCwait = SCpnt; if(SCpnt->request.dev < 0) break; }; SCpnt = SCpnt->next; }; cli(); /* See if this request has already been queued by an interrupt routine */ if (req && ((req->dev < 0) || (req->dev != dev))) { sti(); return NULL; }; if (!SCpnt || SCpnt->request.dev >= 0) /* Might have changed */ { sti(); if(!wait) return NULL; if (!SCwait) { printk("Attempt to allocate device index %d, target %d, lun %d\n", index, scsi_devices[index].id ,scsi_devices[index].lun); panic("No device found in allocate_device\n"); }; SCSI_SLEEP(&scsi_devices[SCwait->index].device_wait, (SCwait->request.dev > 0)); } else { if (req) { memcpy(&SCpnt->request, req, sizeof(struct request)); tablesize = scsi_devices[index].host->sg_tablesize; bh = req->bh; if(!tablesize) bh = NULL; /* Take a quick look through the table to see how big it is. We already have our copy of req, so we can mess with that if we want to. */ while(req->nr_sectors && bh){ tablesize--; req->nr_sectors -= bh->b_size >> 9; req->sector += bh->b_size >> 9; if(!tablesize) break; bh = bh->b_reqnext; }; if(req->nr_sectors && bh && bh->b_reqnext){ /* Any leftovers? */ SCpnt->request.bhtail = bh; req->bh = bh->b_reqnext; /* Divide request */ bh->b_reqnext = NULL; bh = req->bh; /* Now reset things so that req looks OK */ SCpnt->request.nr_sectors -= req->nr_sectors; req->current_nr_sectors = bh->b_size >> 9; req->buffer = bh->b_data; SCpnt->request.waiting = NULL; /* Wait until whole thing done */ } else { req->dev = -1; *reqp = req->next; }; } else { SCpnt->request.dev = 0xffff; /* Busy */ SCpnt->request.waiting = NULL; /* And no one is waiting for this to complete */ }; sti(); break; }; }; SCpnt->use_sg = 0; /* Reset the scatter-gather flag */ SCpnt->old_use_sg = 0; SCpnt->transfersize = 0; /* No default transfer size */ SCpnt->underflow = 0; /* Do not flag underflow conditions */ return SCpnt;}/* This is inline because we have stack problemes if we recurse to deeply.*/ inline void internal_cmnd (Scsi_Cmnd * SCpnt) { int temp; struct Scsi_Host * host;#ifdef DEBUG_DELAY int clock;#endif if ((unsigned long) &SCpnt < current->kernel_stack_page) panic("Kernel stack overflow."); host = SCpnt->host;/* We will wait MIN_RESET_DELAY clock ticks after the last reset so we can avoid the drive not being ready.*/ temp = host->last_reset;while (jiffies < temp);update_timeout(SCpnt, SCpnt->timeout_per_command);/* We will use a queued command if possible, otherwise we will emulate the queing and calling of completion function ourselves. */#ifdef DEBUG printk("internal_cmnd (host = %d, target = %d, command = %08x, buffer = %08x, \n" "bufflen = %d, done = %08x)\n", SCpnt->host->host_no, SCpnt->target, SCpnt->cmnd, SCpnt->buffer, SCpnt->bufflen, SCpnt->done);#endif if (host->hostt->can_queue) {#ifdef DEBUG printk("queuecommand : routine at %08x\n", host->hostt->queuecommand);#endif host->hostt->queuecommand (SCpnt, scsi_done); } else {#ifdef DEBUG printk("command() : routine at %08x\n", host->hostt->command);#endif temp=host->hostt->command (SCpnt); SCpnt->result = temp;#ifdef DEBUG_DELAY clock = jiffies + 400; while (jiffies < clock); printk("done(host = %d, result = %04x) : routine at %08x\n", host->host_no, temp, done);#endif scsi_done(SCpnt); } #ifdef DEBUG printk("leaving internal_cmnd()\n");#endif } static void scsi_request_sense (Scsi_Cmnd * SCpnt) { cli(); SCpnt->flags |= WAS_SENSE | ASKED_FOR_SENSE; update_timeout(SCpnt, SENSE_TIMEOUT); sti(); memcpy ((void *) SCpnt->cmnd , (void *) generic_sense, sizeof(generic_sense)); SCpnt->cmnd[1] = SCpnt->lun << 5; SCpnt->cmnd[4] = sizeof(SCpnt->sense_buffer); SCpnt->request_buffer = &SCpnt->sense_buffer; SCpnt->request_bufflen = sizeof(SCpnt->sense_buffer); SCpnt->use_sg = 0; internal_cmnd (SCpnt); SCpnt->use_sg = SCpnt->old_use_sg; }/* scsi_do_cmd sends all the commands out to the low-level driver. It handles the specifics required for each low level driver - ie queued or non queud. It also prevents conflicts when different high level drivers go for the same host at the same time.*/void scsi_do_cmd (Scsi_Cmnd * SCpnt, const void *cmnd , void *buffer, unsigned bufflen, void (*done)(Scsi_Cmnd *), int timeout, int retries ) { struct Scsi_Host * host = SCpnt->host;#ifdef DEBUG { int i; int target = SCpnt->target; printk ("scsi_do_cmd (host = %d, target = %d, buffer =%08x, " "bufflen = %d, done = %08x, timeout = %d, retries = %d)\n" "command : " , host->host_no, target, buffer, bufflen, done, timeout, retries); for (i = 0; i < 10; ++i) printk ("%02x ", ((unsigned char *) cmnd)[i]); printk("\n"); };#endif if (!host) { panic ("Invalid or not present host. %d\n", host->host_no); } /* We must prevent reentrancy to the lowlevel host driver. This prevents it - we enter a loop until the host we want to talk to is not busy. Race conditions are prevented, as interrupts are disabled inbetween the time we check for the host being not busy, and the time we mark it busy ourselves.*/ while (1==1){ cli(); if (host->hostt->can_queue && host->host_busy >= host->hostt->can_queue) { sti(); SCSI_SLEEP(&host->host_wait, (host->host_busy >= host->hostt->can_queue)); } else { host->host_busy++; sti(); break; }; };/* Our own function scsi_done (which marks the host as not busy, disables the timeout counter, etc) will be called by us or by the scsi_hosts[host].queuecommand() function needs to also call the completion function for the high level driver.*/ memcpy ((void *) SCpnt->data_cmnd , (void *) cmnd, 12);#if 0 SCpnt->host = host; SCpnt->target = target; SCpnt->lun = (SCpnt->data_cmnd[1] >> 5);#endif SCpnt->bufflen = bufflen; SCpnt->buffer = buffer; SCpnt->flags=0; SCpnt->retries=0; SCpnt->allowed=retries; SCpnt->done = done; SCpnt->timeout_per_command = timeout; memcpy ((void *) SCpnt->cmnd , (void *) cmnd, 12); /* Zero the sense buffer. Some host adapters automatically request sense on error. 0 is not a valid sense code. */ memset ((void *) SCpnt->sense_buffer, 0, sizeof SCpnt->sense_buffer); SCpnt->request_buffer = buffer; SCpnt->request_bufflen = bufflen; SCpnt->old_use_sg = SCpnt->use_sg; /* Start the timer ticking. */ SCpnt->internal_timeout = 0; internal_cmnd (SCpnt);#ifdef DEBUG printk ("Leaving scsi_do_cmd()\n");#endif }/* The scsi_done() function disables the timeout timer for the scsi host, marks the host as not busy, and calls the user specified completion function for that host's current command.*/static void reset (Scsi_Cmnd * SCpnt){#ifdef DEBUG printk("scsi: reset(%d)\n", SCpnt->host->host_no);#endif SCpnt->flags |= (WAS_RESET | IS_RESETTING); scsi_reset(SCpnt); #ifdef DEBUG printk("performing request sense\n");#endif if(SCpnt->flags & NEEDS_JUMPSTART) { SCpnt->flags &= ~NEEDS_JUMPSTART; scsi_request_sense (SCpnt); };} static int check_sense (Scsi_Cmnd * SCpnt) { /* If there is no sense information, request it. If we have already requested it, there is no point in asking again - the firmware must be confused. */ if (((SCpnt->sense_buffer[0] & 0x70) >> 4) != 7) { if(!(SCpnt->flags & ASKED_FOR_SENSE)) return SUGGEST_SENSE; else return SUGGEST_RETRY; } SCpnt->flags &= ~ASKED_FOR_SENSE;#ifdef DEBUG_INIT printk("scsi%d : ", SCpnt->host->host_no); print_sense("", SCpnt); printk("\n");#endif if (SCpnt->sense_buffer[2] &0xe0) return SUGGEST_ABORT; switch (SCpnt->sense_buffer[2] & 0xf) { case NO_SENSE: return 0; case RECOVERED_ERROR: if (scsi_devices[SCpnt->index].type == TYPE_TAPE) return SUGGEST_IS_OK; else return 0; case ABORTED_COMMAND: return SUGGEST_RETRY; case NOT_READY: case UNIT_ATTENTION: return SUGGEST_ABORT; /* these three are not supported */ case COPY_ABORTED: case VOLUME_OVERFLOW: case MISCOMPARE: case MEDIUM_ERROR: return SUGGEST_REMAP; case BLANK_CHECK: case DATA_PROTECT: case HARDWARE_ERROR: case ILLEGAL_REQUEST: default: return SUGGEST_ABORT; } }/* This function is the mid-level interrupt routine, which decides how * to handle error conditions. Each invocation of this function must * do one and *only* one of the following: * * (1) Call last_cmnd[host].done. This is done for fatal errors and * normal completion, and indicates that the handling for this * request is complete. * (2) Call internal_cmnd to requeue the command. This will result in * scsi_done being called again when the retry is complete. * (3) Call scsi_request_sense. This asks the host adapter/drive for * more information about the error condition. When the information * is available, scsi_done will be called again. * (4) Call reset(). This is sort of a last resort, and the idea is that * this may kick things loose and get the drive working again. reset() * automatically calls scsi_request_sense, and thus scsi_done will be * called again once the reset is complete. * * If none of the above actions are taken, the drive in question * will hang. If more than one of the above actions are taken by * scsi_done, then unpredictable behavior will result. */static void scsi_done (Scsi_Cmnd * SCpnt) { int status=0; int exit=0; int checked; int oldto; struct Scsi_Host * host = SCpnt->host; int result = SCpnt->result; oldto = update_timeout(SCpnt, 0);#define FINISHED 0#define MAYREDO 1#define REDO 3#define PENDING 4#ifdef DEBUG printk("In scsi_done(host = %d, result = %06x)\n", host->host_no, result);#endif switch (host_byte(result)) { case DID_OK: if (SCpnt->flags & IS_RESETTING) { SCpnt->flags &= ~IS_RESETTING; status = REDO; break; } if (status_byte(result) && (SCpnt->flags & WAS_SENSE)) /* Failed to obtain sense information */ { SCpnt->flags &= ~WAS_SENSE; SCpnt->internal_timeout &= ~SENSE_TIMEOUT; if (!(SCpnt->flags & WAS_RESET)) { printk("scsi%d : target %d lun %d request sense failed, performing reset.\n", SCpnt->host->host_no, SCpnt->target, SCpnt->lun); reset(SCpnt); return; } else { exit = (DRIVER_HARD | SUGGEST_ABORT); status = FINISHED; } } else switch(msg_byte(result)) { case COMMAND_COMPLETE: switch (status_byte(result)) { case GOOD: if (SCpnt->flags & WAS_SENSE) {#ifdef DEBUG printk ("In scsi_done, GOOD status, COMMAND COMPLETE, parsing sense information.\n");#endif SCpnt->flags &= ~WAS_SENSE; SCpnt->internal_timeout &= ~SENSE_TIMEOUT; switch (checked = check_sense(SCpnt)) { case SUGGEST_SENSE: case 0: #ifdef DEBUG printk("NO SENSE. status = REDO\n");#endif update_timeout(SCpnt, oldto); status = REDO; break; case SUGGEST_IS_OK: break; case SUGGEST_REMAP: case SUGGEST_RETRY: #ifdef DEBUG printk("SENSE SUGGEST REMAP or SUGGEST RETRY - status = MAYREDO\n");#endif status = MAYREDO; exit = DRIVER_SENSE | SUGGEST_RETRY; break; case SUGGEST_ABORT:#ifdef DEBUG printk("SENSE SUGGEST ABORT - status = FINISHED");#endif status = FINISHED; exit = DRIVER_SENSE | SUGGEST_ABORT; break; default: printk ("Internal error %s %d \n", __FILE__, __LINE__); } } else {#ifdef DEBUG printk("COMMAND COMPLETE message returned, status = FINISHED. \n");#endif exit = DRIVER_OK; status = FINISHED; } break; case CHECK_CONDITION: switch (check_sense(SCpnt)) { case 0: update_timeout(SCpnt, oldto); status = REDO; break; case SUGGEST_REMAP: case SUGGEST_RETRY: status = MAYREDO; exit = DRIVER_SENSE | SUGGEST_RETRY; break; case SUGGEST_ABORT: status = FINISHED; exit = DRIVER_SENSE | SUGGEST_ABORT; break; case SUGGEST_SENSE: scsi_request_sense (SCpnt); status = PENDING; break; } break; case CONDITION_GOOD: case INTERMEDIATE_GOOD: case INTERMEDIATE_C_GOOD: break; case BUSY: update_timeout(SCpnt, oldto); status = REDO; break; case RESERVATION_CONFLICT: printk("scsi%d : RESERVATION CONFLICT performing reset.\n", SCpnt->host->host_no); reset(SCpnt); return;#if 0 exit = DRIVER_SOFT | SUGGEST_ABORT; status = MAYREDO; break;#endif default: printk ("Internal error %s %d \n" "status byte = %d \n", __FILE__, __LINE__, status_byte(result)); } break; default: panic("scsi: unsupported message byte %d recieved\n", msg_byte(result)); } break; case DID_TIME_OUT: #ifdef DEBUG printk("Host returned DID_TIME_OUT - ");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -