Logo Search packages:      
Sourcecode: faumachine version File versions  Download package

usbstorage.c

/*
 * $Id: usbstorage.c,v 1.25 2009-01-28 12:59:22 potyra Exp $
 *
 * Copyright (C) 2003-2009 FAUmachine Team <info@faumachine.org>.
 * This program is free software. You can redistribute it and/or modify it
 * under the terms of the GNU General Public License, either version 2 of
 * the License, or (at your option) any later version. See COPYING.
 */

#define DEBUG_CONTROL_FLOW    0

/* Phison Electronics Corp. */
#define VENDOR_ID 0x0d7d
/* Flash Disk */
#define PRODUCT_ID      0x1300


#include <assert.h>
#include <fcntl.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define MIN(x, y) ((x) < (y) ? (x) : (y))

#include "fixme.h"
#include "glue-log.h"
#include "glue-main.h"
#include "glue-shm.h"
#include "glue-storage.h"
#include "lib_usb.h"
#include "usb_device_descriptor.h"

#include "sig_ide.h"    /* FIXME */
#include "cd_image.h"   /* FIXME */

#define INCLUDE
#include "arch_scsi_gen_disk.c"
#undef INCLUDE

#include "usbstorage.h"

#define COMP      "usbstorage"

/* FIXME */
/*
 * Config Options
 */
#define CD_AUDIO_SUPPORT                0
#define CD_WRITER_SUPPORT               0
#define CD_CHANGER_SUPPORT              0

/* CD_WRITER_SUPPORT */
#define CD_WRITER_RW_SUPPORT            0
#define CD_WRITER_DAO_SUPPORT           0

#define CD_FORMAT_UNIT_SUPPORT          0
#define CD_READ_BUFFER_CAPACITY_SUPPORT 0
#define CD_READ_MASTER_CUE_SUPPORT      0
#define CD_SEND_CUE_SHEET_SUPPORT       0
#define CD_SEND_OPC_INFORMATION_SUPPORT 0

#define CD_PLAY_CD_SUPPORT              0
#define CD_READ_CD_SUPPORT              0
#define CD_SCAN_CD_SUPPORT              0

struct cpssp {
      struct sig_usb_bus_main *port_usb_main;

      int state_power;

      enum {
            STATE_ILLEGAL = 0,
            STATE_ATTACHED,
            STATE_POWERED,
            STATE_DEFAULT,
            STATE_ADDRESS,
            STATE_CONFIGURED,
      } state;

      int addr;

      int token_pid;
      int token_addr;
      int token_endp;

      uint8_t request;
      uint8_t request_wvalue;

      struct {
            enum {
                  STAGE_NONE,
                  STAGE_SETUP,
                  STAGE_DATA,
                  STAGE_STATUS,
            } stage;
            uint8_t toggle;
            uint8_t buffer[4096];
            unsigned int head;
            unsigned int tail;
      } control, in, out, status;

      uint32_t usbc_tag;
      uint32_t usbc_datalength;
      uint8_t usbc_lun;
      uint8_t usbc_flags;
      uint8_t usbc_cmd_head;
      uint8_t usbc_cmd_tail;
      uint8_t usbc_cmd[16];

      unsigned int cmd_flag;
      unsigned int data_flag;
      unsigned int msg_flag;
      unsigned int in_flag;
      unsigned int req_count;

      uint32_t residue;

#define STATE

#define NAME            disk
#define NAME_(x)  disk_ ## x
#define SNAME           "disk"
#include "arch_scsi_gen_disk.c"
#undef SNAME
#undef NAME_
#undef NAME

#undef STATE
};

static const struct usb_device_descriptor usbstorage_device_desc = {
      .bLength = sizeof usbstorage_device_desc, /* Size of Descriptor */
      .bDescriptorType = USB_DESCRIPTOR_TYPE_DEVICE, /* Device Descriptor (0x01) */

      .bcdUSB = 0x0110, /* USB Specification Number */
      .bDeviceClass = 0, /* Device class */
      .bDeviceSubClass = 0,
      .bDeviceProtocol = 0,
      .bMaxPacketSize0 = 8,
      .idVendor = VENDOR_ID, /* Vendor ID */
      .idProduct = PRODUCT_ID, /* Product ID */
      .bcdDevice = 0x0101, /* Device Release Number */
      .iManufacturer = 0, /* String Index Manufacturer*/
      .iProduct = 0, /* String Index Product */
      .iSerialNumber = 0, /* String Index Serial Number */
      .bNumConfigurations = 1, /* Number of Possible Configurations */
};

