www.pudn.com > efs.rar > fs_hotplug_usb.c


/***********************************************************************
 * fs_hotplug_usb.c
 *
 * USB Mass Storage support for Hotplug.
 * Copyright (C) 2006 QUALCOMM, Inc.
 *
 * Hotplug device I/O and hotplug support functions for USB Mass Storage
 * devices.
 *
 ***********************************************************************/

/*===========================================================================

                        EDIT HISTORY FOR MODULE

  This section contains comments describing changes made to the module.
  Notice that changes are listed in reverse chronological order.

  $Header: //depot/asic/MSMSHARED/services/efs/MSM_EFS.01.02/fs_hotplug_usb.c#15 $ $DateTime: 2006/11/13 14:44:34 $ $Author: davidb $

when         who   what, where, why
----------   ---   ---------------------------------------------------------
2006-11-09   sch   Added extra write handler to the device table
2006-11-06   s h   Ignore devices that have a block size other tha 512 bytes.
2006-10-05   s h   Quiet some excessive messages.
2006-09-20   s h   Renamed TYPE_ values to have HOTPLUG prefix.
2006-09-12   s h   Fix usbhost_open_ret_type default value (was warning).
2006-04-24   s h   Renamed control flag to FS_HOTPLUG_USB
2006-06-02   s h   Added reset handler
2006-05-15   s h   Renamed hotplug_null to hotplug_success_stub
2006-05-07   s h   Replaced DRV_GEOMETRY_DESC with block size & count
2006-05-08   s h   Removed umount from device-specific calls.
2006-04-24   s h   Renamed control flag to FS_HOTPLUG_USB_MS
2006-04-22   s h   Moved all private definitions into fs_hotplug_i.h
2006-04-21   s h   Code commenting and cleanup
2006-04-20   sch   Moved the updation of USB state variables to usbhost_close
2006-04-20   sch   Removed ASSERTs to allow operations already in progress
2006-04-20   sch   USB API change: usbhost_ioctl() instance is now handle
2006-04-20   sch   Pass in handle instead of instance in usbhost_ioctl()
2006-04-14   s h   More debugging prints.
2006-04-13   s h   Code review feedback implemented.
2006-04-13   s h   Allow compilation without USB
2006-03-31   sch   Fixed syntax error in hotplug_usbhost_open_notification
2006-03-31   s h   Added USB-specific unmount
2006-03-24   s h   Brought in all USB-specific functions from hotplug
2006-03-15   sch   Create

===========================================================================*/

#include "customer.h"
#include "fs_hotplug.h"
#include "fs_hotplug_i.h"
#include "fs_hotplug_usb.h"

/* Hotplug supports USB Host Mass Storage.. ie, when the MSM controls
 * an external USB device like a thumbdrive, hard drive, or card
 * reader.  */
#ifdef FS_HOTPLUG_USB

#include "jzap.h"
#include "msg.h"
#include "usbhost_api.h"

/* Unique ID that USBHOST uses to recognize clients (like Hotplug) */
static usbhost_client_id_type client_id;

/* These USB-specific handlers are used in the Hotplug device table
 * for USB devices. */
static int hotplug_usb_read (struct hotplug_device *, uint32 lba,
                             unsigned char *buf, uint16 n_to_read);
static int hotplug_usb_write (struct hotplug_device *,
                              uint32 lba, unsigned char *buf,
                              uint16 n_to_write);
static int hotplug_usb_get_size (struct hotplug_device *,
                                 uint32 *blocks, uint16 *bytes_per_block);
static int hotplug_usb_is_present (struct hotplug_device *);

/* Returns the first USB device in the hotplug_device_list which has
 * specified 'state' value.  */
static struct hotplug_device *
find_by_state (hotplug_usbhost_state state,
               struct hotplug_device *hdev)
{
  do {
    hdev = hotplug_find_dev_by_type (HOTPLUG_TYPE_USB_MS, hdev);
  } while (hdev && (hdev->usb_state != state));
  return hdev;
}

/* Returns the first USB device in the hotplug_device_list which has
 * the specific instance. */
static struct hotplug_device *
find_by_instance (usbhost_dev_instance_type inst,
                  struct hotplug_device *hdev)
{
  do {
    hdev = hotplug_find_dev_by_type (HOTPLUG_TYPE_USB_MS, hdev);
  } while (hdev && (hdev->usb_instance != inst));
  return hdev;
}

