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

megaraid_mm.c

/*
 *
 *                Linux MegaRAID device driver
 *
 * Copyright (c) 2003-2004  LSI Logic Corporation.
 *
 *       This program is free software; you can redistribute it and/or
 *       modify it under the terms of the GNU General Public License
 *       as published by the Free Software Foundation; either version
 *       2 of the License, or (at your option) any later version.
 *
 * FILE           : megaraid_mm.c
 * Version  : v2.20.2.7 (Jul 16 2006)
 *
 * Common management module
 */
#include <linux/sched.h>
#include "megaraid_mm.h"


// Entry points for char node driver
static int mraid_mm_open(struct inode *, struct file *);
static int mraid_mm_ioctl(struct inode *, struct file *, uint, unsigned long);


// routines to convert to and from the old the format
static int mimd_to_kioc(mimd_t __user *, mraid_mmadp_t *, uioc_t *);
static int kioc_to_mimd(uioc_t *, mimd_t __user *);


// Helper functions
static int handle_drvrcmd(void __user *, uint8_t, int *);
static int lld_ioctl(mraid_mmadp_t *, uioc_t *);
static void ioctl_done(uioc_t *);
static void lld_timedout(unsigned long);
static void hinfo_to_cinfo(mraid_hba_info_t *, mcontroller_t *);
static mraid_mmadp_t *mraid_mm_get_adapter(mimd_t __user *, int *);
static uioc_t *mraid_mm_alloc_kioc(mraid_mmadp_t *);
static void mraid_mm_dealloc_kioc(mraid_mmadp_t *, uioc_t *);
static int mraid_mm_attach_buf(mraid_mmadp_t *, uioc_t *, int);
static int mraid_mm_setup_dma_pools(mraid_mmadp_t *);
static void mraid_mm_free_adp_resources(mraid_mmadp_t *);
static void mraid_mm_teardown_dma_pools(mraid_mmadp_t *);

#ifdef CONFIG_COMPAT
static long mraid_mm_compat_ioctl(struct file *, unsigned int, unsigned long);
#endif

MODULE_AUTHOR("LSI Logic Corporation");
MODULE_DESCRIPTION("LSI Logic Management Module");
MODULE_LICENSE("GPL");
MODULE_VERSION(LSI_COMMON_MOD_VERSION);

static int dbglevel = CL_ANN;
module_param_named(dlevel, dbglevel, int, 0);
MODULE_PARM_DESC(dlevel, "Debug level (default=0)");

EXPORT_SYMBOL(mraid_mm_register_adp);
EXPORT_SYMBOL(mraid_mm_unregister_adp);
EXPORT_SYMBOL(mraid_mm_adapter_app_handle);

static int majorno;
static uint32_t drvr_ver      = 0x02200207;

static int adapters_count_g;
static struct list_head adapters_list_g;

static wait_queue_head_t wait_q;

static const struct file_operations lsi_fops = {
      .open = mraid_mm_open,
      .ioctl      = mraid_mm_ioctl,
#ifdef CONFIG_COMPAT
      .compat_ioctl = mraid_mm_compat_ioctl,
#endif
      .owner      = THIS_MODULE,
};

/**
 * mraid_mm_open - open routine for char node interface
 * @inode   : unused
 * @filep   : unused
 *
 * Allow ioctl operations by apps only if they have superuser privilege.
 */
static int
mraid_mm_open(struct inode *inode, struct file *filep)
{
      /*
       * Only allow superuser to access private ioctl interface
       */
      if (!capable(CAP_SYS_ADMIN)) return (-EACCES);

      return 0;
}

/**
 * mraid_mm_ioctl - module entry-point for ioctls
 * @inode   : inode (ignored)
 * @filep   : file operations pointer (ignored)
 * @cmd           : ioctl command
 * @arg           : user ioctl packet
 */
