打印

U盘设备端驱动 -- 基于callback的状态机

[复制链接]
1029|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
Simon21ic|  楼主 | 2016-1-26 16:27 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 Simon21ic 于 2016-1-26 16:39 编辑

U盘设备端使用的是MassStorageClass协议,简单的说一下通信方式:
主机发送31字节的CBW命令,然后根据数据方向,主机发送或者接受N多个块的数据,最后设备发送17字节的CSW应答。

启动后:
vsf_err_t vsfusbd_MSCBOT_class_init(uint8_t iface,
           struct vsfusbd_device_t *device)
{
        struct vsfusbd_config_t *config = &device->config[device->configuration];
        struct vsfusbd_MSCBOT_param_t *param =
                        (struct vsfusbd_MSCBOT_param_t *)config->iface[iface].protocol_param;

        param->scsi_transact = NULL;
        param->device = device;
        vsfscsi_init(&param->scsi_dev);
        vsfusbd_MSCBOT_on_idle(param);
        return VSFERR_NONE;
}

static void vsfusbd_MSCBOT_on_idle(void *p)
{
        struct vsfusbd_MSCBOT_param_t *param = (struct vsfusbd_MSCBOT_param_t *)p;

        param->bufstream.buffer.buffer = (uint8_t *)&param->CBW;
        param->bufstream.buffer.size = sizeof(param->CBW);
        param->bufstream.read = false;
        param->stream.user_mem = &param->bufstream;
        param->stream.op = &buffer_stream_op;
        stream_init(&param->stream);

        param->transact.ep = param->ep_out;
        param->transact.data_size = sizeof(param->CBW);
        param->transact.stream = &param->stream;
        param->transact.cb.on_finish = vsfusbd_MSCBOT_on_cbw;
        param->transact.cb.param = param;
        vsfusbd_ep_recv(param->device, &param->transact);
}
这里初始化了CBW的流,并调用USB接口,接收EP数据,接收完成后,回调vsfusbd_MSCBOT_on_cbw。
static void vsfusbd_MSCBOT_on_cbw(void *p)
{
        struct vsfusbd_MSCBOT_param_t *param = (struct vsfusbd_MSCBOT_param_t *)p;
        struct vsfusbd_device_t *device = param->device;
        struct vsfscsi_lun_t *lun;

        if ((param->CBW.dCBWSignature != USBMSC_CBW_SIGNATURE) ||
                (param->CBW.bCBWCBLength < 1) || (param->CBW.bCBWCBLength > 16))
        {
                // TODO: invalid CBW, how to process this error?
                return;
        }

        if (param->CBW.bCBWLUN > param->scsi_dev.max_lun)
        {
        reply_failure:
                vsfusbd_MSCBOT_ErrHandler(device, param, USBMSC_CSW_FAIL);
                return;
        }

        param->CSW.dCSWStatus = USBMSC_CSW_OK;
        lun = &param->scsi_dev.lun[param->CBW.bCBWLUN];
        if (vsfscsi_execute(lun, param->CBW.CBWCB))
        {
                goto reply_failure;
        }

        if (param->CBW.dCBWDataTransferLength)
        {
                struct vsfusbd_transact_t *transact = &param->transact;

                if (!lun->transact.data_size)
                {
                        goto reply_failure;
                }
                param->scsi_transact = &lun->transact;
                transact->data_size = param->scsi_transact->data_size;
                transact->stream = param->scsi_transact->stream;
                transact->cb.on_finish = vsfusbd_MSCBOT_on_data_finish;
                transact->cb.param = param;

                if ((param->CBW.bmCBWFlags & USBMSC_CBWFLAGS_DIR_MASK) ==
                                        USBMSC_CBWFLAGS_DIR_IN)
                {
                        transact->ep = param->ep_in;
                        vsfusbd_ep_send(param->device, transact);
                }
                else
                {
                        transact->ep = param->ep_out;
                        vsfusbd_ep_recv(param->device, transact);
                }
        }
        else
        {
                if (param->scsi_transact->data_size && param->scsi_transact)
                {
                        vsfscsi_cancel_transact(param->scsi_transact);
                        param->scsi_transact = NULL;
                }
                vsfusbd_MSCBOT_SendCSW(device, param);
        }
}
on_cbw里,会判断命令是否合法,然后调用vsfscsi_execute执行到SCSI层,会返回一个流(根据方向,可以输入也可以输出),然后根据方向,调用ep_send或者ep_recv,直接传入SCSI流。并且,完成后,回调vsfusbd_MSCBOT_on_data_finish。
static void vsfusbd_MSCBOT_on_data_finish(void *p)
{
        struct vsfusbd_MSCBOT_param_t *param = (struct vsfusbd_MSCBOT_param_t *)p;
        vsfusbd_MSCBOT_SendCSW(param->device, param);
}
on_finish就只需要简单的调用发送CSW命令即可,当然,发送CSW命令也只是设置流,调用ep_send,并且设置回调为vsfusbd_MSCBOT_on_idle,继续下一个循环。


这样,虽然代码没显式的使用状态机,但是通过这些callback,也是实现了一个状态机,200行不到的代码,就能够实现MSC协议的传输部分。

相关帖子

发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

266

主题

2597

帖子

104

粉丝