《Linux那些事儿之我是USB》我是U盘(28)彼岸花的传说(五)--总结

static int usb_stor_control_thread(void * __us)
{
    struct us_data *us = (struct us_data *)__us;
    struct Scsi_Host *host = us_to_host(us);
    for (;;) {
        usb_stor_dbg(us, "*** thread sleeping\n");
        if (wait_for_completion_interruptible(&us->cmnd_ready))
           break;
        usb_stor_dbg(us, "*** thread awakened\n");
        /* lock the device pointers */
        mutex_lock(&(us->dev_mutex));
        /* lock access to the state */
        scsi_lock(host);
        /* When we are called with no command pending, we're done */
        if (us->srb == NULL) {
            scsi_unlock(host);
            mutex_unlock(&us->dev_mutex);
            usb_stor_dbg(us, "-- exiting\n");
            break;
        }
        /* has the command timed out *already* ? */
        if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) { //判断flag,US_FLDX_TIMED_OUT这个flag的含义超时了。
            us->srb->result = DID_ABORT << 16;
            goto SkipForAbort;
        }
        scsi_unlock(host);
        /* reject the command if the direction indicator
         * is UNKNOWN
         */
        if (us->srb->sc_data_direction == DMA_BIDIRECTIONAL) { //这个宏表示数据阶段数据传输是双向,srb的sc_data_direction是DMA_BIDIRECTIONAL时,自然就当做出错了。因为不确定方向的话也就没法传输数据了。
            usb_stor_dbg(us, "UNKNOWN data direction\n");
            us->srb->result = DID_ERROR << 16;
        }
        /* reject if target != 0 or if LUN is higher than
         * the maximum known LUN
         */
        else if (us->srb->device->id &&
                !(us->fflags & US_FL_SCM_MULT_TARG)) {
            usb_stor_dbg(us, "Bad target number (%d:%d)\n",
                     us->srb->device->id, us->srb->device->lun);
            us->srb->result = DID_BAD_TARGET << 16;
       }
//US_FL_SCM_MULT_TARG这个flag,表示设备支持多个target,对于那些不支持多个target的设备,其us->srb->device->id必须为0。
struct us_data结构体中的成员struct scsi_cmnd * srb,struct scsi_cmnd结构体中有一成员struct scsi_device * device,描述一个SCSI设备,就像过去的struct usb_device用来描述USB设备一样。
        else if (us->srb->device->lun > us->max_lun) {
            usb_stor_dbg(us, "Bad LUN (%d:%d)\n",
                     us->srb->device->id, us->srb->device->lun);
            us->srb->result = DID_BAD_TARGET << 16;
        }
//us->srb->device->lun不应该大于us->max_lun,us->max_lun早在usb_stor_scan_thread()中调用usb_stor_Bulk_max_lun()函数来向usb mass storage设备获得的最大LUN,比如MAX LUN等于3,那么这个设备支持的就是4个LUN,即0,1,2,3。而us->srb->device->lun则可以是这四个值中的任意一个,看传递进来的命令是要访问谁了。但它显然不可能超过MAX LUN。
        /* Handle those devices which need us to fake
         * their inquiry data */
        else if ((us->srb->cmnd[0] == INQUIRY) &&
                (us->fflags & US_FL_FIX_INQUIRY)) {
            unsigned char data_ptr[36] = {
                0x00, 0x80, 0x02, 0x02,
                0x1F, 0x00, 0x00, 0x00};
            usb_stor_dbg(us, "Faking INQUIRY command\n");
    fill_inquiry_response(us, data_ptr, 36);
            us->srb->result = SAM_STAT_GOOD;
        }
//flag-US_FL_FIX_INQUIRY,这又是us->flags中众多flag中的一个,一些定义于drivers/usb/storage/unusal_devs.h中的设备有这个flag。事实上,通常大多数设备的厂商名(Vendor Name)和产品名(Product Name)是通过INQUIRY命令来获得的,而这个flag表明,这些设备的厂商名和产品名不需要查询,或者根本就不支持查询,它们的厂商名和产品名直接就定义好了,在unusal_devs.h中就设好了。
        /* we've got a command, let's do it! */
        else {
            US_DEBUG(usb_stor_show_command(us, us->srb));
            us->proto_handler(us->srb, us);
            usb_mark_last_busy(us->pusb_dev);
        }
        /* lock access to the state */
        scsi_lock(host);
        /* indicate that the command is done */
        if (us->srb->result != DID_ABORT << 16) {
            usb_stor_dbg(us, "scsi cmd done, result=0x%x\n",
                     us->srb->result);
            us->srb->scsi_done(us->srb);
        } else {
SkipForAbort:
            usb_stor_dbg(us, "scsi command aborted\n");
        }
        /* If an abort request was received we need to signal that
         * the abort has finished.  The proper test for this is
         * the TIMED_OUT flag, not srb->result == DID_ABORT, because
         * the timeout might have occurred after the command had
         * already completed with a different result code. */
        if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) {
            complete(&(us->notify));
            /* Allow USB transfers to resume */
           clear_bit(US_FLIDX_ABORTING, &us->dflags);
            clear_bit(US_FLIDX_TIMED_OUT, &us->dflags);
        }
        /* finished working on this command */
        us->srb = NULL;
        scsi_unlock(host);
        /* unlock the device pointers */
        mutex_unlock(&us->dev_mutex);
    } /* for (;;) */
    /* Wait until we are told to stop */
    for (;;) {
        set_current_state(TASK_INTERRUPTIBLE);
        if (kthread_should_stop())
            break;
        schedule();
    }
    __set_current_state(TASK_RUNNING);
    return 0;
}
}
enum dma_data_direction {
    DMA_BIDIRECTIONAL = 0, //DMA_BIDIRECTIONAL表示两个方向都有可能,不知道究竟是哪个方向
    DMA_TO_DEVICE = 1,
    DMA_FROM_DEVICE = 2,
    DMA_NONE = 3,
};
这些代码被用来表示数据阶段数据传输的方向。DMA_TO_DEVICE表示从主存到设备,DMA_FROM_DEVICE表示从设备到主存。DMA_NONE则只被用于调试,一般不能使用否则将有可能导致内核崩溃。USB Mass Storage协议中边规定了双向传输是非法的,而一个命令传输零数据是合法的,比如TEST_UNIT_READY命令就不用传输数据。
struct scsi_device {
unsigned int id, lun, channel;
}
//unsigned int id,lun,channel这三个成员,定位一个SCSI设备必要的三个成员,一个SCSI卡所控制的设备被划分为几层,先是若干个channel,然后每个channel上有若干个target,每个target用一个target id来表示,然后一个target可以有若干个lun,判断的是target id。对于不支持多个target的设备,必须为0。对于绝大多数USB Mass Storage设备来说,它们的target id肯定为0。有些设备厂家就是要标新立异,它就是要让设备支持多个target,于是它就可以设置US_FL_SCM_MULT_TARG这么一个flag.

你可能感兴趣的:(《Linux那些事儿之我是USB》我是U盘(28)彼岸花的传说(五)--总结)