static int
mraid_mm_ioctl(struct inode *inode, struct file *filep, unsigned int cmd,
                                          unsigned long arg)
{
      uioc_t            *kioc;
      char        signature[EXT_IOCTL_SIGN_SZ]  = {0};
      int         rval;
      mraid_mmadp_t     *adp;
      uint8_t           old_ioctl;
      int         drvrcmd_rval;
      void __user *argp = (void __user *)arg;

      /*
       * Make sure only USCSICMD are issued through this interface.
       * MIMD application would still fire different command.
       */

      if ((_IOC_TYPE(cmd) != MEGAIOC_MAGIC) && (cmd != USCSICMD)) {
            return (-EINVAL);
      }

      /*
       * Look for signature to see if this is the new or old ioctl format.
       */
      if (copy_from_user(signature, argp, EXT_IOCTL_SIGN_SZ)) {
            con_log(CL_ANN, (KERN_WARNING
                  "megaraid cmm: copy from usr addr failed\n"));
            return (-EFAULT);
      }

      if (memcmp(signature, EXT_IOCTL_SIGN, EXT_IOCTL_SIGN_SZ) == 0)
            old_ioctl = 0;
      else
            old_ioctl = 1;

      /*
       * At present, we don't support the new ioctl packet
       */
      if (!old_ioctl )
            return (-EINVAL);

      /*
       * If it is a driver ioctl (as opposed to fw ioctls), then we can
       * handle the command locally. rval > 0 means it is not a drvr cmd
       */
      rval = handle_drvrcmd(argp, old_ioctl, &drvrcmd_rval);

      if (rval < 0)
            return rval;
      else if (rval == 0)
            return drvrcmd_rval;

      rval = 0;
      if ((adp = mraid_mm_get_adapter(argp, &rval)) == NULL) {
            return rval;
      }

      /*
       * Check if adapter can accept ioctl. We may have marked it offline
       * if any previous kioc had timedout on this controller.
       */
      if (!adp->quiescent) {
            con_log(CL_ANN, (KERN_WARNING
                  "megaraid cmm: controller cannot accept cmds due to "
                  "earlier errors\n" ));
            return -EFAULT;
      }

      /*
       * The following call will block till a kioc is available
       */
      kioc = mraid_mm_alloc_kioc(adp);

      /*
       * User sent the old mimd_t ioctl packet. Convert it to uioc_t.
       */
      if ((rval = mimd_to_kioc(argp, adp, kioc))) {
            mraid_mm_dealloc_kioc(adp, kioc);
            return rval;
      }

      kioc->done = ioctl_done;

      /*
       * Issue the IOCTL to the low level driver. After the IOCTL completes
       * release the kioc if and only if it was _not_ timedout. If it was
       * timedout, that means that resources are still with low level driver.
       */
      if ((rval = lld_ioctl(adp, kioc))) {

            if (!kioc->timedout)
                  mraid_mm_dealloc_kioc(adp, kioc);

            return rval;
      }

      /*
       * Convert the kioc back to user space
       */
      rval = kioc_to_mimd(kioc, argp);

      /*
       * Return the kioc to free pool
       */
      mraid_mm_dealloc_kioc(adp, kioc);

      return rval;
}


/**
 * mraid_mm_get_adapter - Returns corresponding adapters for the mimd packet
 * @umimd   : User space mimd_t ioctl packet
 * @rval    : returned success/error status
 *
 * The function return value is a pointer to the located @adapter.
 */
static mraid_mmadp_t *
mraid_mm_get_adapter(mimd_t __user *umimd, int *rval)
{
      mraid_mmadp_t     *adapter;
      mimd_t            mimd;
      uint32_t    adapno;
      int         iterator;


      if (copy_from_user(&mimd, umimd, sizeof(mimd_t))) {
            *rval = -EFAULT;
            return NULL;
      }

      adapno = GETADAP(mimd.ui.fcs.adapno);

      if (adapno >= adapters_count_g) {
            *rval = -ENODEV;
            return NULL;
      }

      adapter = NULL;
      iterator = 0;

      list_for_each_entry(adapter, &adapters_list_g, list) {
            if (iterator++ == adapno) break;
      }

      if (!adapter) {
            *rval = -ENODEV;
            return NULL;
      }

      return adapter;
}

/**
 * handle_drvrcmd - Checks if the opcode is a driver cmd and if it is, handles it.
 * @arg           : packet sent by the user app
 * @old_ioctl     : mimd if 1; uioc otherwise
 * @rval    : pointer for command's returned value (not function status)
 */
static int
handle_drvrcmd(void __user *arg, uint8_t old_ioctl, int *rval)
{
      mimd_t            __user *umimd;
      mimd_t            kmimd;
      uint8_t           opcode;
      uint8_t           subopcode;

      if (old_ioctl)
            goto old_packet;
      else
            goto new_packet;

new_packet:
      return (-ENOTSUPP);

old_packet:
      *rval = 0;
      umimd = arg;

      if (copy_from_user(&kmimd, umimd, sizeof(mimd_t)))
            return (-EFAULT);

      opcode            = kmimd.ui.fcs.opcode;
      subopcode   = kmimd.ui.fcs.subopcode;

      /*
       * If the opcode is 0x82 and the subopcode is either GET_DRVRVER or
       * GET_NUMADP, then we can handle. Otherwise we should return 1 to
       * indicate that we cannot handle this.
       */
      if (opcode != 0x82)
            return 1;

      switch (subopcode) {

      case MEGAIOC_QDRVRVER:

            if (copy_to_user(kmimd.data, &drvr_ver, sizeof(uint32_t)))
                  return (-EFAULT);

            return 0;

      case MEGAIOC_QNADAP:

            *rval = adapters_count_g;

            if (copy_to_user(kmimd.data, &adapters_count_g,
                        sizeof(uint32_t)))
                  return (-EFAULT);

            return 0;

      default:
            /* cannot handle */
            return 1;
      }

      return 0;
}