static const struct __attribute__((__packed__)) {
      struct usb_configuration_descriptor conf0;
      struct usb_interface_descriptor if0;
      struct usb_endpoint_descriptor ep1;
      struct usb_endpoint_descriptor ep2;
} usbstorage_configuration_desc = {
      .conf0 = {
            /* Size of Descriptor */
            .bLength = sizeof(struct usb_configuration_descriptor),

            /* Configuration Descriptor (0x02) */
            .bDescriptorType = USB_DESCRIPTOR_TYPE_CONFIGURATION,

            /* Length of whole Configuration Descriptor */
            .wTotalLength = sizeof(usbstorage_configuration_desc),

            /* Number of Interfaces */
            .bNumInterfaces = 1,

            /* Value to use as an argument to select this configuration */
            .bConfigurationValue = 1,

            /* String Index of String Describing this Configuration */
            .iConfiguration = 0,

            /* Attributes */
            .bmAttributes = (1 << 7)      /* Reserved */
                        | (1 << 6), /* Self Powered */

            /* Maximal Power Consumption */
            .MaxPower = 1, /* Minimal as we're self-powered. */
      },
      .if0 = {
            /* Size of Descriptor */
            .bLength = sizeof(struct usb_interface_descriptor),

            /* Interface Descriptor (0x04) */
            .bDescriptorType = USB_DESCRIPTOR_TYPE_INTERFACE,

            /* Number of Interface */
            .bInterfaceNumber = 0,

            /* Value to Select Alternate Setting */
            .bAlternateSetting = 0,

            /* Number of Endpoints (here only 2: in and out) */
            .bNumEndpoints = 2,

            /* Class Code (Mass Storage) */
            .bInterfaceClass = 8,

            /* Subclass Code (Transparent SCSI) */
            .bInterfaceSubClass = 6,

            /* Protocol Code */
            .bInterfaceProtocol = 0x50, /* Bulk Only */

            /* String Index for Interface Name */
            .iInterface = 0,
      },
      .ep1 = {
            /* Size of Descriptor */
            .bLength = sizeof(struct usb_endpoint_descriptor),

            /* Endpoint Descriptor */
            .bDescriptorType = USB_DESCRIPTOR_TYPE_ENDPOINT,

            /* Endpoint Address and Direction */
            .bEndpointAddress = (1 << 7) /* In Endpoint */
                        | (0 << 4) /* Reserved */
                        | (1 << 0), /* Endpoint Number */

            /* Endpoint Attributes */
            .bmAttributes = (0 << 2) /* Reserved */
                        | (2 << 0), /* Bulk */

            /* Maximum Packet Size */
            .wMaxPacketSize = 64,

            /* Interval for Polling - Ignored for Bulk Endpoints */
            .bInterval = 0,
      },
      .ep2 = {
            /* Size of Descriptor */
            .bLength = sizeof(struct usb_endpoint_descriptor),

            /* Endpoint Descriptor */
            .bDescriptorType = USB_DESCRIPTOR_TYPE_ENDPOINT,

            /* Endpoint Address and Direction */
            .bEndpointAddress = (0 << 7) /* Out Endpoint */
                        | (0 << 4) /* Reserved */
                        | (2 << 0), /* Endpoint Number */

            /* Endpoint Attributes */
            .bmAttributes = (0 << 2) /* Reserved */
                        | (2 << 0), /* Bulk */

            /* Maximum Packet Size */
            .wMaxPacketSize = 64,

            /* Interval for Polling - Ignored for Bulk Endpoints */
            .bInterval = 0,
      },
};

/*forward*/ static void
disk_atn_set(struct cpssp *cpssp, unsigned int val);
/*forward*/ static int
disk_phase_select(struct cpssp *cpssp, uint32_t id);
/*forward*/ static int
disk_recv(struct cpssp *cpssp, uint8_t *buf, unsigned int bufsize);
/*forward*/ static int
disk_send(struct cpssp *cpssp, const uint8_t *buf, unsigned int bufsize);


static void
disk_want_recv(struct cpssp *cpssp, unsigned int count)
{
      uint8_t byte;

      if (DEBUG_CONTROL_FLOW) {
                fprintf(stderr, "%s: called(count=%u)\n", __FUNCTION__, count);
      }

      assert(! cpssp->in_flag);

      cpssp->req_count = count;

      if (cpssp->cmd_flag) {
            /* Command Phase */
            assert(count <= cpssp->usbc_cmd_head - cpssp->usbc_cmd_tail);

            cpssp->req_count = disk_send(cpssp,
                        &cpssp->usbc_cmd[cpssp->usbc_cmd_tail], count);
            cpssp->usbc_cmd_tail += count;

      } else if (cpssp->data_flag) {
            /* Data Out Phase */
            /* Nothing to do here... */

      } else if (cpssp->msg_flag) {
            /* Message Out Phase */
            /* Send LUN. */
            byte = 0x80 + cpssp->usbc_lun;
            cpssp->req_count = disk_send(cpssp, &byte, sizeof(byte));
            assert(cpssp->req_count == 0);

      } else {
            /* Free Phase */
            assert(0); /* Mustn't happen. */
      }
}

static void
disk_want_send(struct cpssp *cpssp, unsigned int count)
{
      uint8_t byte;

      if (DEBUG_CONTROL_FLOW) {
                fprintf(stderr, "%s: called(count=%u)\n", __FUNCTION__, count);
      }

      assert(cpssp->in_flag);

      cpssp->req_count = count;

      if (cpssp->cmd_flag) {
            /* Status Phase */
            /* Nothing to do here... */

      } else if (cpssp->data_flag) {
            /* Data In Phase */
            /* Nothing to do here... */

      } else if (cpssp->msg_flag) {
            /* Message In Phase */
            /* Receive completion byte. */
            cpssp->req_count = disk_recv(cpssp, &byte, sizeof(byte));
            assert(byte == 0x00);

            assert(cpssp->req_count == 0);

      } else {
            assert(0); /* Mustn't happen. */
      }
}