/* This is the callback function hotplug passes to usbhost_register.
 * THIS EXECUTES IN USB CONTEXT!  Don't spend too long here.
 * This can be called multiple times in a row (for multiple devices)
 * When it is called, we are given a valid dev_instance, but not "handle".
 */
void
hotplug_usbhost_callback (usbhost_pdev_type pdev_typ,
                          usbhost_dev_status_type status,
                          usbhost_dev_handle_type dev_instance, void *param)
{
  (void) pdev_typ;
  (void) param;

  if (status == USBHOST_DEV_CONNECTED) {
    struct hotplug_device *hdev;
    /*
     * USB has detected the presence of a mass storage device.  Hurah!
     * Try to find the next available structure in the hotplug table
     * (the first USB device that is in UNCONNECTED state).
     */
    ZAP ("USBHOST_DEV_CONNECTED");
    hdev = find_by_state (USBSTATUS_UNCONNECTED, NULL);
    if (hdev) {
      if (find_by_instance (dev_instance, NULL) != NULL)
        ZAP ("Adding a SECOND duplicate instance!"); /* Bad */

      hdev->usb_state = USBSTATUS_CONNECTED;
      hdev->usb_instance = dev_instance;
      rex_set_sigs (&fs_hotplug_tcb, HOTPLUG_USBHOST_OPEN_SIG);
    } else {
      ZAP ("Could not find an unconnected device slot!");
      MSG_ERROR ("HOTPLUG: New device, with no available slot!" ,0,0,0);
      /* Drop this one */
    }
  } else if (status == USBHOST_DEV_DISCONNECTED) {
    struct hotplug_device *hdev;
    /*
     * USB has detected a disconnect on a device.
     * Find this device instance, mark it closed, and wake up Hotplug task.
     */
    ZAP ("USBHOST_DEV_DISCONNECTED");
    hdev = find_by_instance (dev_instance, NULL);
    if (!hdev)
    {
      ZAP ("Got a DISCONNECT Notify with no matching instance?");
    } else {
      hdev->usb_state = USBSTATUS_CLOSED;
      rex_set_sigs (&fs_hotplug_tcb, HOTPLUG_USBHOST_CLOSE_SIG);
    }
  } else {
    ZAP ("Unknown device status from USB host stack");
    MSG_ERROR ("HOTPLUG: Unexpected device status from USB Host MS!",0,0,0);
  }
}

/*
 * Register with USB host stack.  Tell it what signal to use for its own
 * semaphore, which will execute in our task context.
 * Our Hotplug task reserves a signal for this.
 * We get our client_id from this call.
 */
void
hotplug_usbhost_register (void)
{
  usbhost_task_table_type usb_sigs;

  usb_sigs.sem_wait_sig = HOTPLUG_USBHOST_SEM_WAIT_SIG;
  usb_sigs.wait_sig = 0;        /* Deprecated */
  usb_sigs.dog_rpt_sig = 0;     /* No need for dog reporting */
  usb_sigs.dog_rpt_fn_ptr = NULL;
  client_id =
    usbhost_register (USBHOST_PDEV_TYPE_MASS_STORAGE, "Hotplug", usb_sigs,
                      hotplug_usbhost_callback, NULL);
}


/*
 * Called from USB's context when a device is requested for closure.
 * This means some other task would like to use a device that we have
 * open, and we are being asked to release it.
 *
 * Since Hotplug "owns" any device listed in the table, we ignore
 * this request.  It would indicate a real problem.
 */
static void
hotplug_usbhost_close_notification (usbhost_dev_instance_type handle,
                                    char *client_desc,
                                    usbhost_client_id_type from_client_id,
                                    void *param)
{
    (void) handle;
    (void) client_desc;
    (void) from_client_id;
    (void) param;

    ZAP ("Got a usbhost_close_notification, and ignored it.");
}

/*
 * Called from USB's context when a device has been opened (by our request)
 * We had an instance from the connection, and
 * we now receive a valid handle to use in future calls.
 */