/**
 * mimd_to_kioc   - Converter from old to new ioctl format
 * @umimd   : user space old MIMD IOCTL
 * @adp           : adapter softstate
 * @kioc    : kernel space new format IOCTL
 *
 * Routine to convert MIMD interface IOCTL to new interface IOCTL packet. The
 * new packet is in kernel space so that driver can perform operations on it
 * freely.
 */

static int
mimd_to_kioc(mimd_t __user *umimd, mraid_mmadp_t *adp, uioc_t *kioc)
{
      mbox64_t          *mbox64;
      mbox_t                  *mbox;
      mraid_passthru_t  *pthru32;
      uint32_t          adapno;
      uint8_t                 opcode;
      uint8_t                 subopcode;
      mimd_t                  mimd;

      if (copy_from_user(&mimd, umimd, sizeof(mimd_t)))
            return (-EFAULT);

      /*
       * Applications are not allowed to send extd pthru
       */
      if ((mimd.mbox[0] == MBOXCMD_PASSTHRU64) ||
                  (mimd.mbox[0] == MBOXCMD_EXTPTHRU))
            return (-EINVAL);

      opcode            = mimd.ui.fcs.opcode;
      subopcode   = mimd.ui.fcs.subopcode;
      adapno            = GETADAP(mimd.ui.fcs.adapno);

      if (adapno >= adapters_count_g)
            return (-ENODEV);

      kioc->adapno      = adapno;
      kioc->mb_type     = MBOX_LEGACY;
      kioc->app_type    = APPTYPE_MIMD;

      switch (opcode) {

      case 0x82:

            if (subopcode == MEGAIOC_QADAPINFO) {

                  kioc->opcode      = GET_ADAP_INFO;
                  kioc->data_dir    = UIOC_RD;
                  kioc->xferlen     = sizeof(mraid_hba_info_t);

                  if (mraid_mm_attach_buf(adp, kioc, kioc->xferlen))
                        return (-ENOMEM);
            }
            else {
                  con_log(CL_ANN, (KERN_WARNING
                              "megaraid cmm: Invalid subop\n"));
                  return (-EINVAL);
            }

            break;

      case 0x81:

            kioc->opcode            = MBOX_CMD;
            kioc->xferlen           = mimd.ui.fcs.length;
            kioc->user_data_len     = kioc->xferlen;
            kioc->user_data         = mimd.ui.fcs.buffer;

            if (mraid_mm_attach_buf(adp, kioc, kioc->xferlen))
                  return (-ENOMEM);

            if (mimd.outlen) kioc->data_dir  = UIOC_RD;
            if (mimd.inlen) kioc->data_dir |= UIOC_WR;

            break;

      case 0x80:

            kioc->opcode            = MBOX_CMD;
            kioc->xferlen           = (mimd.outlen > mimd.inlen) ?
                                    mimd.outlen : mimd.inlen;
            kioc->user_data_len     = kioc->xferlen;
            kioc->user_data         = mimd.data;

            if (mraid_mm_attach_buf(adp, kioc, kioc->xferlen))
                  return (-ENOMEM);

            if (mimd.outlen) kioc->data_dir  = UIOC_RD;
            if (mimd.inlen) kioc->data_dir |= UIOC_WR;

            break;

      default:
            return (-EINVAL);
      }

      /*
       * If driver command, nothing else to do
       */
      if (opcode == 0x82)
            return 0;

      /*
       * This is a mailbox cmd; copy the mailbox from mimd
       */
      mbox64      = (mbox64_t *)((unsigned long)kioc->cmdbuf);
      mbox  = &mbox64->mbox32;
      memcpy(mbox, mimd.mbox, 14);

      if (mbox->cmd != MBOXCMD_PASSTHRU) {      // regular DCMD

            mbox->xferaddr    = (uint32_t)kioc->buf_paddr;

            if (kioc->data_dir & UIOC_WR) {
                  if (copy_from_user(kioc->buf_vaddr, kioc->user_data,
                                          kioc->xferlen)) {
                        return (-EFAULT);
                  }
            }

            return 0;
      }

      /*
       * This is a regular 32-bit pthru cmd; mbox points to pthru struct.
       * Just like in above case, the beginning for memblk is treated as
       * a mailbox. The passthru will begin at next 1K boundary. And the
       * data will start 1K after that.
       */
      pthru32                 = kioc->pthru32;
      kioc->user_pthru  = &umimd->pthru;
      mbox->xferaddr          = (uint32_t)kioc->pthru32_h;

      if (copy_from_user(pthru32, kioc->user_pthru,
                  sizeof(mraid_passthru_t))) {
            return (-EFAULT);
      }

      pthru32->dataxferaddr   = kioc->buf_paddr;
      if (kioc->data_dir & UIOC_WR) {
            if (copy_from_user(kioc->buf_vaddr, kioc->user_data,
                                    pthru32->dataxferlen)) {
                  return (-EFAULT);
            }
      }

      return 0;
}