static void
disk_phase_free(struct cpssp *cpssp)
{
      if (DEBUG_CONTROL_FLOW) {
                fprintf(stderr, "%s: called()\n", __FUNCTION__);
      }

      cpssp->cmd_flag = 0;
      cpssp->data_flag = 0;
      cpssp->msg_flag = 0;
      cpssp->in_flag = 0;
}

static void
disk_phase_cmd(struct cpssp *cpssp)
{
      if (DEBUG_CONTROL_FLOW) {
                fprintf(stderr, "%s: called()\n", __FUNCTION__);
      }

      cpssp->cmd_flag = 1;
      cpssp->data_flag = 0;
      cpssp->msg_flag = 0;
      cpssp->in_flag = 0;
}

static void
disk_phase_status(struct cpssp *cpssp)
{
      if (DEBUG_CONTROL_FLOW) {
                fprintf(stderr, "%s: called()\n", __FUNCTION__);
      }

      cpssp->cmd_flag = 1;
      cpssp->data_flag = 0;
      cpssp->msg_flag = 0;
      cpssp->in_flag = 1;
}

static void
disk_phase_data_in(struct cpssp *cpssp)
{
      if (DEBUG_CONTROL_FLOW) {
                fprintf(stderr, "%s: called()\n", __FUNCTION__);
      }

      cpssp->cmd_flag = 0;
      cpssp->data_flag = 1;
      cpssp->msg_flag = 0;
      cpssp->in_flag = 1;
}

static void
disk_phase_data_out(struct cpssp *cpssp)
{
      if (DEBUG_CONTROL_FLOW) {
                fprintf(stderr, "%s: called()\n", __FUNCTION__);
      }

      cpssp->cmd_flag = 0;
      cpssp->data_flag = 1;
      cpssp->msg_flag = 0;
      cpssp->in_flag = 0;
}

static void
disk_phase_msg_in(struct cpssp *cpssp)
{
      if (DEBUG_CONTROL_FLOW) {
                fprintf(stderr, "%s: called()\n", __FUNCTION__);
      }

      cpssp->cmd_flag = 0;
      cpssp->data_flag = 0;
      cpssp->msg_flag = 1;
      cpssp->in_flag = 1;
}

static void
disk_phase_msg_out(struct cpssp *cpssp)
{
      if (DEBUG_CONTROL_FLOW) {
                fprintf(stderr, "%s: called()\n", __FUNCTION__);
      }

      cpssp->cmd_flag = 0;
      cpssp->data_flag = 0;
      cpssp->msg_flag = 1;
      cpssp->in_flag = 0;
}

#define BEHAVIOR

#define USB       1
#define NAME            disk
#define NAME_(x)  disk_ ## x
#define SNAME           "disk"
#include "arch_scsi_gen_disk.c"
#undef SNAME
#undef NAME_
#undef NAME
#undef USB

#undef BEHAVIOR

static uint8_t
usbstorage_toggle(uint8_t pid)
{
      switch (pid) {
      case USB_PID_DATA0: return USB_PID_DATA1;
      case USB_PID_DATA1: return USB_PID_DATA0;
      default: assert(0); /* Mustn't happen. */
      }
}

static void
usbstorage_power_set(void *_cpssp, unsigned int val)
{
      struct cpssp *cpssp = _cpssp;
      
      if (DEBUG_CONTROL_FLOW) {
            fprintf(stderr, "%s: %u\n", __FUNCTION__, val);
      }

      cpssp->state_power = val;

      cpssp->state = val ? STATE_POWERED : STATE_ATTACHED;

      sig_usb_bus_speed_set(cpssp->port_usb_main, cpssp,
                        val ? USB_SPEED_FULL : USB_SPEED_UNCONNECTED);

      disk_power_set(cpssp, val);
}

static void
usbstorage_reset_set(void *_cpssp, int val)
{
      struct cpssp *cpssp = _cpssp;

      if (DEBUG_CONTROL_FLOW) {
            fprintf(stderr, "%s: %d\n", __FUNCTION__, val);
      }

      cpssp->state = STATE_DEFAULT;
      cpssp->addr = 0;

      cpssp->token_pid = -1;
      cpssp->token_addr = -1;
      cpssp->token_endp = -1;

      cpssp->control.stage = STAGE_NONE;
      cpssp->control.toggle = USB_PID_DATA0;
      cpssp->control.head = 0;
      cpssp->control.tail = 0;

      cpssp->in.toggle = USB_PID_DATA0;
      cpssp->in.head = 0;
      cpssp->in.tail = 0;

      cpssp->out.toggle = USB_PID_DATA0;
      cpssp->out.head = 0;
      cpssp->out.tail = 0;

      cpssp->status.head = 0;
      cpssp->status.tail = 0;
}

static void
usbstorage_recv_token_control(struct cpssp *cpssp)
{
      unsigned int len;

      if (DEBUG_CONTROL_FLOW) {
            fprintf(stderr, "%s\n", __FUNCTION__);
      }

      len = cpssp->control.head - cpssp->control.tail;
      if (8 < len) {
            len = 8;
      }
      if (len == 0) {
            if (DEBUG_CONTROL_FLOW) {
                  fprintf(stderr, "%s no more bytes\n", __FUNCTION__);
            }
            sig_usb_bus_send_handshake(cpssp->port_usb_main, cpssp,
                        USB_PID_NAK);
      } else {
            if (DEBUG_CONTROL_FLOW) {
                  fprintf(stderr, "%s sending %d bytes\n", __FUNCTION__, len);
            }
            sig_usb_bus_send_data(cpssp->port_usb_main, cpssp,
                        cpssp->control.toggle,
                        len,
                        cpssp->control.buffer + cpssp->control.tail,
                        0);
      }
}

