162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci *			Linux MegaRAID device driver
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (c) 2003-2004  LSI Logic Corporation.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * FILE		: megaraid_mm.c
962306a36Sopenharmony_ci * Version	: v2.20.2.7 (Jul 16 2006)
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * Common management module
1262306a36Sopenharmony_ci */
1362306a36Sopenharmony_ci#include <linux/sched.h>
1462306a36Sopenharmony_ci#include <linux/slab.h>
1562306a36Sopenharmony_ci#include <linux/mutex.h>
1662306a36Sopenharmony_ci#include "megaraid_mm.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci// Entry points for char node driver
2062306a36Sopenharmony_cistatic DEFINE_MUTEX(mraid_mm_mutex);
2162306a36Sopenharmony_cistatic int mraid_mm_open(struct inode *, struct file *);
2262306a36Sopenharmony_cistatic long mraid_mm_unlocked_ioctl(struct file *, uint, unsigned long);
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci// routines to convert to and from the old the format
2662306a36Sopenharmony_cistatic int mimd_to_kioc(mimd_t __user *, mraid_mmadp_t *, uioc_t *);
2762306a36Sopenharmony_cistatic int kioc_to_mimd(uioc_t *, mimd_t __user *);
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci// Helper functions
3162306a36Sopenharmony_cistatic int handle_drvrcmd(void __user *, uint8_t, int *);
3262306a36Sopenharmony_cistatic int lld_ioctl(mraid_mmadp_t *, uioc_t *);
3362306a36Sopenharmony_cistatic void ioctl_done(uioc_t *);
3462306a36Sopenharmony_cistatic void lld_timedout(struct timer_list *);
3562306a36Sopenharmony_cistatic void hinfo_to_cinfo(mraid_hba_info_t *, mcontroller_t *);
3662306a36Sopenharmony_cistatic mraid_mmadp_t *mraid_mm_get_adapter(mimd_t __user *, int *);
3762306a36Sopenharmony_cistatic uioc_t *mraid_mm_alloc_kioc(mraid_mmadp_t *);
3862306a36Sopenharmony_cistatic void mraid_mm_dealloc_kioc(mraid_mmadp_t *, uioc_t *);
3962306a36Sopenharmony_cistatic int mraid_mm_attach_buf(mraid_mmadp_t *, uioc_t *, int);
4062306a36Sopenharmony_cistatic int mraid_mm_setup_dma_pools(mraid_mmadp_t *);
4162306a36Sopenharmony_cistatic void mraid_mm_free_adp_resources(mraid_mmadp_t *);
4262306a36Sopenharmony_cistatic void mraid_mm_teardown_dma_pools(mraid_mmadp_t *);
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ciMODULE_AUTHOR("LSI Logic Corporation");
4562306a36Sopenharmony_ciMODULE_DESCRIPTION("LSI Logic Management Module");
4662306a36Sopenharmony_ciMODULE_LICENSE("GPL");
4762306a36Sopenharmony_ciMODULE_VERSION(LSI_COMMON_MOD_VERSION);
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic int dbglevel = CL_ANN;
5062306a36Sopenharmony_cimodule_param_named(dlevel, dbglevel, int, 0);
5162306a36Sopenharmony_ciMODULE_PARM_DESC(dlevel, "Debug level (default=0)");
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ciEXPORT_SYMBOL(mraid_mm_register_adp);
5462306a36Sopenharmony_ciEXPORT_SYMBOL(mraid_mm_unregister_adp);
5562306a36Sopenharmony_ciEXPORT_SYMBOL(mraid_mm_adapter_app_handle);
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic uint32_t drvr_ver	= 0x02200207;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistatic int adapters_count_g;
6062306a36Sopenharmony_cistatic struct list_head adapters_list_g;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic wait_queue_head_t wait_q;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic const struct file_operations lsi_fops = {
6562306a36Sopenharmony_ci	.open	= mraid_mm_open,
6662306a36Sopenharmony_ci	.unlocked_ioctl = mraid_mm_unlocked_ioctl,
6762306a36Sopenharmony_ci	.compat_ioctl = compat_ptr_ioctl,
6862306a36Sopenharmony_ci	.owner	= THIS_MODULE,
6962306a36Sopenharmony_ci	.llseek = noop_llseek,
7062306a36Sopenharmony_ci};
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic struct miscdevice megaraid_mm_dev = {
7362306a36Sopenharmony_ci	.minor	= MISC_DYNAMIC_MINOR,
7462306a36Sopenharmony_ci	.name   = "megadev0",
7562306a36Sopenharmony_ci	.fops   = &lsi_fops,
7662306a36Sopenharmony_ci};
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci/**
7962306a36Sopenharmony_ci * mraid_mm_open - open routine for char node interface
8062306a36Sopenharmony_ci * @inode	: unused
8162306a36Sopenharmony_ci * @filep	: unused
8262306a36Sopenharmony_ci *
8362306a36Sopenharmony_ci * Allow ioctl operations by apps only if they have superuser privilege.
8462306a36Sopenharmony_ci */
8562306a36Sopenharmony_cistatic int
8662306a36Sopenharmony_cimraid_mm_open(struct inode *inode, struct file *filep)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	/*
8962306a36Sopenharmony_ci	 * Only allow superuser to access private ioctl interface
9062306a36Sopenharmony_ci	 */
9162306a36Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN)) return (-EACCES);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	return 0;
9462306a36Sopenharmony_ci}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci/**
9762306a36Sopenharmony_ci * mraid_mm_ioctl - module entry-point for ioctls
9862306a36Sopenharmony_ci * @filep	: file operations pointer (ignored)
9962306a36Sopenharmony_ci * @cmd		: ioctl command
10062306a36Sopenharmony_ci * @arg		: user ioctl packet
10162306a36Sopenharmony_ci */
10262306a36Sopenharmony_cistatic int
10362306a36Sopenharmony_cimraid_mm_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	uioc_t		*kioc;
10662306a36Sopenharmony_ci	char		signature[EXT_IOCTL_SIGN_SZ]	= {0};
10762306a36Sopenharmony_ci	int		rval;
10862306a36Sopenharmony_ci	mraid_mmadp_t	*adp;
10962306a36Sopenharmony_ci	uint8_t		old_ioctl;
11062306a36Sopenharmony_ci	int		drvrcmd_rval;
11162306a36Sopenharmony_ci	void __user *argp = (void __user *)arg;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	/*
11462306a36Sopenharmony_ci	 * Make sure only USCSICMD are issued through this interface.
11562306a36Sopenharmony_ci	 * MIMD application would still fire different command.
11662306a36Sopenharmony_ci	 */
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	if ((_IOC_TYPE(cmd) != MEGAIOC_MAGIC) && (cmd != USCSICMD)) {
11962306a36Sopenharmony_ci		return (-EINVAL);
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	/*
12362306a36Sopenharmony_ci	 * Look for signature to see if this is the new or old ioctl format.
12462306a36Sopenharmony_ci	 */
12562306a36Sopenharmony_ci	if (copy_from_user(signature, argp, EXT_IOCTL_SIGN_SZ)) {
12662306a36Sopenharmony_ci		con_log(CL_ANN, (KERN_WARNING
12762306a36Sopenharmony_ci			"megaraid cmm: copy from usr addr failed\n"));
12862306a36Sopenharmony_ci		return (-EFAULT);
12962306a36Sopenharmony_ci	}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	if (memcmp(signature, EXT_IOCTL_SIGN, EXT_IOCTL_SIGN_SZ) == 0)
13262306a36Sopenharmony_ci		old_ioctl = 0;
13362306a36Sopenharmony_ci	else
13462306a36Sopenharmony_ci		old_ioctl = 1;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	/*
13762306a36Sopenharmony_ci	 * At present, we don't support the new ioctl packet
13862306a36Sopenharmony_ci	 */
13962306a36Sopenharmony_ci	if (!old_ioctl )
14062306a36Sopenharmony_ci		return (-EINVAL);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	/*
14362306a36Sopenharmony_ci	 * If it is a driver ioctl (as opposed to fw ioctls), then we can
14462306a36Sopenharmony_ci	 * handle the command locally. rval > 0 means it is not a drvr cmd
14562306a36Sopenharmony_ci	 */
14662306a36Sopenharmony_ci	rval = handle_drvrcmd(argp, old_ioctl, &drvrcmd_rval);
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	if (rval < 0)
14962306a36Sopenharmony_ci		return rval;
15062306a36Sopenharmony_ci	else if (rval == 0)
15162306a36Sopenharmony_ci		return drvrcmd_rval;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	rval = 0;
15462306a36Sopenharmony_ci	if ((adp = mraid_mm_get_adapter(argp, &rval)) == NULL) {
15562306a36Sopenharmony_ci		return rval;
15662306a36Sopenharmony_ci	}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	/*
15962306a36Sopenharmony_ci	 * Check if adapter can accept ioctl. We may have marked it offline
16062306a36Sopenharmony_ci	 * if any previous kioc had timedout on this controller.
16162306a36Sopenharmony_ci	 */
16262306a36Sopenharmony_ci	if (!adp->quiescent) {
16362306a36Sopenharmony_ci		con_log(CL_ANN, (KERN_WARNING
16462306a36Sopenharmony_ci			"megaraid cmm: controller cannot accept cmds due to "
16562306a36Sopenharmony_ci			"earlier errors\n" ));
16662306a36Sopenharmony_ci		return -EFAULT;
16762306a36Sopenharmony_ci	}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	/*
17062306a36Sopenharmony_ci	 * The following call will block till a kioc is available
17162306a36Sopenharmony_ci	 * or return NULL if the list head is empty for the pointer
17262306a36Sopenharmony_ci	 * of type mraid_mmapt passed to mraid_mm_alloc_kioc
17362306a36Sopenharmony_ci	 */
17462306a36Sopenharmony_ci	kioc = mraid_mm_alloc_kioc(adp);
17562306a36Sopenharmony_ci	if (!kioc)
17662306a36Sopenharmony_ci		return -ENXIO;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	/*
17962306a36Sopenharmony_ci	 * User sent the old mimd_t ioctl packet. Convert it to uioc_t.
18062306a36Sopenharmony_ci	 */
18162306a36Sopenharmony_ci	if ((rval = mimd_to_kioc(argp, adp, kioc))) {
18262306a36Sopenharmony_ci		mraid_mm_dealloc_kioc(adp, kioc);
18362306a36Sopenharmony_ci		return rval;
18462306a36Sopenharmony_ci	}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	kioc->done = ioctl_done;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	/*
18962306a36Sopenharmony_ci	 * Issue the IOCTL to the low level driver. After the IOCTL completes
19062306a36Sopenharmony_ci	 * release the kioc if and only if it was _not_ timedout. If it was
19162306a36Sopenharmony_ci	 * timedout, that means that resources are still with low level driver.
19262306a36Sopenharmony_ci	 */
19362306a36Sopenharmony_ci	if ((rval = lld_ioctl(adp, kioc))) {
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci		if (!kioc->timedout)
19662306a36Sopenharmony_ci			mraid_mm_dealloc_kioc(adp, kioc);
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci		return rval;
19962306a36Sopenharmony_ci	}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	/*
20262306a36Sopenharmony_ci	 * Convert the kioc back to user space
20362306a36Sopenharmony_ci	 */
20462306a36Sopenharmony_ci	rval = kioc_to_mimd(kioc, argp);
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	/*
20762306a36Sopenharmony_ci	 * Return the kioc to free pool
20862306a36Sopenharmony_ci	 */
20962306a36Sopenharmony_ci	mraid_mm_dealloc_kioc(adp, kioc);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	return rval;
21262306a36Sopenharmony_ci}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_cistatic long
21562306a36Sopenharmony_cimraid_mm_unlocked_ioctl(struct file *filep, unsigned int cmd,
21662306a36Sopenharmony_ci		        unsigned long arg)
21762306a36Sopenharmony_ci{
21862306a36Sopenharmony_ci	int err;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	mutex_lock(&mraid_mm_mutex);
22162306a36Sopenharmony_ci	err = mraid_mm_ioctl(filep, cmd, arg);
22262306a36Sopenharmony_ci	mutex_unlock(&mraid_mm_mutex);
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	return err;
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci/**
22862306a36Sopenharmony_ci * mraid_mm_get_adapter - Returns corresponding adapters for the mimd packet
22962306a36Sopenharmony_ci * @umimd	: User space mimd_t ioctl packet
23062306a36Sopenharmony_ci * @rval	: returned success/error status
23162306a36Sopenharmony_ci *
23262306a36Sopenharmony_ci * The function return value is a pointer to the located @adapter.
23362306a36Sopenharmony_ci */
23462306a36Sopenharmony_cistatic mraid_mmadp_t *
23562306a36Sopenharmony_cimraid_mm_get_adapter(mimd_t __user *umimd, int *rval)
23662306a36Sopenharmony_ci{
23762306a36Sopenharmony_ci	mraid_mmadp_t	*adapter;
23862306a36Sopenharmony_ci	mimd_t		mimd;
23962306a36Sopenharmony_ci	uint32_t	adapno;
24062306a36Sopenharmony_ci	int		iterator;
24162306a36Sopenharmony_ci	bool		is_found;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	if (copy_from_user(&mimd, umimd, sizeof(mimd_t))) {
24462306a36Sopenharmony_ci		*rval = -EFAULT;
24562306a36Sopenharmony_ci		return NULL;
24662306a36Sopenharmony_ci	}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	adapno = GETADAP(mimd.ui.fcs.adapno);
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	if (adapno >= adapters_count_g) {
25162306a36Sopenharmony_ci		*rval = -ENODEV;
25262306a36Sopenharmony_ci		return NULL;
25362306a36Sopenharmony_ci	}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	adapter = NULL;
25662306a36Sopenharmony_ci	iterator = 0;
25762306a36Sopenharmony_ci	is_found = false;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	list_for_each_entry(adapter, &adapters_list_g, list) {
26062306a36Sopenharmony_ci		if (iterator++ == adapno) {
26162306a36Sopenharmony_ci			is_found = true;
26262306a36Sopenharmony_ci			break;
26362306a36Sopenharmony_ci		}
26462306a36Sopenharmony_ci	}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	if (!is_found) {
26762306a36Sopenharmony_ci		*rval = -ENODEV;
26862306a36Sopenharmony_ci		return NULL;
26962306a36Sopenharmony_ci	}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	return adapter;
27262306a36Sopenharmony_ci}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci/**
27562306a36Sopenharmony_ci * handle_drvrcmd - Checks if the opcode is a driver cmd and if it is, handles it.
27662306a36Sopenharmony_ci * @arg		: packet sent by the user app
27762306a36Sopenharmony_ci * @old_ioctl	: mimd if 1; uioc otherwise
27862306a36Sopenharmony_ci * @rval	: pointer for command's returned value (not function status)
27962306a36Sopenharmony_ci */
28062306a36Sopenharmony_cistatic int
28162306a36Sopenharmony_cihandle_drvrcmd(void __user *arg, uint8_t old_ioctl, int *rval)
28262306a36Sopenharmony_ci{
28362306a36Sopenharmony_ci	mimd_t		__user *umimd;
28462306a36Sopenharmony_ci	mimd_t		kmimd;
28562306a36Sopenharmony_ci	uint8_t		opcode;
28662306a36Sopenharmony_ci	uint8_t		subopcode;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	if (old_ioctl)
28962306a36Sopenharmony_ci		goto old_packet;
29062306a36Sopenharmony_ci	else
29162306a36Sopenharmony_ci		goto new_packet;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_cinew_packet:
29462306a36Sopenharmony_ci	return (-ENOTSUPP);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ciold_packet:
29762306a36Sopenharmony_ci	*rval = 0;
29862306a36Sopenharmony_ci	umimd = arg;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	if (copy_from_user(&kmimd, umimd, sizeof(mimd_t)))
30162306a36Sopenharmony_ci		return (-EFAULT);
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	opcode		= kmimd.ui.fcs.opcode;
30462306a36Sopenharmony_ci	subopcode	= kmimd.ui.fcs.subopcode;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	/*
30762306a36Sopenharmony_ci	 * If the opcode is 0x82 and the subopcode is either GET_DRVRVER or
30862306a36Sopenharmony_ci	 * GET_NUMADP, then we can handle. Otherwise we should return 1 to
30962306a36Sopenharmony_ci	 * indicate that we cannot handle this.
31062306a36Sopenharmony_ci	 */
31162306a36Sopenharmony_ci	if (opcode != 0x82)
31262306a36Sopenharmony_ci		return 1;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	switch (subopcode) {
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	case MEGAIOC_QDRVRVER:
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci		if (copy_to_user(kmimd.data, &drvr_ver, sizeof(uint32_t)))
31962306a36Sopenharmony_ci			return (-EFAULT);
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci		return 0;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	case MEGAIOC_QNADAP:
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci		*rval = adapters_count_g;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci		if (copy_to_user(kmimd.data, &adapters_count_g,
32862306a36Sopenharmony_ci				sizeof(uint32_t)))
32962306a36Sopenharmony_ci			return (-EFAULT);
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci		return 0;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	default:
33462306a36Sopenharmony_ci		/* cannot handle */
33562306a36Sopenharmony_ci		return 1;
33662306a36Sopenharmony_ci	}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	return 0;
33962306a36Sopenharmony_ci}
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci/**
34362306a36Sopenharmony_ci * mimd_to_kioc	- Converter from old to new ioctl format
34462306a36Sopenharmony_ci * @umimd	: user space old MIMD IOCTL
34562306a36Sopenharmony_ci * @adp		: adapter softstate
34662306a36Sopenharmony_ci * @kioc	: kernel space new format IOCTL
34762306a36Sopenharmony_ci *
34862306a36Sopenharmony_ci * Routine to convert MIMD interface IOCTL to new interface IOCTL packet. The
34962306a36Sopenharmony_ci * new packet is in kernel space so that driver can perform operations on it
35062306a36Sopenharmony_ci * freely.
35162306a36Sopenharmony_ci */
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_cistatic int
35462306a36Sopenharmony_cimimd_to_kioc(mimd_t __user *umimd, mraid_mmadp_t *adp, uioc_t *kioc)
35562306a36Sopenharmony_ci{
35662306a36Sopenharmony_ci	mbox64_t		*mbox64;
35762306a36Sopenharmony_ci	mbox_t			*mbox;
35862306a36Sopenharmony_ci	mraid_passthru_t	*pthru32;
35962306a36Sopenharmony_ci	uint32_t		adapno;
36062306a36Sopenharmony_ci	uint8_t			opcode;
36162306a36Sopenharmony_ci	uint8_t			subopcode;
36262306a36Sopenharmony_ci	mimd_t			mimd;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	if (copy_from_user(&mimd, umimd, sizeof(mimd_t)))
36562306a36Sopenharmony_ci		return (-EFAULT);
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	/*
36862306a36Sopenharmony_ci	 * Applications are not allowed to send extd pthru
36962306a36Sopenharmony_ci	 */
37062306a36Sopenharmony_ci	if ((mimd.mbox[0] == MBOXCMD_PASSTHRU64) ||
37162306a36Sopenharmony_ci			(mimd.mbox[0] == MBOXCMD_EXTPTHRU))
37262306a36Sopenharmony_ci		return (-EINVAL);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	opcode		= mimd.ui.fcs.opcode;
37562306a36Sopenharmony_ci	subopcode	= mimd.ui.fcs.subopcode;
37662306a36Sopenharmony_ci	adapno		= GETADAP(mimd.ui.fcs.adapno);
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	if (adapno >= adapters_count_g)
37962306a36Sopenharmony_ci		return (-ENODEV);
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	kioc->adapno	= adapno;
38262306a36Sopenharmony_ci	kioc->mb_type	= MBOX_LEGACY;
38362306a36Sopenharmony_ci	kioc->app_type	= APPTYPE_MIMD;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	switch (opcode) {
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	case 0x82:
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci		if (subopcode == MEGAIOC_QADAPINFO) {
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci			kioc->opcode	= GET_ADAP_INFO;
39262306a36Sopenharmony_ci			kioc->data_dir	= UIOC_RD;
39362306a36Sopenharmony_ci			kioc->xferlen	= sizeof(mraid_hba_info_t);
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci			if (mraid_mm_attach_buf(adp, kioc, kioc->xferlen))
39662306a36Sopenharmony_ci				return (-ENOMEM);
39762306a36Sopenharmony_ci		}
39862306a36Sopenharmony_ci		else {
39962306a36Sopenharmony_ci			con_log(CL_ANN, (KERN_WARNING
40062306a36Sopenharmony_ci					"megaraid cmm: Invalid subop\n"));
40162306a36Sopenharmony_ci			return (-EINVAL);
40262306a36Sopenharmony_ci		}
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci		break;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	case 0x81:
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci		kioc->opcode		= MBOX_CMD;
40962306a36Sopenharmony_ci		kioc->xferlen		= mimd.ui.fcs.length;
41062306a36Sopenharmony_ci		kioc->user_data_len	= kioc->xferlen;
41162306a36Sopenharmony_ci		kioc->user_data		= mimd.ui.fcs.buffer;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci		if (mraid_mm_attach_buf(adp, kioc, kioc->xferlen))
41462306a36Sopenharmony_ci			return (-ENOMEM);
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci		if (mimd.outlen) kioc->data_dir  = UIOC_RD;
41762306a36Sopenharmony_ci		if (mimd.inlen) kioc->data_dir |= UIOC_WR;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci		break;
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	case 0x80:
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci		kioc->opcode		= MBOX_CMD;
42462306a36Sopenharmony_ci		kioc->xferlen		= (mimd.outlen > mimd.inlen) ?
42562306a36Sopenharmony_ci						mimd.outlen : mimd.inlen;
42662306a36Sopenharmony_ci		kioc->user_data_len	= kioc->xferlen;
42762306a36Sopenharmony_ci		kioc->user_data		= mimd.data;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci		if (mraid_mm_attach_buf(adp, kioc, kioc->xferlen))
43062306a36Sopenharmony_ci			return (-ENOMEM);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci		if (mimd.outlen) kioc->data_dir  = UIOC_RD;
43362306a36Sopenharmony_ci		if (mimd.inlen) kioc->data_dir |= UIOC_WR;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci		break;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	default:
43862306a36Sopenharmony_ci		return (-EINVAL);
43962306a36Sopenharmony_ci	}
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	/*
44262306a36Sopenharmony_ci	 * If driver command, nothing else to do
44362306a36Sopenharmony_ci	 */
44462306a36Sopenharmony_ci	if (opcode == 0x82)
44562306a36Sopenharmony_ci		return 0;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	/*
44862306a36Sopenharmony_ci	 * This is a mailbox cmd; copy the mailbox from mimd
44962306a36Sopenharmony_ci	 */
45062306a36Sopenharmony_ci	mbox64	= (mbox64_t *)((unsigned long)kioc->cmdbuf);
45162306a36Sopenharmony_ci	mbox	= &mbox64->mbox32;
45262306a36Sopenharmony_ci	memcpy(mbox, mimd.mbox, 14);
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	if (mbox->cmd != MBOXCMD_PASSTHRU) {	// regular DCMD
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci		mbox->xferaddr	= (uint32_t)kioc->buf_paddr;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci		if (kioc->data_dir & UIOC_WR) {
45962306a36Sopenharmony_ci			if (copy_from_user(kioc->buf_vaddr, kioc->user_data,
46062306a36Sopenharmony_ci							kioc->xferlen)) {
46162306a36Sopenharmony_ci				return (-EFAULT);
46262306a36Sopenharmony_ci			}
46362306a36Sopenharmony_ci		}
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci		return 0;
46662306a36Sopenharmony_ci	}
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	/*
46962306a36Sopenharmony_ci	 * This is a regular 32-bit pthru cmd; mbox points to pthru struct.
47062306a36Sopenharmony_ci	 * Just like in above case, the beginning for memblk is treated as
47162306a36Sopenharmony_ci	 * a mailbox. The passthru will begin at next 1K boundary. And the
47262306a36Sopenharmony_ci	 * data will start 1K after that.
47362306a36Sopenharmony_ci	 */
47462306a36Sopenharmony_ci	pthru32			= kioc->pthru32;
47562306a36Sopenharmony_ci	kioc->user_pthru	= &umimd->pthru;
47662306a36Sopenharmony_ci	mbox->xferaddr		= (uint32_t)kioc->pthru32_h;
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	if (copy_from_user(pthru32, kioc->user_pthru,
47962306a36Sopenharmony_ci			sizeof(mraid_passthru_t))) {
48062306a36Sopenharmony_ci		return (-EFAULT);
48162306a36Sopenharmony_ci	}
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	pthru32->dataxferaddr	= kioc->buf_paddr;
48462306a36Sopenharmony_ci	if (kioc->data_dir & UIOC_WR) {
48562306a36Sopenharmony_ci		if (pthru32->dataxferlen > kioc->xferlen)
48662306a36Sopenharmony_ci			return -EINVAL;
48762306a36Sopenharmony_ci		if (copy_from_user(kioc->buf_vaddr, kioc->user_data,
48862306a36Sopenharmony_ci						pthru32->dataxferlen)) {
48962306a36Sopenharmony_ci			return (-EFAULT);
49062306a36Sopenharmony_ci		}
49162306a36Sopenharmony_ci	}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	return 0;
49462306a36Sopenharmony_ci}
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci/**
49762306a36Sopenharmony_ci * mraid_mm_attach_buf - Attach a free dma buffer for required size
49862306a36Sopenharmony_ci * @adp		: Adapter softstate
49962306a36Sopenharmony_ci * @kioc	: kioc that the buffer needs to be attached to
50062306a36Sopenharmony_ci * @xferlen	: required length for buffer
50162306a36Sopenharmony_ci *
50262306a36Sopenharmony_ci * First we search for a pool with smallest buffer that is >= @xferlen. If
50362306a36Sopenharmony_ci * that pool has no free buffer, we will try for the next bigger size. If none
50462306a36Sopenharmony_ci * is available, we will try to allocate the smallest buffer that is >=
50562306a36Sopenharmony_ci * @xferlen and attach it the pool.
50662306a36Sopenharmony_ci */
50762306a36Sopenharmony_cistatic int
50862306a36Sopenharmony_cimraid_mm_attach_buf(mraid_mmadp_t *adp, uioc_t *kioc, int xferlen)
50962306a36Sopenharmony_ci{
51062306a36Sopenharmony_ci	mm_dmapool_t	*pool;
51162306a36Sopenharmony_ci	int		right_pool = -1;
51262306a36Sopenharmony_ci	unsigned long	flags;
51362306a36Sopenharmony_ci	int		i;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	kioc->pool_index	= -1;
51662306a36Sopenharmony_ci	kioc->buf_vaddr		= NULL;
51762306a36Sopenharmony_ci	kioc->buf_paddr		= 0;
51862306a36Sopenharmony_ci	kioc->free_buf		= 0;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	/*
52162306a36Sopenharmony_ci	 * We need xferlen amount of memory. See if we can get it from our
52262306a36Sopenharmony_ci	 * dma pools. If we don't get exact size, we will try bigger buffer
52362306a36Sopenharmony_ci	 */
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	for (i = 0; i < MAX_DMA_POOLS; i++) {
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci		pool = &adp->dma_pool_list[i];
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci		if (xferlen > pool->buf_size)
53062306a36Sopenharmony_ci			continue;
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci		if (right_pool == -1)
53362306a36Sopenharmony_ci			right_pool = i;
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci		spin_lock_irqsave(&pool->lock, flags);
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci		if (!pool->in_use) {
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci			pool->in_use		= 1;
54062306a36Sopenharmony_ci			kioc->pool_index	= i;
54162306a36Sopenharmony_ci			kioc->buf_vaddr		= pool->vaddr;
54262306a36Sopenharmony_ci			kioc->buf_paddr		= pool->paddr;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci			spin_unlock_irqrestore(&pool->lock, flags);
54562306a36Sopenharmony_ci			return 0;
54662306a36Sopenharmony_ci		}
54762306a36Sopenharmony_ci		else {
54862306a36Sopenharmony_ci			spin_unlock_irqrestore(&pool->lock, flags);
54962306a36Sopenharmony_ci			continue;
55062306a36Sopenharmony_ci		}
55162306a36Sopenharmony_ci	}
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	/*
55462306a36Sopenharmony_ci	 * If xferlen doesn't match any of our pools, return error
55562306a36Sopenharmony_ci	 */
55662306a36Sopenharmony_ci	if (right_pool == -1)
55762306a36Sopenharmony_ci		return -EINVAL;
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	/*
56062306a36Sopenharmony_ci	 * We did not get any buffer from the preallocated pool. Let us try
56162306a36Sopenharmony_ci	 * to allocate one new buffer. NOTE: This is a blocking call.
56262306a36Sopenharmony_ci	 */
56362306a36Sopenharmony_ci	pool = &adp->dma_pool_list[right_pool];
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	spin_lock_irqsave(&pool->lock, flags);
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	kioc->pool_index	= right_pool;
56862306a36Sopenharmony_ci	kioc->free_buf		= 1;
56962306a36Sopenharmony_ci	kioc->buf_vaddr		= dma_pool_alloc(pool->handle, GFP_ATOMIC,
57062306a36Sopenharmony_ci							&kioc->buf_paddr);
57162306a36Sopenharmony_ci	spin_unlock_irqrestore(&pool->lock, flags);
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	if (!kioc->buf_vaddr)
57462306a36Sopenharmony_ci		return -ENOMEM;
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	return 0;
57762306a36Sopenharmony_ci}
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci/**
58062306a36Sopenharmony_ci * mraid_mm_alloc_kioc - Returns a uioc_t from free list
58162306a36Sopenharmony_ci * @adp	: Adapter softstate for this module
58262306a36Sopenharmony_ci *
58362306a36Sopenharmony_ci * The kioc_semaphore is initialized with number of kioc nodes in the
58462306a36Sopenharmony_ci * free kioc pool. If the kioc pool is empty, this function blocks till
58562306a36Sopenharmony_ci * a kioc becomes free.
58662306a36Sopenharmony_ci */
58762306a36Sopenharmony_cistatic uioc_t *
58862306a36Sopenharmony_cimraid_mm_alloc_kioc(mraid_mmadp_t *adp)
58962306a36Sopenharmony_ci{
59062306a36Sopenharmony_ci	uioc_t			*kioc;
59162306a36Sopenharmony_ci	struct list_head*	head;
59262306a36Sopenharmony_ci	unsigned long		flags;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	down(&adp->kioc_semaphore);
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	spin_lock_irqsave(&adp->kioc_pool_lock, flags);
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	head = &adp->kioc_pool;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	if (list_empty(head)) {
60162306a36Sopenharmony_ci		up(&adp->kioc_semaphore);
60262306a36Sopenharmony_ci		spin_unlock_irqrestore(&adp->kioc_pool_lock, flags);
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci		con_log(CL_ANN, ("megaraid cmm: kioc list empty!\n"));
60562306a36Sopenharmony_ci		return NULL;
60662306a36Sopenharmony_ci	}
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	kioc = list_entry(head->next, uioc_t, list);
60962306a36Sopenharmony_ci	list_del_init(&kioc->list);
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	spin_unlock_irqrestore(&adp->kioc_pool_lock, flags);
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	memset((caddr_t)(unsigned long)kioc->cmdbuf, 0, sizeof(mbox64_t));
61462306a36Sopenharmony_ci	memset((caddr_t) kioc->pthru32, 0, sizeof(mraid_passthru_t));
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	kioc->buf_vaddr		= NULL;
61762306a36Sopenharmony_ci	kioc->buf_paddr		= 0;
61862306a36Sopenharmony_ci	kioc->pool_index	=-1;
61962306a36Sopenharmony_ci	kioc->free_buf		= 0;
62062306a36Sopenharmony_ci	kioc->user_data		= NULL;
62162306a36Sopenharmony_ci	kioc->user_data_len	= 0;
62262306a36Sopenharmony_ci	kioc->user_pthru	= NULL;
62362306a36Sopenharmony_ci	kioc->timedout		= 0;
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	return kioc;
62662306a36Sopenharmony_ci}
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci/**
62962306a36Sopenharmony_ci * mraid_mm_dealloc_kioc - Return kioc to free pool
63062306a36Sopenharmony_ci * @adp		: Adapter softstate
63162306a36Sopenharmony_ci * @kioc	: uioc_t node to be returned to free pool
63262306a36Sopenharmony_ci */
63362306a36Sopenharmony_cistatic void
63462306a36Sopenharmony_cimraid_mm_dealloc_kioc(mraid_mmadp_t *adp, uioc_t *kioc)
63562306a36Sopenharmony_ci{
63662306a36Sopenharmony_ci	mm_dmapool_t	*pool;
63762306a36Sopenharmony_ci	unsigned long	flags;
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	if (kioc->pool_index != -1) {
64062306a36Sopenharmony_ci		pool = &adp->dma_pool_list[kioc->pool_index];
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci		/* This routine may be called in non-isr context also */
64362306a36Sopenharmony_ci		spin_lock_irqsave(&pool->lock, flags);
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci		/*
64662306a36Sopenharmony_ci		 * While attaching the dma buffer, if we didn't get the
64762306a36Sopenharmony_ci		 * required buffer from the pool, we would have allocated
64862306a36Sopenharmony_ci		 * it at the run time and set the free_buf flag. We must
64962306a36Sopenharmony_ci		 * free that buffer. Otherwise, just mark that the buffer is
65062306a36Sopenharmony_ci		 * not in use
65162306a36Sopenharmony_ci		 */
65262306a36Sopenharmony_ci		if (kioc->free_buf == 1)
65362306a36Sopenharmony_ci			dma_pool_free(pool->handle, kioc->buf_vaddr,
65462306a36Sopenharmony_ci							kioc->buf_paddr);
65562306a36Sopenharmony_ci		else
65662306a36Sopenharmony_ci			pool->in_use = 0;
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci		spin_unlock_irqrestore(&pool->lock, flags);
65962306a36Sopenharmony_ci	}
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	/* Return the kioc to the free pool */
66262306a36Sopenharmony_ci	spin_lock_irqsave(&adp->kioc_pool_lock, flags);
66362306a36Sopenharmony_ci	list_add(&kioc->list, &adp->kioc_pool);
66462306a36Sopenharmony_ci	spin_unlock_irqrestore(&adp->kioc_pool_lock, flags);
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	/* increment the free kioc count */
66762306a36Sopenharmony_ci	up(&adp->kioc_semaphore);
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	return;
67062306a36Sopenharmony_ci}
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci/**
67362306a36Sopenharmony_ci * lld_ioctl - Routine to issue ioctl to low level drvr
67462306a36Sopenharmony_ci * @adp		: The adapter handle
67562306a36Sopenharmony_ci * @kioc	: The ioctl packet with kernel addresses
67662306a36Sopenharmony_ci */
67762306a36Sopenharmony_cistatic int
67862306a36Sopenharmony_cilld_ioctl(mraid_mmadp_t *adp, uioc_t *kioc)
67962306a36Sopenharmony_ci{
68062306a36Sopenharmony_ci	int			rval;
68162306a36Sopenharmony_ci	struct uioc_timeout	timeout = { };
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	kioc->status	= -ENODATA;
68462306a36Sopenharmony_ci	rval		= adp->issue_uioc(adp->drvr_data, kioc, IOCTL_ISSUE);
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	if (rval) return rval;
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	/*
68962306a36Sopenharmony_ci	 * Start the timer
69062306a36Sopenharmony_ci	 */
69162306a36Sopenharmony_ci	if (adp->timeout > 0) {
69262306a36Sopenharmony_ci		timeout.uioc = kioc;
69362306a36Sopenharmony_ci		timer_setup_on_stack(&timeout.timer, lld_timedout, 0);
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci		timeout.timer.expires	= jiffies + adp->timeout * HZ;
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci		add_timer(&timeout.timer);
69862306a36Sopenharmony_ci	}
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	/*
70162306a36Sopenharmony_ci	 * Wait till the low level driver completes the ioctl. After this
70262306a36Sopenharmony_ci	 * call, the ioctl either completed successfully or timedout.
70362306a36Sopenharmony_ci	 */
70462306a36Sopenharmony_ci	wait_event(wait_q, (kioc->status != -ENODATA));
70562306a36Sopenharmony_ci	if (timeout.timer.function) {
70662306a36Sopenharmony_ci		del_timer_sync(&timeout.timer);
70762306a36Sopenharmony_ci		destroy_timer_on_stack(&timeout.timer);
70862306a36Sopenharmony_ci	}
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	/*
71162306a36Sopenharmony_ci	 * If the command had timedout, we mark the controller offline
71262306a36Sopenharmony_ci	 * before returning
71362306a36Sopenharmony_ci	 */
71462306a36Sopenharmony_ci	if (kioc->timedout) {
71562306a36Sopenharmony_ci		adp->quiescent = 0;
71662306a36Sopenharmony_ci	}
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	return kioc->status;
71962306a36Sopenharmony_ci}
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci/**
72362306a36Sopenharmony_ci * ioctl_done - callback from the low level driver
72462306a36Sopenharmony_ci * @kioc	: completed ioctl packet
72562306a36Sopenharmony_ci */
72662306a36Sopenharmony_cistatic void
72762306a36Sopenharmony_ciioctl_done(uioc_t *kioc)
72862306a36Sopenharmony_ci{
72962306a36Sopenharmony_ci	uint32_t	adapno;
73062306a36Sopenharmony_ci	int		iterator;
73162306a36Sopenharmony_ci	mraid_mmadp_t*	adapter;
73262306a36Sopenharmony_ci	bool		is_found;
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	/*
73562306a36Sopenharmony_ci	 * When the kioc returns from driver, make sure it still doesn't
73662306a36Sopenharmony_ci	 * have ENODATA in status. Otherwise, driver will hang on wait_event
73762306a36Sopenharmony_ci	 * forever
73862306a36Sopenharmony_ci	 */
73962306a36Sopenharmony_ci	if (kioc->status == -ENODATA) {
74062306a36Sopenharmony_ci		con_log(CL_ANN, (KERN_WARNING
74162306a36Sopenharmony_ci			"megaraid cmm: lld didn't change status!\n"));
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci		kioc->status = -EINVAL;
74462306a36Sopenharmony_ci	}
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	/*
74762306a36Sopenharmony_ci	 * Check if this kioc was timedout before. If so, nobody is waiting
74862306a36Sopenharmony_ci	 * on this kioc. We don't have to wake up anybody. Instead, we just
74962306a36Sopenharmony_ci	 * have to free the kioc
75062306a36Sopenharmony_ci	 */
75162306a36Sopenharmony_ci	if (kioc->timedout) {
75262306a36Sopenharmony_ci		iterator	= 0;
75362306a36Sopenharmony_ci		adapter		= NULL;
75462306a36Sopenharmony_ci		adapno		= kioc->adapno;
75562306a36Sopenharmony_ci		is_found	= false;
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci		con_log(CL_ANN, ( KERN_WARNING "megaraid cmm: completed "
75862306a36Sopenharmony_ci					"ioctl that was timedout before\n"));
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci		list_for_each_entry(adapter, &adapters_list_g, list) {
76162306a36Sopenharmony_ci			if (iterator++ == adapno) {
76262306a36Sopenharmony_ci				is_found = true;
76362306a36Sopenharmony_ci				break;
76462306a36Sopenharmony_ci			}
76562306a36Sopenharmony_ci		}
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci		kioc->timedout = 0;
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci		if (is_found)
77062306a36Sopenharmony_ci			mraid_mm_dealloc_kioc( adapter, kioc );
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	}
77362306a36Sopenharmony_ci	else {
77462306a36Sopenharmony_ci		wake_up(&wait_q);
77562306a36Sopenharmony_ci	}
77662306a36Sopenharmony_ci}
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci/**
78062306a36Sopenharmony_ci * lld_timedout	- callback from the expired timer
78162306a36Sopenharmony_ci * @t		: timer that timed out
78262306a36Sopenharmony_ci */
78362306a36Sopenharmony_cistatic void
78462306a36Sopenharmony_cilld_timedout(struct timer_list *t)
78562306a36Sopenharmony_ci{
78662306a36Sopenharmony_ci	struct uioc_timeout *timeout = from_timer(timeout, t, timer);
78762306a36Sopenharmony_ci	uioc_t *kioc	= timeout->uioc;
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	kioc->status 	= -ETIME;
79062306a36Sopenharmony_ci	kioc->timedout	= 1;
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	con_log(CL_ANN, (KERN_WARNING "megaraid cmm: ioctl timed out\n"));
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	wake_up(&wait_q);
79562306a36Sopenharmony_ci}
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci/**
79962306a36Sopenharmony_ci * kioc_to_mimd	- Converter from new back to old format
80062306a36Sopenharmony_ci * @kioc	: Kernel space IOCTL packet (successfully issued)
80162306a36Sopenharmony_ci * @mimd	: User space MIMD packet
80262306a36Sopenharmony_ci */
80362306a36Sopenharmony_cistatic int
80462306a36Sopenharmony_cikioc_to_mimd(uioc_t *kioc, mimd_t __user *mimd)
80562306a36Sopenharmony_ci{
80662306a36Sopenharmony_ci	mimd_t			kmimd;
80762306a36Sopenharmony_ci	uint8_t			opcode;
80862306a36Sopenharmony_ci	uint8_t			subopcode;
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	mbox64_t		*mbox64;
81162306a36Sopenharmony_ci	mraid_passthru_t	__user *upthru32;
81262306a36Sopenharmony_ci	mraid_passthru_t	*kpthru32;
81362306a36Sopenharmony_ci	mcontroller_t		cinfo;
81462306a36Sopenharmony_ci	mraid_hba_info_t	*hinfo;
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	if (copy_from_user(&kmimd, mimd, sizeof(mimd_t)))
81862306a36Sopenharmony_ci		return (-EFAULT);
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	opcode		= kmimd.ui.fcs.opcode;
82162306a36Sopenharmony_ci	subopcode	= kmimd.ui.fcs.subopcode;
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	if (opcode == 0x82) {
82462306a36Sopenharmony_ci		switch (subopcode) {
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci		case MEGAIOC_QADAPINFO:
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci			hinfo = (mraid_hba_info_t *)(unsigned long)
82962306a36Sopenharmony_ci					kioc->buf_vaddr;
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci			hinfo_to_cinfo(hinfo, &cinfo);
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci			if (copy_to_user(kmimd.data, &cinfo, sizeof(cinfo)))
83462306a36Sopenharmony_ci				return (-EFAULT);
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci			return 0;
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci		default:
83962306a36Sopenharmony_ci			return (-EINVAL);
84062306a36Sopenharmony_ci		}
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci		return 0;
84362306a36Sopenharmony_ci	}
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci	mbox64 = (mbox64_t *)(unsigned long)kioc->cmdbuf;
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci	if (kioc->user_pthru) {
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci		upthru32 = kioc->user_pthru;
85062306a36Sopenharmony_ci		kpthru32 = kioc->pthru32;
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci		if (copy_to_user(&upthru32->scsistatus,
85362306a36Sopenharmony_ci					&kpthru32->scsistatus,
85462306a36Sopenharmony_ci					sizeof(uint8_t))) {
85562306a36Sopenharmony_ci			return (-EFAULT);
85662306a36Sopenharmony_ci		}
85762306a36Sopenharmony_ci	}
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	if (kioc->user_data) {
86062306a36Sopenharmony_ci		if (copy_to_user(kioc->user_data, kioc->buf_vaddr,
86162306a36Sopenharmony_ci					kioc->user_data_len)) {
86262306a36Sopenharmony_ci			return (-EFAULT);
86362306a36Sopenharmony_ci		}
86462306a36Sopenharmony_ci	}
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	if (copy_to_user(&mimd->mbox[17],
86762306a36Sopenharmony_ci			&mbox64->mbox32.status, sizeof(uint8_t))) {
86862306a36Sopenharmony_ci		return (-EFAULT);
86962306a36Sopenharmony_ci	}
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	return 0;
87262306a36Sopenharmony_ci}
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci/**
87662306a36Sopenharmony_ci * hinfo_to_cinfo - Convert new format hba info into old format
87762306a36Sopenharmony_ci * @hinfo	: New format, more comprehensive adapter info
87862306a36Sopenharmony_ci * @cinfo	: Old format adapter info to support mimd_t apps
87962306a36Sopenharmony_ci */
88062306a36Sopenharmony_cistatic void
88162306a36Sopenharmony_cihinfo_to_cinfo(mraid_hba_info_t *hinfo, mcontroller_t *cinfo)
88262306a36Sopenharmony_ci{
88362306a36Sopenharmony_ci	if (!hinfo || !cinfo)
88462306a36Sopenharmony_ci		return;
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	cinfo->base		= hinfo->baseport;
88762306a36Sopenharmony_ci	cinfo->irq		= hinfo->irq;
88862306a36Sopenharmony_ci	cinfo->numldrv		= hinfo->num_ldrv;
88962306a36Sopenharmony_ci	cinfo->pcibus		= hinfo->pci_bus;
89062306a36Sopenharmony_ci	cinfo->pcidev		= hinfo->pci_slot;
89162306a36Sopenharmony_ci	cinfo->pcifun		= PCI_FUNC(hinfo->pci_dev_fn);
89262306a36Sopenharmony_ci	cinfo->pciid		= hinfo->pci_device_id;
89362306a36Sopenharmony_ci	cinfo->pcivendor	= hinfo->pci_vendor_id;
89462306a36Sopenharmony_ci	cinfo->pcislot		= hinfo->pci_slot;
89562306a36Sopenharmony_ci	cinfo->uid		= hinfo->unique_id;
89662306a36Sopenharmony_ci}
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci/**
90062306a36Sopenharmony_ci * mraid_mm_register_adp - Registration routine for low level drivers
90162306a36Sopenharmony_ci * @lld_adp	: Adapter object
90262306a36Sopenharmony_ci */
90362306a36Sopenharmony_ciint
90462306a36Sopenharmony_cimraid_mm_register_adp(mraid_mmadp_t *lld_adp)
90562306a36Sopenharmony_ci{
90662306a36Sopenharmony_ci	mraid_mmadp_t	*adapter;
90762306a36Sopenharmony_ci	mbox64_t	*mbox_list;
90862306a36Sopenharmony_ci	uioc_t		*kioc;
90962306a36Sopenharmony_ci	uint32_t	rval;
91062306a36Sopenharmony_ci	int		i;
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	if (lld_adp->drvr_type != DRVRTYPE_MBOX)
91462306a36Sopenharmony_ci		return (-EINVAL);
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	adapter = kzalloc(sizeof(mraid_mmadp_t), GFP_KERNEL);
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	if (!adapter)
91962306a36Sopenharmony_ci		return -ENOMEM;
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci	adapter->unique_id	= lld_adp->unique_id;
92362306a36Sopenharmony_ci	adapter->drvr_type	= lld_adp->drvr_type;
92462306a36Sopenharmony_ci	adapter->drvr_data	= lld_adp->drvr_data;
92562306a36Sopenharmony_ci	adapter->pdev		= lld_adp->pdev;
92662306a36Sopenharmony_ci	adapter->issue_uioc	= lld_adp->issue_uioc;
92762306a36Sopenharmony_ci	adapter->timeout	= lld_adp->timeout;
92862306a36Sopenharmony_ci	adapter->max_kioc	= lld_adp->max_kioc;
92962306a36Sopenharmony_ci	adapter->quiescent	= 1;
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	/*
93262306a36Sopenharmony_ci	 * Allocate single blocks of memory for all required kiocs,
93362306a36Sopenharmony_ci	 * mailboxes and passthru structures.
93462306a36Sopenharmony_ci	 */
93562306a36Sopenharmony_ci	adapter->kioc_list	= kmalloc_array(lld_adp->max_kioc,
93662306a36Sopenharmony_ci						  sizeof(uioc_t),
93762306a36Sopenharmony_ci						  GFP_KERNEL);
93862306a36Sopenharmony_ci	adapter->mbox_list	= kmalloc_array(lld_adp->max_kioc,
93962306a36Sopenharmony_ci						  sizeof(mbox64_t),
94062306a36Sopenharmony_ci						  GFP_KERNEL);
94162306a36Sopenharmony_ci	adapter->pthru_dma_pool = dma_pool_create("megaraid mm pthru pool",
94262306a36Sopenharmony_ci						&adapter->pdev->dev,
94362306a36Sopenharmony_ci						sizeof(mraid_passthru_t),
94462306a36Sopenharmony_ci						16, 0);
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	if (!adapter->kioc_list || !adapter->mbox_list ||
94762306a36Sopenharmony_ci			!adapter->pthru_dma_pool) {
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci		con_log(CL_ANN, (KERN_WARNING
95062306a36Sopenharmony_ci			"megaraid cmm: out of memory, %s %d\n", __func__,
95162306a36Sopenharmony_ci			__LINE__));
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci		rval = (-ENOMEM);
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci		goto memalloc_error;
95662306a36Sopenharmony_ci	}
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	/*
95962306a36Sopenharmony_ci	 * Slice kioc_list and make a kioc_pool with the individiual kiocs
96062306a36Sopenharmony_ci	 */
96162306a36Sopenharmony_ci	INIT_LIST_HEAD(&adapter->kioc_pool);
96262306a36Sopenharmony_ci	spin_lock_init(&adapter->kioc_pool_lock);
96362306a36Sopenharmony_ci	sema_init(&adapter->kioc_semaphore, lld_adp->max_kioc);
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	mbox_list	= (mbox64_t *)adapter->mbox_list;
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	for (i = 0; i < lld_adp->max_kioc; i++) {
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci		kioc		= adapter->kioc_list + i;
97062306a36Sopenharmony_ci		kioc->cmdbuf	= (uint64_t)(unsigned long)(mbox_list + i);
97162306a36Sopenharmony_ci		kioc->pthru32	= dma_pool_alloc(adapter->pthru_dma_pool,
97262306a36Sopenharmony_ci						GFP_KERNEL, &kioc->pthru32_h);
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci		if (!kioc->pthru32) {
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci			con_log(CL_ANN, (KERN_WARNING
97762306a36Sopenharmony_ci				"megaraid cmm: out of memory, %s %d\n",
97862306a36Sopenharmony_ci					__func__, __LINE__));
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci			rval = (-ENOMEM);
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci			goto pthru_dma_pool_error;
98362306a36Sopenharmony_ci		}
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci		list_add_tail(&kioc->list, &adapter->kioc_pool);
98662306a36Sopenharmony_ci	}
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	// Setup the dma pools for data buffers
98962306a36Sopenharmony_ci	if ((rval = mraid_mm_setup_dma_pools(adapter)) != 0) {
99062306a36Sopenharmony_ci		goto dma_pool_error;
99162306a36Sopenharmony_ci	}
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	list_add_tail(&adapter->list, &adapters_list_g);
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	adapters_count_g++;
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci	return 0;
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_cidma_pool_error:
100062306a36Sopenharmony_ci	/* Do nothing */
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_cipthru_dma_pool_error:
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci	for (i = 0; i < lld_adp->max_kioc; i++) {
100562306a36Sopenharmony_ci		kioc = adapter->kioc_list + i;
100662306a36Sopenharmony_ci		if (kioc->pthru32) {
100762306a36Sopenharmony_ci			dma_pool_free(adapter->pthru_dma_pool, kioc->pthru32,
100862306a36Sopenharmony_ci				kioc->pthru32_h);
100962306a36Sopenharmony_ci		}
101062306a36Sopenharmony_ci	}
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_cimemalloc_error:
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	kfree(adapter->kioc_list);
101562306a36Sopenharmony_ci	kfree(adapter->mbox_list);
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci	dma_pool_destroy(adapter->pthru_dma_pool);
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci	kfree(adapter);
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci	return rval;
102262306a36Sopenharmony_ci}
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci/**
102662306a36Sopenharmony_ci * mraid_mm_adapter_app_handle - return the application handle for this adapter
102762306a36Sopenharmony_ci * @unique_id	: adapter unique identifier
102862306a36Sopenharmony_ci *
102962306a36Sopenharmony_ci * For the given driver data, locate the adapter in our global list and
103062306a36Sopenharmony_ci * return the corresponding handle, which is also used by applications to
103162306a36Sopenharmony_ci * uniquely identify an adapter.
103262306a36Sopenharmony_ci *
103362306a36Sopenharmony_ci * Return adapter handle if found in the list.
103462306a36Sopenharmony_ci * Return 0 if adapter could not be located, should never happen though.
103562306a36Sopenharmony_ci */
103662306a36Sopenharmony_ciuint32_t
103762306a36Sopenharmony_cimraid_mm_adapter_app_handle(uint32_t unique_id)
103862306a36Sopenharmony_ci{
103962306a36Sopenharmony_ci	mraid_mmadp_t	*adapter;
104062306a36Sopenharmony_ci	mraid_mmadp_t	*tmp;
104162306a36Sopenharmony_ci	int		index = 0;
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci	list_for_each_entry_safe(adapter, tmp, &adapters_list_g, list) {
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci		if (adapter->unique_id == unique_id) {
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci			return MKADAP(index);
104862306a36Sopenharmony_ci		}
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci		index++;
105162306a36Sopenharmony_ci	}
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci	return 0;
105462306a36Sopenharmony_ci}
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci/**
105862306a36Sopenharmony_ci * mraid_mm_setup_dma_pools - Set up dma buffer pools per adapter
105962306a36Sopenharmony_ci * @adp	: Adapter softstate
106062306a36Sopenharmony_ci *
106162306a36Sopenharmony_ci * We maintain a pool of dma buffers per each adapter. Each pool has one
106262306a36Sopenharmony_ci * buffer. E.g, we may have 5 dma pools - one each for 4k, 8k ... 64k buffers.
106362306a36Sopenharmony_ci * We have just one 4k buffer in 4k pool, one 8k buffer in 8k pool etc. We
106462306a36Sopenharmony_ci * dont' want to waste too much memory by allocating more buffers per each
106562306a36Sopenharmony_ci * pool.
106662306a36Sopenharmony_ci */
106762306a36Sopenharmony_cistatic int
106862306a36Sopenharmony_cimraid_mm_setup_dma_pools(mraid_mmadp_t *adp)
106962306a36Sopenharmony_ci{
107062306a36Sopenharmony_ci	mm_dmapool_t	*pool;
107162306a36Sopenharmony_ci	int		bufsize;
107262306a36Sopenharmony_ci	int		i;
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci	/*
107562306a36Sopenharmony_ci	 * Create MAX_DMA_POOLS number of pools
107662306a36Sopenharmony_ci	 */
107762306a36Sopenharmony_ci	bufsize = MRAID_MM_INIT_BUFF_SIZE;
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	for (i = 0; i < MAX_DMA_POOLS; i++){
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci		pool = &adp->dma_pool_list[i];
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci		pool->buf_size = bufsize;
108462306a36Sopenharmony_ci		spin_lock_init(&pool->lock);
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci		pool->handle = dma_pool_create("megaraid mm data buffer",
108762306a36Sopenharmony_ci						&adp->pdev->dev, bufsize,
108862306a36Sopenharmony_ci						16, 0);
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci		if (!pool->handle) {
109162306a36Sopenharmony_ci			goto dma_pool_setup_error;
109262306a36Sopenharmony_ci		}
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci		pool->vaddr = dma_pool_alloc(pool->handle, GFP_KERNEL,
109562306a36Sopenharmony_ci							&pool->paddr);
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci		if (!pool->vaddr)
109862306a36Sopenharmony_ci			goto dma_pool_setup_error;
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci		bufsize = bufsize * 2;
110162306a36Sopenharmony_ci	}
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_ci	return 0;
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_cidma_pool_setup_error:
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci	mraid_mm_teardown_dma_pools(adp);
110862306a36Sopenharmony_ci	return (-ENOMEM);
110962306a36Sopenharmony_ci}
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci/**
111362306a36Sopenharmony_ci * mraid_mm_unregister_adp - Unregister routine for low level drivers
111462306a36Sopenharmony_ci * @unique_id	: UID of the adpater
111562306a36Sopenharmony_ci *
111662306a36Sopenharmony_ci * Assumes no outstanding ioctls to llds.
111762306a36Sopenharmony_ci */
111862306a36Sopenharmony_ciint
111962306a36Sopenharmony_cimraid_mm_unregister_adp(uint32_t unique_id)
112062306a36Sopenharmony_ci{
112162306a36Sopenharmony_ci	mraid_mmadp_t	*adapter;
112262306a36Sopenharmony_ci	mraid_mmadp_t	*tmp;
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci	list_for_each_entry_safe(adapter, tmp, &adapters_list_g, list) {
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci		if (adapter->unique_id == unique_id) {
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci			adapters_count_g--;
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci			list_del_init(&adapter->list);
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci			mraid_mm_free_adp_resources(adapter);
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci			kfree(adapter);
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci			con_log(CL_ANN, (
113862306a36Sopenharmony_ci				"megaraid cmm: Unregistered one adapter:%#x\n",
113962306a36Sopenharmony_ci				unique_id));
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci			return 0;
114262306a36Sopenharmony_ci		}
114362306a36Sopenharmony_ci	}
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci	return (-ENODEV);
114662306a36Sopenharmony_ci}
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci/**
114962306a36Sopenharmony_ci * mraid_mm_free_adp_resources - Free adapter softstate
115062306a36Sopenharmony_ci * @adp	: Adapter softstate
115162306a36Sopenharmony_ci */
115262306a36Sopenharmony_cistatic void
115362306a36Sopenharmony_cimraid_mm_free_adp_resources(mraid_mmadp_t *adp)
115462306a36Sopenharmony_ci{
115562306a36Sopenharmony_ci	uioc_t	*kioc;
115662306a36Sopenharmony_ci	int	i;
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	mraid_mm_teardown_dma_pools(adp);
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci	for (i = 0; i < adp->max_kioc; i++) {
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_ci		kioc = adp->kioc_list + i;
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci		dma_pool_free(adp->pthru_dma_pool, kioc->pthru32,
116562306a36Sopenharmony_ci				kioc->pthru32_h);
116662306a36Sopenharmony_ci	}
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci	kfree(adp->kioc_list);
116962306a36Sopenharmony_ci	kfree(adp->mbox_list);
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci	dma_pool_destroy(adp->pthru_dma_pool);
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci	return;
117562306a36Sopenharmony_ci}
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci/**
117962306a36Sopenharmony_ci * mraid_mm_teardown_dma_pools - Free all per adapter dma buffers
118062306a36Sopenharmony_ci * @adp	: Adapter softstate
118162306a36Sopenharmony_ci */
118262306a36Sopenharmony_cistatic void
118362306a36Sopenharmony_cimraid_mm_teardown_dma_pools(mraid_mmadp_t *adp)
118462306a36Sopenharmony_ci{
118562306a36Sopenharmony_ci	int		i;
118662306a36Sopenharmony_ci	mm_dmapool_t	*pool;
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci	for (i = 0; i < MAX_DMA_POOLS; i++) {
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci		pool = &adp->dma_pool_list[i];
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ci		if (pool->handle) {
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci			if (pool->vaddr)
119562306a36Sopenharmony_ci				dma_pool_free(pool->handle, pool->vaddr,
119662306a36Sopenharmony_ci							pool->paddr);
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci			dma_pool_destroy(pool->handle);
119962306a36Sopenharmony_ci			pool->handle = NULL;
120062306a36Sopenharmony_ci		}
120162306a36Sopenharmony_ci	}
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_ci	return;
120462306a36Sopenharmony_ci}
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci/**
120762306a36Sopenharmony_ci * mraid_mm_init	- Module entry point
120862306a36Sopenharmony_ci */
120962306a36Sopenharmony_cistatic int __init
121062306a36Sopenharmony_cimraid_mm_init(void)
121162306a36Sopenharmony_ci{
121262306a36Sopenharmony_ci	int err;
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci	// Announce the driver version
121562306a36Sopenharmony_ci	con_log(CL_ANN, (KERN_INFO "megaraid cmm: %s %s\n",
121662306a36Sopenharmony_ci		LSI_COMMON_MOD_VERSION, LSI_COMMON_MOD_EXT_VERSION));
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ci	err = misc_register(&megaraid_mm_dev);
121962306a36Sopenharmony_ci	if (err < 0) {
122062306a36Sopenharmony_ci		con_log(CL_ANN, ("megaraid cmm: cannot register misc device\n"));
122162306a36Sopenharmony_ci		return err;
122262306a36Sopenharmony_ci	}
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci	init_waitqueue_head(&wait_q);
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_ci	INIT_LIST_HEAD(&adapters_list_g);
122762306a36Sopenharmony_ci
122862306a36Sopenharmony_ci	return 0;
122962306a36Sopenharmony_ci}
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci/**
123362306a36Sopenharmony_ci * mraid_mm_exit	- Module exit point
123462306a36Sopenharmony_ci */
123562306a36Sopenharmony_cistatic void __exit
123662306a36Sopenharmony_cimraid_mm_exit(void)
123762306a36Sopenharmony_ci{
123862306a36Sopenharmony_ci	con_log(CL_DLEVEL1 , ("exiting common mod\n"));
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_ci	misc_deregister(&megaraid_mm_dev);
124162306a36Sopenharmony_ci}
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_cimodule_init(mraid_mm_init);
124462306a36Sopenharmony_cimodule_exit(mraid_mm_exit);
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_ci/* vi: set ts=8 sw=8 tw=78: */
1247