/**
 * mraid_mm_attch_buf - Attach a free dma buffer for required size
 * @adp           : Adapter softstate
 * @kioc    : kioc that the buffer needs to be attached to
 * @xferlen : required length for buffer
 *
 * First we search for a pool with smallest buffer that is >= @xferlen. If
 * that pool has no free buffer, we will try for the next bigger size. If none
 * is available, we will try to allocate the smallest buffer that is >=
 * @xferlen and attach it the pool.
 */
static int
mraid_mm_attach_buf(mraid_mmadp_t *adp, uioc_t *kioc, int xferlen)
{
      mm_dmapool_t      *pool;
      int         right_pool = -1;
      unsigned long     flags;
      int         i;

      kioc->pool_index  = -1;
      kioc->buf_vaddr         = NULL;
      kioc->buf_paddr         = 0;
      kioc->free_buf          = 0;

      /*
       * We need xferlen amount of memory. See if we can get it from our
       * dma pools. If we don't get exact size, we will try bigger buffer
       */

      for (i = 0; i < MAX_DMA_POOLS; i++) {

            pool = &adp->dma_pool_list[i];

            if (xferlen > pool->buf_size)
                  continue;

            if (right_pool == -1)
                  right_pool = i;

            spin_lock_irqsave(&pool->lock, flags);

            if (!pool->in_use) {

                  pool->in_use            = 1;
                  kioc->pool_index  = i;
                  kioc->buf_vaddr         = pool->vaddr;
                  kioc->buf_paddr         = pool->paddr;

                  spin_unlock_irqrestore(&pool->lock, flags);
                  return 0;
            }
            else {
                  spin_unlock_irqrestore(&pool->lock, flags);
                  continue;
            }
      }

      /*
       * If xferlen doesn't match any of our pools, return error
       */
      if (right_pool == -1)
            return -EINVAL;

      /*
       * We did not get any buffer from the preallocated pool. Let us try
       * to allocate one new buffer. NOTE: This is a blocking call.
       */
      pool = &adp->dma_pool_list[right_pool];

      spin_lock_irqsave(&pool->lock, flags);

      kioc->pool_index  = right_pool;
      kioc->free_buf          = 1;
      kioc->buf_vaddr   = pci_pool_alloc(pool->handle, GFP_KERNEL,
                                          &kioc->buf_paddr);
      spin_unlock_irqrestore(&pool->lock, flags);

      if (!kioc->buf_vaddr)
            return -ENOMEM;

      return 0;
}

/**
 * mraid_mm_alloc_kioc - Returns a uioc_t from free list
 * @adp     : Adapter softstate for this module
 *
 * The kioc_semaphore is initialized with number of kioc nodes in the
 * free kioc pool. If the kioc pool is empty, this function blocks till
 * a kioc becomes free.
 */
static uioc_t *
mraid_mm_alloc_kioc(mraid_mmadp_t *adp)
{
      uioc_t                  *kioc;
      struct list_head* head;
      unsigned long           flags;

      down(&adp->kioc_semaphore);

      spin_lock_irqsave(&adp->kioc_pool_lock, flags);

      head = &adp->kioc_pool;

      if (list_empty(head)) {
            up(&adp->kioc_semaphore);
            spin_unlock_irqrestore(&adp->kioc_pool_lock, flags);

            con_log(CL_ANN, ("megaraid cmm: kioc list empty!\n"));
            return NULL;
      }

      kioc = list_entry(head->next, uioc_t, list);
      list_del_init(&kioc->list);

      spin_unlock_irqrestore(&adp->kioc_pool_lock, flags);

      memset((caddr_t)(unsigned long)kioc->cmdbuf, 0, sizeof(mbox64_t));
      memset((caddr_t) kioc->pthru32, 0, sizeof(mraid_passthru_t));

      kioc->buf_vaddr         = NULL;
      kioc->buf_paddr         = 0;
      kioc->pool_index  =-1;
      kioc->free_buf          = 0;
      kioc->user_data         = NULL;
      kioc->user_data_len     = 0;
      kioc->user_pthru  = NULL;
      kioc->timedout          = 0;

      return kioc;
}