static void
usbstorage_recv_data_control(
      struct cpssp *cpssp,
      unsigned int length,
      uint8_t *data
)
{
      switch (cpssp->control.stage) {
      case STAGE_SETUP: {
            uint8_t bmRequestType;
            uint8_t bRequest;
            uint16_t wValue;
            uint16_t wIndex;
            uint16_t wLength;

            if (DEBUG_CONTROL_FLOW) {
                  fprintf(stderr, "%s: %d %02x %02x %02x %02x %02x %02x %02x %02x\n",
                        __FUNCTION__, length,
                        data[0], data[1], data[2], data[3],
                        data[4], data[5], data[6], data[7]);
            }

            assert(length == 8);
            
            bmRequestType = data[0];
            bRequest = data[1];
            wValue = (data[3] << 8) | data[2];
            wIndex = (data[5] << 8) | data[4];
            wLength = (data[7] << 8) | data[6];

            /* For SET_ADDRESS/SET_CONFIGURATION requests. */
            cpssp->request = bRequest;
            cpssp->request_wvalue = wValue;

            if (DEBUG_CONTROL_FLOW) {
                  fprintf(stderr, "%s: 0x%02x 0x%02x 0x%04x 0x%04x 0x%04x\n",
                        __FUNCTION__,
                        bmRequestType, bRequest,
                        wValue, wIndex, wLength);
            }

            switch ((bmRequestType << 8) | bRequest) {
#if 0
            case USB_DEV_REQ_GET_STATUS:
                  fprintf(stderr, "%s: get_status\n", __FUNCTION__);
                  break;
#endif
            case (0 << (8+7)) /* Host-to-Device */
               | (0 << (8+5)) /* Standard */
               | (0 << (8+0)) /* Device */
               | USB_DEV_REQ_CLEAR_FEATURE:
                  if (DEBUG_CONTROL_FLOW) {
                        fprintf(stderr, "%s: clear_feature (device)\n", __FUNCTION__);
                  }
                  cpssp->control.head = 0;
                  cpssp->control.tail = 0;
                  break;

            case (0 << (8+7)) /* Host-to-Device */
               | (0 << (8+5)) /* Standard */
               | (1 << (8+0)) /* Interface */
               | USB_DEV_REQ_CLEAR_FEATURE:
                  if (DEBUG_CONTROL_FLOW) {
                        fprintf(stderr, "%s: clear_feature (interface)\n", __FUNCTION__);
                  }
                  cpssp->control.head = 0;
                  cpssp->control.tail = 0;
                  break;

            case (0 << (8+7)) /* Host-to-Device */
               | (0 << (8+5)) /* Standard */
               | (2 << (8+0)) /* Endpoint */
               | USB_DEV_REQ_CLEAR_FEATURE:
                  if (DEBUG_CONTROL_FLOW) {
                        fprintf(stderr, "%s: clear_feature (endpoint)\n", __FUNCTION__);
                  }
                  cpssp->control.head = 0;
                  cpssp->control.tail = 0;
                  break;

            case (0 << (8+7)) /* Host-to-Device */
               | (0 << (8+5)) /* Standard */
               | (0 << (8+0)) /* Device */
               | USB_DEV_REQ_SET_FEATURE:
                  if (DEBUG_CONTROL_FLOW) {
                        fprintf(stderr, "%s: set_feature (device)\n", __FUNCTION__);
                  }
                  cpssp->control.head = 0;
                  cpssp->control.tail = 0;
                  break;

            case (0 << (8+7)) /* Host-to-Device */
               | (0 << (8+5)) /* Standard */
               | (1 << (8+0)) /* Interface */
               | USB_DEV_REQ_SET_FEATURE:
                  if (DEBUG_CONTROL_FLOW) {
                        fprintf(stderr, "%s: set_feature (interface)\n", __FUNCTION__);
                  }
                  cpssp->control.head = 0;
                  cpssp->control.tail = 0;
                  break;

            case (0 << (8+7)) /* Host-to-Device */
               | (0 << (8+5)) /* Standard */
               | (2 << (8+0)) /* Endpoint */
               | USB_DEV_REQ_SET_FEATURE:
                  if (DEBUG_CONTROL_FLOW) {
                        fprintf(stderr, "%s: set_feature (endpoint)\n", __FUNCTION__);
                  }
                  cpssp->control.head = 0;
                  cpssp->control.tail = 0;
                  break;

            case (0 << (8+7)) /* Host-to-Device */
               | (0 << (8+5)) /* Standard */
               | (0 << (8+0)) /* Device */
               | USB_DEV_REQ_SET_ADDRESS:
                  if (DEBUG_CONTROL_FLOW) {
                        fprintf(stderr, "%s: set_address\n", __FUNCTION__);
                  }
                  /* New address gets valid after handshake! */
                  cpssp->control.head = 0;
                  cpssp->control.tail = 0;
                  break;

            case (1 << (8+7)) /* Device-to-Host */
               | (0 << (8+5)) /* Standard */
               | (0 << (8+0)) /* Device */
               | USB_DEV_REQ_GET_DESCRIPTOR: {
                  uint8_t descriptor_type;
                  uint8_t descriptor_index;

                  if (DEBUG_CONTROL_FLOW) {
                        fprintf(stderr, "%s: get_desciptor\n", __FUNCTION__);
                  }
                  descriptor_type = (wValue >> 8) & 0xff;
                  descriptor_index = (wValue >> 0) & 0xff;

                  switch (descriptor_type) {
                  case USB_DESCRIPTOR_TYPE_DEVICE:
                        if (DEBUG_CONTROL_FLOW) {
                              fprintf(stderr, "%s: device\n", __FUNCTION__);
                        }
                        assert(sizeof(usbstorage_device_desc)
                                    <= sizeof(cpssp->control.buffer));
                        memcpy(cpssp->control.buffer, &usbstorage_device_desc,
                                    sizeof(usbstorage_device_desc));

                        cpssp->control.head = MIN(wLength,
                                    sizeof(usbstorage_device_desc));
                        cpssp->control.tail = 0;
                        break;

                  case USB_DESCRIPTOR_TYPE_CONFIGURATION:
                        if (DEBUG_CONTROL_FLOW) {
                              fprintf(stderr, "%s: configuration\n", __FUNCTION__);
                        }
                        assert(sizeof(usbstorage_configuration_desc)
                                    <= sizeof(cpssp->control.buffer));
                        memcpy(cpssp->control.buffer, &usbstorage_configuration_desc,
                                    sizeof(usbstorage_configuration_desc));

                        cpssp->control.head = MIN(wLength,
                                    sizeof(usbstorage_configuration_desc));
                        cpssp->control.tail = 0;
                        break;

                  case USB_DESCRIPTOR_TYPE_STRING:
                        if (DEBUG_CONTROL_FLOW) {
                              fprintf(stderr, "%s: string\n", __FUNCTION__);
                        }
                        assert(0); /* FIXME */
                        break;

                  default:
                        fprintf(stderr, "WARNING: %s: Unknown descriptor type.\n",
                                    COMP);
                        break;
                  }
                  break;
                }
#if 0
            case USB_DEV_REQ_SET_DESCRIPTOR:
                  if (DEBUG_CONTROL_FLOW) {
                        fprintf(stderr, "%s: set_desciptor\n", __FUNCTION__);
                  }
                  cpssp->control.head = 0;
                  cpssp->control.tail = 0;
                  break;
            case USB_DEV_REQ_GET_CONFIGURATION:
                  if (DEBUG_CONTROL_FLOW) {
                        fprintf(stderr, "%s: get_configuration\n", __FUNCTION__);
                  }
                  cpssp->control.head = 0;
                  cpssp->control.tail = 0;
                  break;
#endif
            case (0 << (8+7)) /* Host-to-Device */
               | (0 << (8+5)) /* Standard */
               | (0 << (8+0)) /* Device */
               | USB_DEV_REQ_SET_CONFIGURATION:
                  if (DEBUG_CONTROL_FLOW) {
                        fprintf(stderr, "%s: set_configuration\n", __FUNCTION__);
                  }
                  /* New configuration gets valid after handshake! */
                  cpssp->control.head = 0;
                  cpssp->control.tail = 0;
                  break;
#if 0
            case USB_DEV_REQ_GET_INTERFACE:
                  if (DEBUG_CONTROL_FLOW) {
                        fprintf(stderr, "%s: get_interface\n", __FUNCTION__);
                  }
                  cpssp->control.head = 0;
                  cpssp->control.tail = 0;
                  break;
            case USB_DEV_REQ_SET_INTERFACE:
                  if (DEBUG_CONTROL_FLOW) {
                        fprintf(stderr, "%s: set_interface\n", __FUNCTION__);
                  }
                  cpssp->control.head = 0;
                  cpssp->control.tail = 0;
                  break;
            case USB_DEV_REQ_SYNCH_FRAME:
                  if (DEBUG_CONTROL_FLOW) {
                        fprintf(stderr, "%s: synch_frame\n", __FUNCTION__);
                  }
                  cpssp->control.head = 0;
                  cpssp->control.tail = 0;
                  break;
#endif

            case (1 << (8+7)) /* Host-to-Device */
               | (1 << (8+5)) /* Class */
               | (1 << (8+0)) /* Interface */
               | 0xfe:
                  /* Get Max LUN */
                  cpssp->control.buffer[0] = 0;
                  cpssp->control.head = MIN(wLength, 1);
                  cpssp->control.tail = 0;
                  break;

            case (0 << (8+7)) /* Host-to-Device */
               | (1 << (8+5)) /* Class */
               | (1 << (8+0)) /* Interface */
               | 0xff:
                  /* Reset BULK */
                  cpssp->control.head = 0;
                  cpssp->control.tail = 0;
                  break;

            default:
                  fprintf(stderr, "WARNING: %s: Unknown request 0x%02x.\n",
                              COMP, bRequest);
                  break;
            }

            /* Advance stage. */
            if (wLength == 0) {
                  cpssp->control.stage = STAGE_STATUS;
                  cpssp->control.buffer[0] = 0;
                  cpssp->control.head = 1;
                  cpssp->control.tail = 0;
            } else {
                  cpssp->control.stage = STAGE_DATA;
            }
            break;
          }
      case STAGE_DATA:
            /* Only vendor-specific host-to-device requests -- just ACK */
            break;

      case STAGE_STATUS:
            if (DEBUG_CONTROL_FLOW) {
                  fprintf(stderr, "%s: stage_status\n", __FUNCTION__);
            }
            cpssp->control.stage = STAGE_NONE;
            cpssp->control.buffer[0] = 0;
            cpssp->control.head = 1;
            cpssp->control.tail = 0;
            break;
      default:
            assert(0); /* Mustn't happen. */
      }

      cpssp->control.toggle = usbstorage_toggle(cpssp->control.toggle);
      sig_usb_bus_send_handshake(cpssp->port_usb_main, cpssp, USB_PID_ACK);
}

