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

[复制链接]
1506|0
 楼主| Simon21ic 发表于 2016-1-26 16:27 | 显示全部楼层 |阅读模式
本帖最后由 Simon21ic 于 2016-1-26 16:39 编辑

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

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

  7.         param->scsi_transact = NULL;
  8.         param->device = device;
  9.         vsfscsi_init(&param->scsi_dev);
  10.         vsfusbd_MSCBOT_on_idle(param);
  11.         return VSFERR_NONE;
  12. }

  13. static void vsfusbd_MSCBOT_on_idle(void *p)
  14. {
  15.         struct vsfusbd_MSCBOT_param_t *param = (struct vsfusbd_MSCBOT_param_t *)p;

  16.         param->bufstream.buffer.buffer = (uint8_t *)&param->CBW;
  17.         param->bufstream.buffer.size = sizeof(param->CBW);
  18.         param->bufstream.read = false;
  19.         param->stream.user_mem = &param->bufstream;
  20.         param->stream.op = &buffer_stream_op;
  21.         stream_init(&param->stream);

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

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

  12.         if (param->CBW.bCBWLUN > param->scsi_dev.max_lun)
  13.         {
  14.         reply_failure:
  15.                 vsfusbd_MSCBOT_ErrHandler(device, param, USBMSC_CSW_FAIL);
  16.                 return;
  17.         }

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

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

  27.                 if (!lun->transact.data_size)
  28.                 {
  29.                         goto reply_failure;
  30.                 }
  31.                 param->scsi_transact = &lun->transact;
  32.                 transact->data_size = param->scsi_transact->data_size;
  33.                 transact->stream = param->scsi_transact->stream;
  34.                 transact->cb.on_finish = vsfusbd_MSCBOT_on_data_finish;
  35.                 transact->cb.param = param;

  36.                 if ((param->CBW.bmCBWFlags & USBMSC_CBWFLAGS_DIR_MASK) ==
  37.                                         USBMSC_CBWFLAGS_DIR_IN)
  38.                 {
  39.                         transact->ep = param->ep_in;
  40.                         vsfusbd_ep_send(param->device, transact);
  41.                 }
  42.                 else
  43.                 {
  44.                         transact->ep = param->ep_out;
  45.                         vsfusbd_ep_recv(param->device, transact);
  46.                 }
  47.         }
  48.         else
  49.         {
  50.                 if (param->scsi_transact->data_size && param->scsi_transact)
  51.                 {
  52.                         vsfscsi_cancel_transact(param->scsi_transact);
  53.                         param->scsi_transact = NULL;
  54.                 }
  55.                 vsfusbd_MSCBOT_SendCSW(device, param);
  56.         }
  57. }
on_cbw里,会判断命令是否合法,然后调用vsfscsi_execute执行到SCSI层,会返回一个流(根据方向,可以输入也可以输出),然后根据方向,调用ep_send或者ep_recv,直接传入SCSI流。并且,完成后,回调vsfusbd_MSCBOT_on_data_finish。
  1. static void vsfusbd_MSCBOT_on_data_finish(void *p)
  2. {
  3.         struct vsfusbd_MSCBOT_param_t *param = (struct vsfusbd_MSCBOT_param_t *)p;
  4.         vsfusbd_MSCBOT_SendCSW(param->device, param);
  5. }
on_finish就只需要简单的调用发送CSW命令即可,当然,发送CSW命令也只是设置流,调用ep_send,并且设置回调为vsfusbd_MSCBOT_on_idle,继续下一个循环。


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

您需要登录后才可以回帖 登录 | 注册

本版积分规则

266

主题

2597

帖子

104

粉丝
快速回复 在线客服 返回列表 返回顶部