/**
 * mraid_mm_dealloc_kioc - Return kioc to free pool
 * @adp           : Adapter softstate
 * @kioc    : uioc_t node to be returned to free pool
 */
static void
mraid_mm_dealloc_kioc(mraid_mmadp_t *adp, uioc_t *kioc)
{
      mm_dmapool_t      *pool;
      unsigned long     flags;

      if (kioc->pool_index != -1) {
            pool = &adp->dma_pool_list[kioc->pool_index];

            /* This routine may be called in non-isr context also */
            spin_lock_irqsave(&pool->lock, flags);

            /*
             * While attaching the dma buffer, if we didn't get the 
             * required buffer from the pool, we would have allocated 
             * it at the run time and set the free_buf flag. We must 
             * free that buffer. Otherwise, just mark that the buffer is 
             * not in use
             */
            if (kioc->free_buf == 1)
                  pci_pool_free(pool->handle, kioc->buf_vaddr, 
                                          kioc->buf_paddr);
            else
                  pool->in_use = 0;

            spin_unlock_irqrestore(&pool->lock, flags);
      }

      /* Return the kioc to the free pool */
      spin_lock_irqsave(&adp->kioc_pool_lock, flags);
      list_add(&kioc->list, &adp->kioc_pool);
      spin_unlock_irqrestore(&adp->kioc_pool_lock, flags);

      /* increment the free kioc count */
      up(&adp->kioc_semaphore);

      return;
}

/**
 * lld_ioctl - Routine to issue ioctl to low level drvr
 * @adp           : The adapter handle
 * @kioc    : The ioctl packet with kernel addresses
 */
static int
lld_ioctl(mraid_mmadp_t *adp, uioc_t *kioc)
{
      int               rval;
      struct timer_list timer;
      struct timer_list *tp = NULL;

      kioc->status      = -ENODATA;
      rval        = adp->issue_uioc(adp->drvr_data, kioc, IOCTL_ISSUE);

      if (rval) return rval;

      /*
       * Start the timer
       */
      if (adp->timeout > 0) {
            tp          = &timer;
            init_timer(tp);

            tp->function      = lld_timedout;
            tp->data    = (unsigned long)kioc;
            tp->expires = jiffies + adp->timeout * HZ;

            add_timer(tp);
      }

      /*
       * Wait till the low level driver completes the ioctl. After this
       * call, the ioctl either completed successfully or timedout.
       */
      wait_event(wait_q, (kioc->status != -ENODATA));
      if (tp) {
            del_timer_sync(tp);
      }

      /*
       * If the command had timedout, we mark the controller offline
       * before returning
       */
      if (kioc->timedout) {
            adp->quiescent = 0;
      }

      return kioc->status;
}


/**
 * ioctl_done - callback from the low level driver
 * @kioc    : completed ioctl packet
 */
static void
ioctl_done(uioc_t *kioc)
{
      uint32_t    adapno;
      int         iterator;
      mraid_mmadp_t*    adapter;

      /*
       * When the kioc returns from driver, make sure it still doesn't
       * have ENODATA in status. Otherwise, driver will hang on wait_event
       * forever
       */
      if (kioc->status == -ENODATA) {
            con_log(CL_ANN, (KERN_WARNING
                  "megaraid cmm: lld didn't change status!\n"));

            kioc->status = -EINVAL;
      }

      /*
       * Check if this kioc was timedout before. If so, nobody is waiting
       * on this kioc. We don't have to wake up anybody. Instead, we just
       * have to free the kioc
       */
      if (kioc->timedout) {
            iterator    = 0;
            adapter           = NULL;
            adapno            = kioc->adapno;

            con_log(CL_ANN, ( KERN_WARNING "megaraid cmm: completed "
                              "ioctl that was timedout before\n"));

            list_for_each_entry(adapter, &adapters_list_g, list) {
                  if (iterator++ == adapno) break;
            }

            kioc->timedout = 0;

            if (adapter) {
                  mraid_mm_dealloc_kioc( adapter, kioc );
            }
      }
      else {
            wake_up(&wait_q);
      }
}