static void
usbstorage_recv_handshake_control(struct cpssp *cpssp, int pid)
{
      switch (cpssp->control.stage) {
      case STAGE_DATA:
            if (pid == USB_PID_ACK) {
                  unsigned int len;

                  cpssp->control.toggle = usbstorage_toggle(cpssp->control.toggle);
                  len = cpssp->control.head - cpssp->control.tail;
                  if (8 < len) {
                        len = 8;
                  }
                  cpssp->control.tail += len;
            }
            break;

      case STAGE_STATUS:
            switch (cpssp->request) {
            case USB_DEV_REQ_SET_ADDRESS:
                  /* New address gets valid now. */
                  cpssp->addr = cpssp->request_wvalue & 0xf;

                  cpssp->state = (cpssp->addr == 0)
                              ? STATE_DEFAULT : STATE_ADDRESS;
                  break;

            case USB_DEV_REQ_SET_CONFIGURATION:
                  /* New configuration gets valid now. */
                  switch (cpssp->state) {
                  case STATE_ADDRESS:
                        if ((cpssp->request_wvalue & 0xff) == 1) {
                              /* Configured now. */
                              cpssp->state = STATE_CONFIGURED;
                        } else {
                              assert(0); /* FIXME */
                        }
                        break;
                  case STATE_CONFIGURED:
                        if ((cpssp->request_wvalue & 0xff) == 0) {
                              /* Deconfigured now. */
                              cpssp->state = STATE_ADDRESS;
                        } else {
                              assert(0); /* FIXME */
                        }
                        break;
                  default:
                        assert(0); /* FIXME */
                  }

            default:
                  /* Nothing to do... */
                  ;
            }

            cpssp->control.stage = STAGE_NONE;
            cpssp->control.toggle = USB_PID_DATA0;
            break;

      default:
            assert(0); /* Mustn't happen. */
      }
}