static usbhost_open_ret_type
hotplug_usbhost_open_notification (usbhost_dev_instance_type inst,
                                   usbhost_dev_handle_type handle,
                                   usbhost_open_status stat,
                                   char *client_desc, void *param)
{
  usbhost_open_ret_type result;
  (void) client_desc;
  (void) param;

  /* Assume failure.  Request a retry?  There is no "ERROR" code possible. */
  result = USBHOST_OPEN_RET_RESCHEDULE;

  if (stat == USBHOST_OPEN_AVAILABLE)
  {
    /*
     * Find the first entry in our hotplug table that is of this USB
     * instance and associate that dev_instance to this handle.
     */
    struct hotplug_device *hdev;
    ZAP ("USBHOST_OPEN_AVAILABLE");
    hdev = find_by_instance (inst, NULL);
    if (!hdev) {
      ZAP ("Mysterious USB device opened.  No matching instance");
    } else if (hdev->usb_state != USBSTATUS_OPEN_REQUESTED) {
      ZAP ("We did not request open on this instance!");
    } else {
      hdev->usb_handle = handle; /* Save the handle! */
      hdev->usb_state = USBSTATUS_OPEN;
      result = USBHOST_OPEN_RET_UNSCHEDULE; /* Accept this connection */
    }
  }
  else if (stat == USBHOST_OPEN_NOT_AVAILABLE)
  {
    /* Our open request was denied.  Request a retry. */
    ZAP ("USBHOST_OPEN_NOT_AVAILABLE");
  }
  else
  {
    ZMSG ("Got an open_notify with a bizzare status: %d", stat, 0,0);
  }
  return result;
}

/* -------------- Hotplug Context -------------- */

/* This is called by fs_hotplug_task() after a USB device is connected
 * and the callback gives us the signal to open any new devices */
void
hotplug_usbhost_open (void)
{
  struct hotplug_device *hdev = NULL;
  int serviced = 0;

  /* Some of the USB devices are available for communication.
     (state = USBSTATUS_CONNECTED)
     We should find ALL such devices and open them. */
  ZAP ("hotplug_usbhost_open");
  while (1)
  {
    hdev = find_by_state (USBSTATUS_CONNECTED, hdev);
    if (!hdev)
      break;
    ZPRINTF ("hotplug_usbhost_open(%s)", hdev->name);
    /* Ask that the device be opened now. */
    hdev->usb_state = USBSTATUS_OPEN_REQUESTED;
    usbhost_req_open (client_id,
                      hdev->usb_instance,
                      hotplug_usbhost_open_notification,
                      hotplug_usbhost_close_notification);
    serviced++;
  }

  if (serviced == 0)
    ZAP ("hotplug_usbhost_open without any CONNECTED devices!");
}

/* This is called by fs_hotplug_task() after a USB device is disconnected.
 * One or more devices is now in USBSTATUS_CLOSED. */
void
hotplug_usbhost_close (void)
{
  struct hotplug_device *hdev = NULL;
  int serviced = 0;

  /* Some of the devices are no longer available */
  while (1)
  {
    hdev = find_by_state (USBSTATUS_CLOSED, hdev);
    if (!hdev)
      break;

    /* This device will be unmounted the next time hotplug task
     * polls and finds it unuseable without a handle. */
    hdev->usb_instance = HOTPLUG_NO_INSTANCE;
    hdev->usb_handle = HOTPLUG_NO_HANDLE;
    hdev->usb_state = USBSTATUS_UNCONNECTED;

    ZPRINTF ("hotplug_usbhost_close(%s)", hdev->name);
    serviced++;
  }

  if (serviced == 0)
    ZAP ("hotplug_usbhost_close without any CLOSED devices!");
}

/* --------- Device IO Handlers ----------- */

static int
hotplug_usb_read (struct hotplug_device *hdev, uint32 lba,
                  unsigned char *buf, uint16 n_to_read)
{
  usbhost_IoctlTransfer ms_data;

  ms_data.numberBlocks = n_to_read;
  ms_data.startBlock = lba;
  ms_data.buffer = buf;
  ms_data.length = n_to_read * 512;

  if ((hdev->usb_handle != HOTPLUG_NO_HANDLE)
      && (USBHOST_SUCCESS == usbhost_ioctl (client_id, hdev->usb_handle,
                                            USBHOST_IOCTL_STOR_BLOCK_READ,
                                            (void *) &ms_data)))
    return 0;                   /* Good! */
  else
    return -1;                  /* Read failed */
}