/**
 * lld_timedout   - callback from the expired timer
 * @ptr           : ioctl packet that timed out
 */
static void
lld_timedout(unsigned long ptr)
{
      uioc_t *kioc      = (uioc_t *)ptr;

      kioc->status      = -ETIME;
      kioc->timedout    = 1;

      con_log(CL_ANN, (KERN_WARNING "megaraid cmm: ioctl timed out\n"));

      wake_up(&wait_q);
}


/**
 * kioc_to_mimd   - Converter from new back to old format
 * @kioc    : Kernel space IOCTL packet (successfully issued)
 * @mimd    : User space MIMD packet
 */
static int
kioc_to_mimd(uioc_t *kioc, mimd_t __user *mimd)
{
      mimd_t                  kmimd;
      uint8_t                 opcode;
      uint8_t                 subopcode;

      mbox64_t          *mbox64;
      mraid_passthru_t  __user *upthru32;
      mraid_passthru_t  *kpthru32;
      mcontroller_t           cinfo;
      mraid_hba_info_t  *hinfo;


      if (copy_from_user(&kmimd, mimd, sizeof(mimd_t)))
            return (-EFAULT);

      opcode            = kmimd.ui.fcs.opcode;
      subopcode   = kmimd.ui.fcs.subopcode;

      if (opcode == 0x82) {
            switch (subopcode) {

            case MEGAIOC_QADAPINFO:

                  hinfo = (mraid_hba_info_t *)(unsigned long)
                              kioc->buf_vaddr;

                  hinfo_to_cinfo(hinfo, &cinfo);

                  if (copy_to_user(kmimd.data, &cinfo, sizeof(cinfo)))
                        return (-EFAULT);

                  return 0;

            default:
                  return (-EINVAL);
            }

            return 0;
      }

      mbox64 = (mbox64_t *)(unsigned long)kioc->cmdbuf;

      if (kioc->user_pthru) {

            upthru32 = kioc->user_pthru;
            kpthru32 = kioc->pthru32;

            if (copy_to_user(&upthru32->scsistatus,
                              &kpthru32->scsistatus,
                              sizeof(uint8_t))) {
                  return (-EFAULT);
            }
      }

      if (kioc->user_data) {
            if (copy_to_user(kioc->user_data, kioc->buf_vaddr,
                              kioc->user_data_len)) {
                  return (-EFAULT);
            }
      }

      if (copy_to_user(&mimd->mbox[17],
                  &mbox64->mbox32.status, sizeof(uint8_t))) {
            return (-EFAULT);
      }

      return 0;
}


/**
 * hinfo_to_cinfo - Convert new format hba info into old format
 * @hinfo   : New format, more comprehensive adapter info
 * @cinfo   : Old format adapter info to support mimd_t apps
 */
static void
hinfo_to_cinfo(mraid_hba_info_t *hinfo, mcontroller_t *cinfo)
{
      if (!hinfo || !cinfo)
            return;

      cinfo->base       = hinfo->baseport;
      cinfo->irq        = hinfo->irq;
      cinfo->numldrv          = hinfo->num_ldrv;
      cinfo->pcibus           = hinfo->pci_bus;
      cinfo->pcidev           = hinfo->pci_slot;
      cinfo->pcifun           = PCI_FUNC(hinfo->pci_dev_fn);
      cinfo->pciid            = hinfo->pci_device_id;
      cinfo->pcivendor  = hinfo->pci_vendor_id;
      cinfo->pcislot          = hinfo->pci_slot;
      cinfo->uid        = hinfo->unique_id;
}


/**
 * mraid_mm_register_adp - Registration routine for low level drivers
 * @lld_adp : Adapter objejct
 */