static void
usbstorage_recv_token_in(struct cpssp *cpssp)
{
      unsigned int len;

      if (DEBUG_CONTROL_FLOW) {
            fprintf(stderr, "%s\n", __FUNCTION__);
      }

      if (cpssp->in.head - cpssp->in.tail == 0
       && cpssp->req_count == 0) {
            /*
             * No more bytes (yet).
             */
            if (DEBUG_CONTROL_FLOW) {
                  fprintf(stderr, "%s no more bytes\n", __FUNCTION__);
            }
            sig_usb_bus_send_handshake(cpssp->port_usb_main, cpssp,
                        USB_PID_NAK);
            return;
      }

      if (cpssp->in.head - cpssp->in.tail == 0) {
            /*
             * Refill buffer.
             */
            if (cpssp->cmd_flag) {
                  /* Status Phase */
                  uint8_t status;
                  struct usbs {
                        uint32_t signature; /* 'USBS' */
                        uint32_t tag;
                        uint32_t residue;
                        uint8_t status;
                  } *usbs;

                  assert(cpssp->in_flag); /* FIXME */

                  if (cpssp->usbc_datalength) {
                        /*
                         * We're in Status Phase already. But
                         * Host wants to read even more data.
                         * Give it a short packet.
                         */
                        cpssp->in.head = 0;
                        cpssp->in.tail = 0;

                        cpssp->usbc_datalength = 0;

                  } else {
                        /*
                         * Get status info.
                         */
                        cpssp->req_count = disk_recv(cpssp,
                                    &status, sizeof(status));
                        assert(cpssp->req_count == 0);

                        usbs = (struct usbs *) cpssp->in.buffer;

                        usbs->signature = 0x53425355;
                        usbs->tag = cpssp->usbc_tag;
                        usbs->residue = cpssp->residue;
                        usbs->status = status ? 1 : 0;

                        cpssp->in.head = 13;
                        cpssp->in.tail = 0;
                  }

            } else if (cpssp->data_flag) {
                  /* Data In Phase */
                  assert(cpssp->in_flag); /* FIXME */

                  len = MIN(cpssp->req_count,
                              sizeof(cpssp->in.buffer));
                  cpssp->req_count = disk_recv(cpssp,
                              cpssp->in.buffer, len);
                  cpssp->in.head = len;
                  cpssp->in.tail = 0;

                  if (len < 64) {
                        /* Short packet => EOF. */
                        cpssp->usbc_datalength = 0;
                  } else {
                        cpssp->usbc_datalength -= len;
                  }
                  cpssp->residue -= len;

            } else if (cpssp->msg_flag) {
                  /* Message In Phase */
                  assert(0); /* FIXME */

            } else {
                  /* Free Phase */
                  assert(0); /* FIXME */
            }
      }

      /*
       * Send bytes to USB.
       */
      len = MIN(cpssp->in.head - cpssp->in.tail, 64);
      if (DEBUG_CONTROL_FLOW) {
            unsigned int i;

            fprintf(stderr, "%s sending %d bytes\n", __FUNCTION__, len);
            for (i = 0; i < len; i++) {
                  fprintf(stderr, " %02x",
                              cpssp->in.buffer[cpssp->in.tail + i]);
            }
            fprintf(stderr, "\n");
      }
      sig_usb_bus_send_data(cpssp->port_usb_main, cpssp,
                  cpssp->in.toggle, len,
                  cpssp->in.buffer + cpssp->in.tail, 0);
};