static int
hotplug_usb_write (struct hotplug_device *hdev, uint32 lba,
                   unsigned char *buf, uint16 n_to_write)
{
  usbhost_IoctlTransfer ms_data;

  ms_data.numberBlocks = n_to_write;
  ms_data.startBlock = lba;
  ms_data.buffer = buf;
  ms_data.length = n_to_write * 512;

  if ((hdev->usb_handle != HOTPLUG_NO_HANDLE)
      && (USBHOST_SUCCESS == usbhost_ioctl (client_id, hdev->usb_handle,
                                            USBHOST_IOCTL_STOR_BLOCK_WRITE,
                                            (void *) &ms_data)))
    return 0;                   /* Good! */
  else
    return -1;                  /* Write failed */
}

static int
hotplug_usb_get_size (struct hotplug_device *hdev,
                      uint32 *blocks, uint16 *bytes_per_block)
{
  uint32 count;
  usbhost_status_type status;

  if (hdev->usb_handle == HOTPLUG_NO_HANDLE)
    return -1;

  status = usbhost_ioctl (client_id, hdev->usb_handle,
                          USBHOST_IOCTL_STOR_BLOCK_SIZE, (void *) &count);
  if (status == USBHOST_ERROR)
    return -1;
  *bytes_per_block = count;

  status = usbhost_ioctl (client_id, hdev->usb_handle,
                          USBHOST_IOCTL_STOR_NUMBER_OF_BLOCKS,
                          (void *) &count);
  if (status == USBHOST_ERROR)
    return -1;
  *blocks = count;

  return 0;
}

/* This checks if a particular USB device is ready for use.
 * In the case of card readers, this includes having useable media inserted.
 * (A connected device without media is considered not-present.)
 *
 * NOTE that this must be callable even when the device is mounted and active!
 * This means re-entrancy is required in the USB stack.
 */
static int
hotplug_usb_is_present (struct hotplug_device *hdev)
{
  usbhost_status_type status;
  int ready;

  if (hdev->usb_state != USBSTATUS_OPEN)
    return 0;

  /* This will check if the media has changed or become unavailable */
  status = usbhost_ioctl (client_id, hdev->usb_handle,
                          USBHOST_IOCTL_STOR_CHECK_MEDIA, NULL);

  ready = 0;                    /* Assume the worst */
  switch (status)
  {
    case USBHOST_SUCCESS:
      ready = 1;                /* Success! */
      break;
    case USBHOST_ERROR:
      ZAPN (" USBHOST_ERROR ");
      break;
    case USBHOST_ERROR_MEDIA_CHANGED:
      ZAPN (" USBHOST_ERROR_MEDIA_CHANGED ");
      break;
    case USBHOST_ERROR_MEDIA_NOT_PRESENT:
      ZAPN (" USBHOST_ERROR_MEDIA_NOT_PRESENT ");
      break;
    default:
      ZPRINTF ("Unexpected CHECK_MEDIA return status: %d!", status);
      break;
  }

  /* Also make sure this is a 512-byte block device, so we don't
   * accidentally talk to a CD-ROM device (like U3 thumbdrives) and
   * crash the USB stack. */
  if (ready)
  {
    uint32 count;
    status = usbhost_ioctl (client_id, hdev->usb_handle,
                            USBHOST_IOCTL_STOR_BLOCK_SIZE, (void *) &count);
    if (status != USBHOST_SUCCESS) {
      ZAPN (" STOR_BLOCK_SIZE failed ");
      ready = 0;
    } else if (count != 512) {
      ZPRINTF (" Media has %d block size, != 512.  Ignored!", count);
      ready = 0;
    }
  }

  return ready;
}

/* These are the entry point functions shared with Hotplug for use by any
 * USB device */
struct hotplug_dev_funcs hotplug_usb_dev = {
  hotplug_fat_mount,            /* Mount */
  hotplug_success_stub,         /* Open */
  hotplug_success_stub,         /* Close */
  hotplug_usb_read,             /* Read */
  hotplug_usb_write,            /* Write */
  hotplug_usb_write,            /* Write */
  hotplug_no_erase,             /* Erase */
  hotplug_usb_get_size,         /* Get Size */
  hotplug_usb_is_present,       /* Is Present */
  hotplug_success_stub,         /* Format prep */
  hotplug_success_stub,         /* Reset */
};

#else  /* !FS_HOTPLUG_USB */
/* Stub functions for builds where there is no USB MS support. */
void hotplug_usbhost_register(void) {}
void hotplug_usbhost_open (void) {}
void hotplug_usbhost_close (void) {}
#endif