int
mraid_mm_register_adp(mraid_mmadp_t *lld_adp)
{
      mraid_mmadp_t     *adapter;
      mbox64_t    *mbox_list;
      uioc_t            *kioc;
      uint32_t    rval;
      int         i;


      if (lld_adp->drvr_type != DRVRTYPE_MBOX)
            return (-EINVAL);

      adapter = kzalloc(sizeof(mraid_mmadp_t), GFP_KERNEL);

      if (!adapter)
            return -ENOMEM;


      adapter->unique_id      = lld_adp->unique_id;
      adapter->drvr_type      = lld_adp->drvr_type;
      adapter->drvr_data      = lld_adp->drvr_data;
      adapter->pdev           = lld_adp->pdev;
      adapter->issue_uioc     = lld_adp->issue_uioc;
      adapter->timeout  = lld_adp->timeout;
      adapter->max_kioc = lld_adp->max_kioc;
      adapter->quiescent      = 1;

      /*
       * Allocate single blocks of memory for all required kiocs,
       * mailboxes and passthru structures.
       */
      adapter->kioc_list      = kmalloc(sizeof(uioc_t) * lld_adp->max_kioc,
                                    GFP_KERNEL);
      adapter->mbox_list      = kmalloc(sizeof(mbox64_t) * lld_adp->max_kioc,
                                    GFP_KERNEL);
      adapter->pthru_dma_pool = pci_pool_create("megaraid mm pthru pool",
                                    adapter->pdev,
                                    sizeof(mraid_passthru_t),
                                    16, 0);

      if (!adapter->kioc_list || !adapter->mbox_list ||
                  !adapter->pthru_dma_pool) {

            con_log(CL_ANN, (KERN_WARNING
                  "megaraid cmm: out of memory, %s %d\n", __FUNCTION__,
                  __LINE__));

            rval = (-ENOMEM);

            goto memalloc_error;
      }

      /*
       * Slice kioc_list and make a kioc_pool with the individiual kiocs
       */
      INIT_LIST_HEAD(&adapter->kioc_pool);
      spin_lock_init(&adapter->kioc_pool_lock);
      sema_init(&adapter->kioc_semaphore, lld_adp->max_kioc);

      mbox_list   = (mbox64_t *)adapter->mbox_list;

      for (i = 0; i < lld_adp->max_kioc; i++) {

            kioc        = adapter->kioc_list + i;
            kioc->cmdbuf      = (uint64_t)(unsigned long)(mbox_list + i);
            kioc->pthru32     = pci_pool_alloc(adapter->pthru_dma_pool,
                                    GFP_KERNEL, &kioc->pthru32_h);

            if (!kioc->pthru32) {

                  con_log(CL_ANN, (KERN_WARNING
                        "megaraid cmm: out of memory, %s %d\n",
                              __FUNCTION__, __LINE__));

                  rval = (-ENOMEM);

                  goto pthru_dma_pool_error;
            }

            list_add_tail(&kioc->list, &adapter->kioc_pool);
      }

      // Setup the dma pools for data buffers
      if ((rval = mraid_mm_setup_dma_pools(adapter)) != 0) {
            goto dma_pool_error;
      }

      list_add_tail(&adapter->list, &adapters_list_g);

      adapters_count_g++;

      return 0;

dma_pool_error:
      /* Do nothing */

pthru_dma_pool_error:

      for (i = 0; i < lld_adp->max_kioc; i++) {
            kioc = adapter->kioc_list + i;
            if (kioc->pthru32) {
                  pci_pool_free(adapter->pthru_dma_pool, kioc->pthru32,
                        kioc->pthru32_h);
            }
      }

memalloc_error:

      kfree(adapter->kioc_list);
      kfree(adapter->mbox_list);

      if (adapter->pthru_dma_pool)
            pci_pool_destroy(adapter->pthru_dma_pool);

      kfree(adapter);

      return rval;
}


/**
 * mraid_mm_adapter_app_handle - return the application handle for this adapter
 * @unique_id     : adapter unique identifier
 *
 * For the given driver data, locate the adapter in our global list and
 * return the corresponding handle, which is also used by applications to
 * uniquely identify an adapter.
 *
 * Return adapter handle if found in the list.
 * Return 0 if adapter could not be located, should never happen though.
 */
uint32_t
mraid_mm_adapter_app_handle(uint32_t unique_id)
{
      mraid_mmadp_t     *adapter;
      mraid_mmadp_t     *tmp;
      int         index = 0;

      list_for_each_entry_safe(adapter, tmp, &adapters_list_g, list) {

            if (adapter->unique_id == unique_id) {

                  return MKADAP(index);
            }

            index++;
      }

      return 0;
}


/**
 * mraid_mm_setup_dma_pools - Set up dma buffer pools per adapter
 * @adp     : Adapter softstate
 *
 * We maintain a pool of dma buffers per each adapter. Each pool has one
 * buffer. E.g, we may have 5 dma pools - one each for 4k, 8k ... 64k buffers.
 * We have just one 4k buffer in 4k pool, one 8k buffer in 8k pool etc. We
 * dont' want to waste too much memory by allocating more buffers per each
 * pool.
 */