static void
usbstorage_recv_handshake_in(struct cpssp *cpssp, int pid)
{
      if (DEBUG_CONTROL_FLOW) {
            fprintf(stderr, "%s %d\n", __FUNCTION__, pid);
      }

      if (pid == USB_PID_ACK) {
            unsigned int len;

            /* Try to ack bytes from IN buffer. */
            len = cpssp->in.head - cpssp->in.tail;
            if (64 < len) {
                  len = 64;
            }
            if (len != 0) {
                  cpssp->in.toggle = usbstorage_toggle(cpssp->in.toggle);
                  cpssp->in.tail += len;
                  return;
            }

            /* Try to ack bytes from STATUS buffer. */
            len = cpssp->status.head - cpssp->status.tail;
            if (64 < len) {
                  len = 64;
            }
            if (len != 0) {
                  /* Use IN toggle! */
                  cpssp->in.toggle = usbstorage_toggle(cpssp->in.toggle);

                  cpssp->status.tail += len;
                  return;
            }
      }
}

static void
usbstorage_recv_data_out(
      struct cpssp *cpssp,
      unsigned int length,
      uint8_t *data
)
{
      struct usbc {
            uint32_t signature; /* 'USBC' */
            uint32_t tag;
            uint32_t datalength;
            uint8_t flags;
            uint8_t lun;
            uint8_t cmdlength;
            uint8_t cmd[16];
      } *usbc;
      unsigned int i;

      if (DEBUG_CONTROL_FLOW) {
            fprintf(stderr, "%s: %d bytes\n", __FUNCTION__, length);
      }

      if (cpssp->cmd_flag) {
            /* Command Phase */
            /* Handled in disk_want_recv. */
            assert(0);

      } else if (cpssp->data_flag) {
            /* Data Out Phase */
            assert(! cpssp->in_flag); /* FIXME */

            (void) disk_send(cpssp, data, length);

            cpssp->usbc_datalength -= length;
            cpssp->residue -= length;

      } else if (cpssp->msg_flag) {
            /* Message Out Phase */
            /* Handled in disk_want_recv. */
            assert(0);

      } else {
            /* Free Phase */
            usbc = (struct usbc *) data;

            if (DEBUG_CONTROL_FLOW) {
                  asm volatile("" : : : "memory");

                  fprintf(stderr, "%s:\n", __FUNCTION__);
                  fprintf(stderr, "signature %08x\n", usbc->signature);
                  fprintf(stderr, "tag %08x\n", usbc->tag);
                  fprintf(stderr, "datalength %08x\n", usbc->datalength);
                  fprintf(stderr, "flags %02x\n", usbc->flags);
                  fprintf(stderr, "lun %02x\n", usbc->lun);
                  fprintf(stderr, "cmdlength %02x\n", usbc->cmdlength);
                  fprintf(stderr, "cmd", usbc->cmdlength);
                  for (i = 0; i < usbc->cmdlength; i++) {
                        fprintf(stderr, " %02x", usbc->cmd[i]);
                  }
                  fprintf(stderr, "\n");

                  asm volatile("" : : : "memory");
            }

            cpssp->usbc_tag = usbc->tag;
            cpssp->usbc_datalength = usbc->datalength;
            cpssp->usbc_flags = usbc->flags;
            cpssp->usbc_lun = usbc->lun;
            cpssp->usbc_cmd_head = usbc->cmdlength;
            cpssp->usbc_cmd_tail = 0;
            memcpy(cpssp->usbc_cmd, usbc->cmd, usbc->cmdlength);

            cpssp->residue = usbc->datalength;

            disk_atn_set(cpssp, 0);
            (void) disk_phase_select(cpssp, 0);
      }

      sig_usb_bus_send_handshake(cpssp->port_usb_main, cpssp, USB_PID_ACK);
}

static void
usbstorage_recv_token(void *_cpssp, int pid, int addr, int endp)
{
      struct cpssp *cpssp = _cpssp;

      if (DEBUG_CONTROL_FLOW) {
            fprintf(stderr, "%s: %d %d %d\n", __FUNCTION__, pid, addr, endp);
      }

      if (cpssp->state < STATE_DEFAULT) {
            /* Device must not respond to any bus activity. */
            return;
      }

      if (pid != USB_PID_SETUP
       && pid != USB_PID_IN
       && pid != USB_PID_OUT) {
            fprintf(stderr, "%s: Unknown PID 0x%x.\n", COMP, pid);
            return;
      }

      cpssp->token_pid = pid;
      cpssp->token_addr = addr;
      cpssp->token_endp = endp;

      if (cpssp->addr != addr) {
            /* Ignore tokens to other addresses. */
            return;
      }

      switch (pid) {
      case USB_PID_SETUP:
            if (endp != 0) {
                  assert(0); /* FIXME */
            }
            cpssp->control.stage = STAGE_SETUP;
            cpssp->control.toggle = USB_PID_DATA0;
            break;

      case USB_PID_IN:
            switch (endp) {
            case 0:
                  usbstorage_recv_token_control(cpssp);
                  break;
            case 1:
                  usbstorage_recv_token_in(cpssp);
                  break;
            case 2:
                  fprintf(stderr, "WARNING: %s: Trying to receive from OUT entpoint.\n",
                              COMP);
                  break;
            default:
                  assert(0); /* FIXME */
            }
            break;

      case USB_PID_OUT:
            cpssp->control.stage = STAGE_STATUS;
            cpssp->control.toggle = USB_PID_DATA1;
            break;

      default:
            assert(0); /* Cannot happen. */
      }
}