static int
mraid_mm_setup_dma_pools(mraid_mmadp_t *adp)
{
      mm_dmapool_t      *pool;
      int         bufsize;
      int         i;

      /*
       * Create MAX_DMA_POOLS number of pools
       */
      bufsize = MRAID_MM_INIT_BUFF_SIZE;

      for (i = 0; i < MAX_DMA_POOLS; i++){

            pool = &adp->dma_pool_list[i];

            pool->buf_size = bufsize;
            spin_lock_init(&pool->lock);

            pool->handle = pci_pool_create("megaraid mm data buffer",
                                    adp->pdev, bufsize, 16, 0);

            if (!pool->handle) {
                  goto dma_pool_setup_error;
            }

            pool->vaddr = pci_pool_alloc(pool->handle, GFP_KERNEL,
                                          &pool->paddr);

            if (!pool->vaddr)
                  goto dma_pool_setup_error;

            bufsize = bufsize * 2;
      }

      return 0;

dma_pool_setup_error:

      mraid_mm_teardown_dma_pools(adp);
      return (-ENOMEM);
}


/**
 * mraid_mm_unregister_adp - Unregister routine for low level drivers
 * @unique_id     : UID of the adpater
 *
 * Assumes no outstanding ioctls to llds.
 */
int
mraid_mm_unregister_adp(uint32_t unique_id)
{
      mraid_mmadp_t     *adapter;
      mraid_mmadp_t     *tmp;

      list_for_each_entry_safe(adapter, tmp, &adapters_list_g, list) {


            if (adapter->unique_id == unique_id) {

                  adapters_count_g--;

                  list_del_init(&adapter->list);

                  mraid_mm_free_adp_resources(adapter);

                  kfree(adapter);

                  con_log(CL_ANN, (
                        "megaraid cmm: Unregistered one adapter:%#x\n",
                        unique_id));

                  return 0;
            }
      }

      return (-ENODEV);
}

/**
 * mraid_mm_free_adp_resources - Free adapter softstate
 * @adp     : Adapter softstate
 */
static void
mraid_mm_free_adp_resources(mraid_mmadp_t *adp)
{
      uioc_t      *kioc;
      int   i;

      mraid_mm_teardown_dma_pools(adp);

      for (i = 0; i < adp->max_kioc; i++) {

            kioc = adp->kioc_list + i;

            pci_pool_free(adp->pthru_dma_pool, kioc->pthru32,
                        kioc->pthru32_h);
      }

      kfree(adp->kioc_list);
      kfree(adp->mbox_list);

      pci_pool_destroy(adp->pthru_dma_pool);


      return;
}


/**
 * mraid_mm_teardown_dma_pools - Free all per adapter dma buffers
 * @adp     : Adapter softstate
 */
static void
mraid_mm_teardown_dma_pools(mraid_mmadp_t *adp)
{
      int         i;
      mm_dmapool_t      *pool;

      for (i = 0; i < MAX_DMA_POOLS; i++) {

            pool = &adp->dma_pool_list[i];

            if (pool->handle) {

                  if (pool->vaddr)
                        pci_pool_free(pool->handle, pool->vaddr,
                                          pool->paddr);

                  pci_pool_destroy(pool->handle);
                  pool->handle = NULL;
            }
      }

      return;
}

/**
 * mraid_mm_init  - Module entry point
 */
static int __init
mraid_mm_init(void)
{
      // Announce the driver version
      con_log(CL_ANN, (KERN_INFO "megaraid cmm: %s %s\n",
            LSI_COMMON_MOD_VERSION, LSI_COMMON_MOD_EXT_VERSION));

      majorno = register_chrdev(0, "megadev", &lsi_fops);

      if (majorno < 0) {
            con_log(CL_ANN, ("megaraid cmm: cannot get major\n"));
            return majorno;
      }

      init_waitqueue_head(&wait_q);

      INIT_LIST_HEAD(&adapters_list_g);

      return 0;
}


#ifdef CONFIG_COMPAT
/**
 * mraid_mm_compat_ioctl      - 32bit to 64bit ioctl conversion routine
 * @filep   : file operations pointer (ignored)
 * @cmd           : ioctl command
 * @arg           : user ioctl packet
 */
static long
mraid_mm_compat_ioctl(struct file *filep, unsigned int cmd,
                  unsigned long arg)
{
      int err;

      err = mraid_mm_ioctl(NULL, filep, cmd, arg);

      return err;
}
#endif

/**
 * mraid_mm_exit  - Module exit point
 */
static void __exit
mraid_mm_exit(void)
{
      con_log(CL_DLEVEL1 , ("exiting common mod\n"));

      unregister_chrdev(majorno, "megadev");
}

module_init(mraid_mm_init);
module_exit(mraid_mm_exit);

/* vi: set ts=8 sw=8 tw=78: */

Generated by  Doxygen 1.6.0   Back to index