static void
usbstorage_recv_data(
      void *_cpssp,
      int pid,
      unsigned int length,
      uint8_t *data,
      uint16_t crc16
)
{
      struct cpssp *cpssp = _cpssp;

      if (DEBUG_CONTROL_FLOW) {
            fprintf(stderr, "%s: %d %u\n", __FUNCTION__, pid, length);
      }

      if (cpssp->state < STATE_DEFAULT) {
            /* Device must not respond to any bus activity. */
            return;
      }
      if (cpssp->addr != cpssp->token_addr) {
            /* Ignore data for other addresses. */
            return;
      }
      if (cpssp->state < STATE_CONFIGURED
       && cpssp->token_endp != 0) {
            /* We must not accept packets to other entpoints but 0. */
            return;
      }

      switch (cpssp->token_endp) {
      case 0:
            usbstorage_recv_data_control(cpssp, length, data);
            break;
      case 1:
            fprintf(stderr, "WARNING: %s: Got data for IN endpoint.\n",
                        COMP);
            break;
      case 2:
            usbstorage_recv_data_out(cpssp, length, data);
            break;
      default:
            fprintf(stderr, "WARNING: %s: Got data for endpoint %d.\n",
                        COMP, cpssp->token_endp);
            break;
      }
}

static void
usbstorage_recv_handshake(void *_cpssp, int pid)
{
      struct cpssp *cpssp = _cpssp;

      if (DEBUG_CONTROL_FLOW) {
            fprintf(stderr, "%s: %d\n", __FUNCTION__, pid);
      }

      if (cpssp->state < STATE_DEFAULT) {
            /* Device must not respond to any bus activity. */
            return;
      }
      if (cpssp->addr != cpssp->token_addr) {
            /* Ignore data for other addresses. */
            return;
      }
      if (cpssp->state < STATE_CONFIGURED
       && cpssp->token_endp != 0) {
            /* We must not accept packets to other entpoints but 0. */
            return;
      }

      switch (cpssp->token_endp) {
      case 0:
            usbstorage_recv_handshake_control(cpssp, pid);
            break;
      case 1:
            usbstorage_recv_handshake_in(cpssp, pid);
            break;
      case 2:
            fprintf(stderr, "WARNING: %s: Got handshake for OUT endpoint\n",
                        COMP, cpssp->token_endp);
            break;
      default:
            fprintf(stderr, "WARNING: %s: Got handshake for endpoint %d.\n",
                        COMP, cpssp->token_endp);
            break;
      }
}

void
usbstorage_init(unsigned int nr, struct sig_usb_bus *port_usb)
{
      static const struct sig_boolean_funcs usb_power_funcs = {
            .set = usbstorage_power_set,
      };
      static const struct sig_usb_bus_main_funcs usb_main_funcs = {
            .reset_set = usbstorage_reset_set,
            .speed_set = NULL,
            .recv_token = usbstorage_recv_token,
            .recv_sof = NULL,
            .recv_data = usbstorage_recv_data,
            .recv_handshake = usbstorage_recv_handshake,
      };
      struct cpssp *cpssp;

      cpssp = shm_map(COMP, nr, sizeof(*cpssp), 0);

      cpssp->port_usb_main = port_usb->bus;

      disk_init(cpssp);

      /* Call */
      sig_usb_bus_main_connect(port_usb->bus, cpssp, &usb_main_funcs);

      /* In */
      cpssp->state_power = 0;
      sig_boolean_connect_in(port_usb->power, cpssp, &usb_power_funcs);
}

void
usbstorage_create(unsigned int nr, const char *name)
{
      struct cpssp *cpssp;
      char path[1024];

      shm_create(COMP, nr, sizeof(*cpssp));
      cpssp = shm_map(COMP, nr, sizeof(*cpssp), 0);

        /* Shoudn't be necessary! FIXME */
        assert(strlen(COMP "-XX.media") + 1 <= sizeof(path));
        sprintf(path, COMP "-%d.media", nr);

      disk_create(cpssp, path, 0, 128); /* FIXME */

      shm_unmap(cpssp, sizeof(*cpssp));
}

void
usbstorage_destroy(unsigned int nr)
{
      struct cpssp *cpssp;

      cpssp = shm_map(COMP, nr, sizeof(*cpssp), 0);

      disk_destroy(cpssp);

      shm_unmap(cpssp, sizeof(*cpssp));
      shm_destroy(COMP, nr);
}

Generated by  Doxygen 1.6.0   Back to index