18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci *			Linux MegaRAID device driver
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright (c) 2003-2004  LSI Logic Corporation.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * FILE		: megaraid_mm.c
98c2ecf20Sopenharmony_ci * Version	: v2.20.2.7 (Jul 16 2006)
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * Common management module
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ci#include <linux/sched.h>
148c2ecf20Sopenharmony_ci#include <linux/slab.h>
158c2ecf20Sopenharmony_ci#include <linux/mutex.h>
168c2ecf20Sopenharmony_ci#include "megaraid_mm.h"
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci// Entry points for char node driver
208c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(mraid_mm_mutex);
218c2ecf20Sopenharmony_cistatic int mraid_mm_open(struct inode *, struct file *);
228c2ecf20Sopenharmony_cistatic long mraid_mm_unlocked_ioctl(struct file *, uint, unsigned long);
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci// routines to convert to and from the old the format
268c2ecf20Sopenharmony_cistatic int mimd_to_kioc(mimd_t __user *, mraid_mmadp_t *, uioc_t *);
278c2ecf20Sopenharmony_cistatic int kioc_to_mimd(uioc_t *, mimd_t __user *);
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci// Helper functions
318c2ecf20Sopenharmony_cistatic int handle_drvrcmd(void __user *, uint8_t, int *);
328c2ecf20Sopenharmony_cistatic int lld_ioctl(mraid_mmadp_t *, uioc_t *);
338c2ecf20Sopenharmony_cistatic void ioctl_done(uioc_t *);
348c2ecf20Sopenharmony_cistatic void lld_timedout(struct timer_list *);
358c2ecf20Sopenharmony_cistatic void hinfo_to_cinfo(mraid_hba_info_t *, mcontroller_t *);
368c2ecf20Sopenharmony_cistatic mraid_mmadp_t *mraid_mm_get_adapter(mimd_t __user *, int *);
378c2ecf20Sopenharmony_cistatic uioc_t *mraid_mm_alloc_kioc(mraid_mmadp_t *);
388c2ecf20Sopenharmony_cistatic void mraid_mm_dealloc_kioc(mraid_mmadp_t *, uioc_t *);
398c2ecf20Sopenharmony_cistatic int mraid_mm_attach_buf(mraid_mmadp_t *, uioc_t *, int);
408c2ecf20Sopenharmony_cistatic int mraid_mm_setup_dma_pools(mraid_mmadp_t *);
418c2ecf20Sopenharmony_cistatic void mraid_mm_free_adp_resources(mraid_mmadp_t *);
428c2ecf20Sopenharmony_cistatic void mraid_mm_teardown_dma_pools(mraid_mmadp_t *);
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ciMODULE_AUTHOR("LSI Logic Corporation");
458c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("LSI Logic Management Module");
468c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
478c2ecf20Sopenharmony_ciMODULE_VERSION(LSI_COMMON_MOD_VERSION);
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistatic int dbglevel = CL_ANN;
508c2ecf20Sopenharmony_cimodule_param_named(dlevel, dbglevel, int, 0);
518c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dlevel, "Debug level (default=0)");
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mraid_mm_register_adp);
548c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mraid_mm_unregister_adp);
558c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mraid_mm_adapter_app_handle);
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic uint32_t drvr_ver	= 0x02200207;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic int adapters_count_g;
608c2ecf20Sopenharmony_cistatic struct list_head adapters_list_g;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistatic wait_queue_head_t wait_q;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic const struct file_operations lsi_fops = {
658c2ecf20Sopenharmony_ci	.open	= mraid_mm_open,
668c2ecf20Sopenharmony_ci	.unlocked_ioctl = mraid_mm_unlocked_ioctl,
678c2ecf20Sopenharmony_ci	.compat_ioctl = compat_ptr_ioctl,
688c2ecf20Sopenharmony_ci	.owner	= THIS_MODULE,
698c2ecf20Sopenharmony_ci	.llseek = noop_llseek,
708c2ecf20Sopenharmony_ci};
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic struct miscdevice megaraid_mm_dev = {
738c2ecf20Sopenharmony_ci	.minor	= MISC_DYNAMIC_MINOR,
748c2ecf20Sopenharmony_ci	.name   = "megadev0",
758c2ecf20Sopenharmony_ci	.fops   = &lsi_fops,
768c2ecf20Sopenharmony_ci};
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci/**
798c2ecf20Sopenharmony_ci * mraid_mm_open - open routine for char node interface
808c2ecf20Sopenharmony_ci * @inode	: unused
818c2ecf20Sopenharmony_ci * @filep	: unused
828c2ecf20Sopenharmony_ci *
838c2ecf20Sopenharmony_ci * Allow ioctl operations by apps only if they have superuser privilege.
848c2ecf20Sopenharmony_ci */
858c2ecf20Sopenharmony_cistatic int
868c2ecf20Sopenharmony_cimraid_mm_open(struct inode *inode, struct file *filep)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	/*
898c2ecf20Sopenharmony_ci	 * Only allow superuser to access private ioctl interface
908c2ecf20Sopenharmony_ci	 */
918c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN)) return (-EACCES);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	return 0;
948c2ecf20Sopenharmony_ci}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci/**
978c2ecf20Sopenharmony_ci * mraid_mm_ioctl - module entry-point for ioctls
988c2ecf20Sopenharmony_ci * @filep	: file operations pointer (ignored)
998c2ecf20Sopenharmony_ci * @cmd		: ioctl command
1008c2ecf20Sopenharmony_ci * @arg		: user ioctl packet
1018c2ecf20Sopenharmony_ci */
1028c2ecf20Sopenharmony_cistatic int
1038c2ecf20Sopenharmony_cimraid_mm_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	uioc_t		*kioc;
1068c2ecf20Sopenharmony_ci	char		signature[EXT_IOCTL_SIGN_SZ]	= {0};
1078c2ecf20Sopenharmony_ci	int		rval;
1088c2ecf20Sopenharmony_ci	mraid_mmadp_t	*adp;
1098c2ecf20Sopenharmony_ci	uint8_t		old_ioctl;
1108c2ecf20Sopenharmony_ci	int		drvrcmd_rval;
1118c2ecf20Sopenharmony_ci	void __user *argp = (void __user *)arg;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	/*
1148c2ecf20Sopenharmony_ci	 * Make sure only USCSICMD are issued through this interface.
1158c2ecf20Sopenharmony_ci	 * MIMD application would still fire different command.
1168c2ecf20Sopenharmony_ci	 */
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	if ((_IOC_TYPE(cmd) != MEGAIOC_MAGIC) && (cmd != USCSICMD)) {
1198c2ecf20Sopenharmony_ci		return (-EINVAL);
1208c2ecf20Sopenharmony_ci	}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	/*
1238c2ecf20Sopenharmony_ci	 * Look for signature to see if this is the new or old ioctl format.
1248c2ecf20Sopenharmony_ci	 */
1258c2ecf20Sopenharmony_ci	if (copy_from_user(signature, argp, EXT_IOCTL_SIGN_SZ)) {
1268c2ecf20Sopenharmony_ci		con_log(CL_ANN, (KERN_WARNING
1278c2ecf20Sopenharmony_ci			"megaraid cmm: copy from usr addr failed\n"));
1288c2ecf20Sopenharmony_ci		return (-EFAULT);
1298c2ecf20Sopenharmony_ci	}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	if (memcmp(signature, EXT_IOCTL_SIGN, EXT_IOCTL_SIGN_SZ) == 0)
1328c2ecf20Sopenharmony_ci		old_ioctl = 0;
1338c2ecf20Sopenharmony_ci	else
1348c2ecf20Sopenharmony_ci		old_ioctl = 1;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	/*
1378c2ecf20Sopenharmony_ci	 * At present, we don't support the new ioctl packet
1388c2ecf20Sopenharmony_ci	 */
1398c2ecf20Sopenharmony_ci	if (!old_ioctl )
1408c2ecf20Sopenharmony_ci		return (-EINVAL);
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	/*
1438c2ecf20Sopenharmony_ci	 * If it is a driver ioctl (as opposed to fw ioctls), then we can
1448c2ecf20Sopenharmony_ci	 * handle the command locally. rval > 0 means it is not a drvr cmd
1458c2ecf20Sopenharmony_ci	 */
1468c2ecf20Sopenharmony_ci	rval = handle_drvrcmd(argp, old_ioctl, &drvrcmd_rval);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	if (rval < 0)
1498c2ecf20Sopenharmony_ci		return rval;
1508c2ecf20Sopenharmony_ci	else if (rval == 0)
1518c2ecf20Sopenharmony_ci		return drvrcmd_rval;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	rval = 0;
1548c2ecf20Sopenharmony_ci	if ((adp = mraid_mm_get_adapter(argp, &rval)) == NULL) {
1558c2ecf20Sopenharmony_ci		return rval;
1568c2ecf20Sopenharmony_ci	}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	/*
1598c2ecf20Sopenharmony_ci	 * Check if adapter can accept ioctl. We may have marked it offline
1608c2ecf20Sopenharmony_ci	 * if any previous kioc had timedout on this controller.
1618c2ecf20Sopenharmony_ci	 */
1628c2ecf20Sopenharmony_ci	if (!adp->quiescent) {
1638c2ecf20Sopenharmony_ci		con_log(CL_ANN, (KERN_WARNING
1648c2ecf20Sopenharmony_ci			"megaraid cmm: controller cannot accept cmds due to "
1658c2ecf20Sopenharmony_ci			"earlier errors\n" ));
1668c2ecf20Sopenharmony_ci		return -EFAULT;
1678c2ecf20Sopenharmony_ci	}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	/*
1708c2ecf20Sopenharmony_ci	 * The following call will block till a kioc is available
1718c2ecf20Sopenharmony_ci	 * or return NULL if the list head is empty for the pointer
1728c2ecf20Sopenharmony_ci	 * of type mraid_mmapt passed to mraid_mm_alloc_kioc
1738c2ecf20Sopenharmony_ci	 */
1748c2ecf20Sopenharmony_ci	kioc = mraid_mm_alloc_kioc(adp);
1758c2ecf20Sopenharmony_ci	if (!kioc)
1768c2ecf20Sopenharmony_ci		return -ENXIO;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	/*
1798c2ecf20Sopenharmony_ci	 * User sent the old mimd_t ioctl packet. Convert it to uioc_t.
1808c2ecf20Sopenharmony_ci	 */
1818c2ecf20Sopenharmony_ci	if ((rval = mimd_to_kioc(argp, adp, kioc))) {
1828c2ecf20Sopenharmony_ci		mraid_mm_dealloc_kioc(adp, kioc);
1838c2ecf20Sopenharmony_ci		return rval;
1848c2ecf20Sopenharmony_ci	}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	kioc->done = ioctl_done;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	/*
1898c2ecf20Sopenharmony_ci	 * Issue the IOCTL to the low level driver. After the IOCTL completes
1908c2ecf20Sopenharmony_ci	 * release the kioc if and only if it was _not_ timedout. If it was
1918c2ecf20Sopenharmony_ci	 * timedout, that means that resources are still with low level driver.
1928c2ecf20Sopenharmony_ci	 */
1938c2ecf20Sopenharmony_ci	if ((rval = lld_ioctl(adp, kioc))) {
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci		if (!kioc->timedout)
1968c2ecf20Sopenharmony_ci			mraid_mm_dealloc_kioc(adp, kioc);
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci		return rval;
1998c2ecf20Sopenharmony_ci	}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	/*
2028c2ecf20Sopenharmony_ci	 * Convert the kioc back to user space
2038c2ecf20Sopenharmony_ci	 */
2048c2ecf20Sopenharmony_ci	rval = kioc_to_mimd(kioc, argp);
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	/*
2078c2ecf20Sopenharmony_ci	 * Return the kioc to free pool
2088c2ecf20Sopenharmony_ci	 */
2098c2ecf20Sopenharmony_ci	mraid_mm_dealloc_kioc(adp, kioc);
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	return rval;
2128c2ecf20Sopenharmony_ci}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_cistatic long
2158c2ecf20Sopenharmony_cimraid_mm_unlocked_ioctl(struct file *filep, unsigned int cmd,
2168c2ecf20Sopenharmony_ci		        unsigned long arg)
2178c2ecf20Sopenharmony_ci{
2188c2ecf20Sopenharmony_ci	int err;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	mutex_lock(&mraid_mm_mutex);
2218c2ecf20Sopenharmony_ci	err = mraid_mm_ioctl(filep, cmd, arg);
2228c2ecf20Sopenharmony_ci	mutex_unlock(&mraid_mm_mutex);
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	return err;
2258c2ecf20Sopenharmony_ci}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci/**
2288c2ecf20Sopenharmony_ci * mraid_mm_get_adapter - Returns corresponding adapters for the mimd packet
2298c2ecf20Sopenharmony_ci * @umimd	: User space mimd_t ioctl packet
2308c2ecf20Sopenharmony_ci * @rval	: returned success/error status
2318c2ecf20Sopenharmony_ci *
2328c2ecf20Sopenharmony_ci * The function return value is a pointer to the located @adapter.
2338c2ecf20Sopenharmony_ci */
2348c2ecf20Sopenharmony_cistatic mraid_mmadp_t *
2358c2ecf20Sopenharmony_cimraid_mm_get_adapter(mimd_t __user *umimd, int *rval)
2368c2ecf20Sopenharmony_ci{
2378c2ecf20Sopenharmony_ci	mraid_mmadp_t	*adapter;
2388c2ecf20Sopenharmony_ci	mimd_t		mimd;
2398c2ecf20Sopenharmony_ci	uint32_t	adapno;
2408c2ecf20Sopenharmony_ci	int		iterator;
2418c2ecf20Sopenharmony_ci	bool		is_found;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	if (copy_from_user(&mimd, umimd, sizeof(mimd_t))) {
2448c2ecf20Sopenharmony_ci		*rval = -EFAULT;
2458c2ecf20Sopenharmony_ci		return NULL;
2468c2ecf20Sopenharmony_ci	}
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	adapno = GETADAP(mimd.ui.fcs.adapno);
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	if (adapno >= adapters_count_g) {
2518c2ecf20Sopenharmony_ci		*rval = -ENODEV;
2528c2ecf20Sopenharmony_ci		return NULL;
2538c2ecf20Sopenharmony_ci	}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	adapter = NULL;
2568c2ecf20Sopenharmony_ci	iterator = 0;
2578c2ecf20Sopenharmony_ci	is_found = false;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	list_for_each_entry(adapter, &adapters_list_g, list) {
2608c2ecf20Sopenharmony_ci		if (iterator++ == adapno) {
2618c2ecf20Sopenharmony_ci			is_found = true;
2628c2ecf20Sopenharmony_ci			break;
2638c2ecf20Sopenharmony_ci		}
2648c2ecf20Sopenharmony_ci	}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	if (!is_found) {
2678c2ecf20Sopenharmony_ci		*rval = -ENODEV;
2688c2ecf20Sopenharmony_ci		return NULL;
2698c2ecf20Sopenharmony_ci	}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	return adapter;
2728c2ecf20Sopenharmony_ci}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci/**
2758c2ecf20Sopenharmony_ci * handle_drvrcmd - Checks if the opcode is a driver cmd and if it is, handles it.
2768c2ecf20Sopenharmony_ci * @arg		: packet sent by the user app
2778c2ecf20Sopenharmony_ci * @old_ioctl	: mimd if 1; uioc otherwise
2788c2ecf20Sopenharmony_ci * @rval	: pointer for command's returned value (not function status)
2798c2ecf20Sopenharmony_ci */
2808c2ecf20Sopenharmony_cistatic int
2818c2ecf20Sopenharmony_cihandle_drvrcmd(void __user *arg, uint8_t old_ioctl, int *rval)
2828c2ecf20Sopenharmony_ci{
2838c2ecf20Sopenharmony_ci	mimd_t		__user *umimd;
2848c2ecf20Sopenharmony_ci	mimd_t		kmimd;
2858c2ecf20Sopenharmony_ci	uint8_t		opcode;
2868c2ecf20Sopenharmony_ci	uint8_t		subopcode;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	if (old_ioctl)
2898c2ecf20Sopenharmony_ci		goto old_packet;
2908c2ecf20Sopenharmony_ci	else
2918c2ecf20Sopenharmony_ci		goto new_packet;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_cinew_packet:
2948c2ecf20Sopenharmony_ci	return (-ENOTSUPP);
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ciold_packet:
2978c2ecf20Sopenharmony_ci	*rval = 0;
2988c2ecf20Sopenharmony_ci	umimd = arg;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	if (copy_from_user(&kmimd, umimd, sizeof(mimd_t)))
3018c2ecf20Sopenharmony_ci		return (-EFAULT);
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	opcode		= kmimd.ui.fcs.opcode;
3048c2ecf20Sopenharmony_ci	subopcode	= kmimd.ui.fcs.subopcode;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	/*
3078c2ecf20Sopenharmony_ci	 * If the opcode is 0x82 and the subopcode is either GET_DRVRVER or
3088c2ecf20Sopenharmony_ci	 * GET_NUMADP, then we can handle. Otherwise we should return 1 to
3098c2ecf20Sopenharmony_ci	 * indicate that we cannot handle this.
3108c2ecf20Sopenharmony_ci	 */
3118c2ecf20Sopenharmony_ci	if (opcode != 0x82)
3128c2ecf20Sopenharmony_ci		return 1;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	switch (subopcode) {
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	case MEGAIOC_QDRVRVER:
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci		if (copy_to_user(kmimd.data, &drvr_ver, sizeof(uint32_t)))
3198c2ecf20Sopenharmony_ci			return (-EFAULT);
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci		return 0;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	case MEGAIOC_QNADAP:
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci		*rval = adapters_count_g;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci		if (copy_to_user(kmimd.data, &adapters_count_g,
3288c2ecf20Sopenharmony_ci				sizeof(uint32_t)))
3298c2ecf20Sopenharmony_ci			return (-EFAULT);
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci		return 0;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	default:
3348c2ecf20Sopenharmony_ci		/* cannot handle */
3358c2ecf20Sopenharmony_ci		return 1;
3368c2ecf20Sopenharmony_ci	}
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	return 0;
3398c2ecf20Sopenharmony_ci}
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci/**
3438c2ecf20Sopenharmony_ci * mimd_to_kioc	- Converter from old to new ioctl format
3448c2ecf20Sopenharmony_ci * @umimd	: user space old MIMD IOCTL
3458c2ecf20Sopenharmony_ci * @adp		: adapter softstate
3468c2ecf20Sopenharmony_ci * @kioc	: kernel space new format IOCTL
3478c2ecf20Sopenharmony_ci *
3488c2ecf20Sopenharmony_ci * Routine to convert MIMD interface IOCTL to new interface IOCTL packet. The
3498c2ecf20Sopenharmony_ci * new packet is in kernel space so that driver can perform operations on it
3508c2ecf20Sopenharmony_ci * freely.
3518c2ecf20Sopenharmony_ci */
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_cistatic int
3548c2ecf20Sopenharmony_cimimd_to_kioc(mimd_t __user *umimd, mraid_mmadp_t *adp, uioc_t *kioc)
3558c2ecf20Sopenharmony_ci{
3568c2ecf20Sopenharmony_ci	mbox64_t		*mbox64;
3578c2ecf20Sopenharmony_ci	mbox_t			*mbox;
3588c2ecf20Sopenharmony_ci	mraid_passthru_t	*pthru32;
3598c2ecf20Sopenharmony_ci	uint32_t		adapno;
3608c2ecf20Sopenharmony_ci	uint8_t			opcode;
3618c2ecf20Sopenharmony_ci	uint8_t			subopcode;
3628c2ecf20Sopenharmony_ci	mimd_t			mimd;
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	if (copy_from_user(&mimd, umimd, sizeof(mimd_t)))
3658c2ecf20Sopenharmony_ci		return (-EFAULT);
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	/*
3688c2ecf20Sopenharmony_ci	 * Applications are not allowed to send extd pthru
3698c2ecf20Sopenharmony_ci	 */
3708c2ecf20Sopenharmony_ci	if ((mimd.mbox[0] == MBOXCMD_PASSTHRU64) ||
3718c2ecf20Sopenharmony_ci			(mimd.mbox[0] == MBOXCMD_EXTPTHRU))
3728c2ecf20Sopenharmony_ci		return (-EINVAL);
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	opcode		= mimd.ui.fcs.opcode;
3758c2ecf20Sopenharmony_ci	subopcode	= mimd.ui.fcs.subopcode;
3768c2ecf20Sopenharmony_ci	adapno		= GETADAP(mimd.ui.fcs.adapno);
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	if (adapno >= adapters_count_g)
3798c2ecf20Sopenharmony_ci		return (-ENODEV);
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	kioc->adapno	= adapno;
3828c2ecf20Sopenharmony_ci	kioc->mb_type	= MBOX_LEGACY;
3838c2ecf20Sopenharmony_ci	kioc->app_type	= APPTYPE_MIMD;
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	switch (opcode) {
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	case 0x82:
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci		if (subopcode == MEGAIOC_QADAPINFO) {
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci			kioc->opcode	= GET_ADAP_INFO;
3928c2ecf20Sopenharmony_ci			kioc->data_dir	= UIOC_RD;
3938c2ecf20Sopenharmony_ci			kioc->xferlen	= sizeof(mraid_hba_info_t);
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci			if (mraid_mm_attach_buf(adp, kioc, kioc->xferlen))
3968c2ecf20Sopenharmony_ci				return (-ENOMEM);
3978c2ecf20Sopenharmony_ci		}
3988c2ecf20Sopenharmony_ci		else {
3998c2ecf20Sopenharmony_ci			con_log(CL_ANN, (KERN_WARNING
4008c2ecf20Sopenharmony_ci					"megaraid cmm: Invalid subop\n"));
4018c2ecf20Sopenharmony_ci			return (-EINVAL);
4028c2ecf20Sopenharmony_ci		}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci		break;
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	case 0x81:
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci		kioc->opcode		= MBOX_CMD;
4098c2ecf20Sopenharmony_ci		kioc->xferlen		= mimd.ui.fcs.length;
4108c2ecf20Sopenharmony_ci		kioc->user_data_len	= kioc->xferlen;
4118c2ecf20Sopenharmony_ci		kioc->user_data		= mimd.ui.fcs.buffer;
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci		if (mraid_mm_attach_buf(adp, kioc, kioc->xferlen))
4148c2ecf20Sopenharmony_ci			return (-ENOMEM);
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci		if (mimd.outlen) kioc->data_dir  = UIOC_RD;
4178c2ecf20Sopenharmony_ci		if (mimd.inlen) kioc->data_dir |= UIOC_WR;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci		break;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	case 0x80:
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci		kioc->opcode		= MBOX_CMD;
4248c2ecf20Sopenharmony_ci		kioc->xferlen		= (mimd.outlen > mimd.inlen) ?
4258c2ecf20Sopenharmony_ci						mimd.outlen : mimd.inlen;
4268c2ecf20Sopenharmony_ci		kioc->user_data_len	= kioc->xferlen;
4278c2ecf20Sopenharmony_ci		kioc->user_data		= mimd.data;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci		if (mraid_mm_attach_buf(adp, kioc, kioc->xferlen))
4308c2ecf20Sopenharmony_ci			return (-ENOMEM);
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci		if (mimd.outlen) kioc->data_dir  = UIOC_RD;
4338c2ecf20Sopenharmony_ci		if (mimd.inlen) kioc->data_dir |= UIOC_WR;
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci		break;
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	default:
4388c2ecf20Sopenharmony_ci		return (-EINVAL);
4398c2ecf20Sopenharmony_ci	}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	/*
4428c2ecf20Sopenharmony_ci	 * If driver command, nothing else to do
4438c2ecf20Sopenharmony_ci	 */
4448c2ecf20Sopenharmony_ci	if (opcode == 0x82)
4458c2ecf20Sopenharmony_ci		return 0;
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	/*
4488c2ecf20Sopenharmony_ci	 * This is a mailbox cmd; copy the mailbox from mimd
4498c2ecf20Sopenharmony_ci	 */
4508c2ecf20Sopenharmony_ci	mbox64	= (mbox64_t *)((unsigned long)kioc->cmdbuf);
4518c2ecf20Sopenharmony_ci	mbox	= &mbox64->mbox32;
4528c2ecf20Sopenharmony_ci	memcpy(mbox, mimd.mbox, 14);
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	if (mbox->cmd != MBOXCMD_PASSTHRU) {	// regular DCMD
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci		mbox->xferaddr	= (uint32_t)kioc->buf_paddr;
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci		if (kioc->data_dir & UIOC_WR) {
4598c2ecf20Sopenharmony_ci			if (copy_from_user(kioc->buf_vaddr, kioc->user_data,
4608c2ecf20Sopenharmony_ci							kioc->xferlen)) {
4618c2ecf20Sopenharmony_ci				return (-EFAULT);
4628c2ecf20Sopenharmony_ci			}
4638c2ecf20Sopenharmony_ci		}
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci		return 0;
4668c2ecf20Sopenharmony_ci	}
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	/*
4698c2ecf20Sopenharmony_ci	 * This is a regular 32-bit pthru cmd; mbox points to pthru struct.
4708c2ecf20Sopenharmony_ci	 * Just like in above case, the beginning for memblk is treated as
4718c2ecf20Sopenharmony_ci	 * a mailbox. The passthru will begin at next 1K boundary. And the
4728c2ecf20Sopenharmony_ci	 * data will start 1K after that.
4738c2ecf20Sopenharmony_ci	 */
4748c2ecf20Sopenharmony_ci	pthru32			= kioc->pthru32;
4758c2ecf20Sopenharmony_ci	kioc->user_pthru	= &umimd->pthru;
4768c2ecf20Sopenharmony_ci	mbox->xferaddr		= (uint32_t)kioc->pthru32_h;
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	if (copy_from_user(pthru32, kioc->user_pthru,
4798c2ecf20Sopenharmony_ci			sizeof(mraid_passthru_t))) {
4808c2ecf20Sopenharmony_ci		return (-EFAULT);
4818c2ecf20Sopenharmony_ci	}
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	pthru32->dataxferaddr	= kioc->buf_paddr;
4848c2ecf20Sopenharmony_ci	if (kioc->data_dir & UIOC_WR) {
4858c2ecf20Sopenharmony_ci		if (pthru32->dataxferlen > kioc->xferlen)
4868c2ecf20Sopenharmony_ci			return -EINVAL;
4878c2ecf20Sopenharmony_ci		if (copy_from_user(kioc->buf_vaddr, kioc->user_data,
4888c2ecf20Sopenharmony_ci						pthru32->dataxferlen)) {
4898c2ecf20Sopenharmony_ci			return (-EFAULT);
4908c2ecf20Sopenharmony_ci		}
4918c2ecf20Sopenharmony_ci	}
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	return 0;
4948c2ecf20Sopenharmony_ci}
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci/**
4978c2ecf20Sopenharmony_ci * mraid_mm_attch_buf - Attach a free dma buffer for required size
4988c2ecf20Sopenharmony_ci * @adp		: Adapter softstate
4998c2ecf20Sopenharmony_ci * @kioc	: kioc that the buffer needs to be attached to
5008c2ecf20Sopenharmony_ci * @xferlen	: required length for buffer
5018c2ecf20Sopenharmony_ci *
5028c2ecf20Sopenharmony_ci * First we search for a pool with smallest buffer that is >= @xferlen. If
5038c2ecf20Sopenharmony_ci * that pool has no free buffer, we will try for the next bigger size. If none
5048c2ecf20Sopenharmony_ci * is available, we will try to allocate the smallest buffer that is >=
5058c2ecf20Sopenharmony_ci * @xferlen and attach it the pool.
5068c2ecf20Sopenharmony_ci */
5078c2ecf20Sopenharmony_cistatic int
5088c2ecf20Sopenharmony_cimraid_mm_attach_buf(mraid_mmadp_t *adp, uioc_t *kioc, int xferlen)
5098c2ecf20Sopenharmony_ci{
5108c2ecf20Sopenharmony_ci	mm_dmapool_t	*pool;
5118c2ecf20Sopenharmony_ci	int		right_pool = -1;
5128c2ecf20Sopenharmony_ci	unsigned long	flags;
5138c2ecf20Sopenharmony_ci	int		i;
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	kioc->pool_index	= -1;
5168c2ecf20Sopenharmony_ci	kioc->buf_vaddr		= NULL;
5178c2ecf20Sopenharmony_ci	kioc->buf_paddr		= 0;
5188c2ecf20Sopenharmony_ci	kioc->free_buf		= 0;
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	/*
5218c2ecf20Sopenharmony_ci	 * We need xferlen amount of memory. See if we can get it from our
5228c2ecf20Sopenharmony_ci	 * dma pools. If we don't get exact size, we will try bigger buffer
5238c2ecf20Sopenharmony_ci	 */
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_DMA_POOLS; i++) {
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci		pool = &adp->dma_pool_list[i];
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci		if (xferlen > pool->buf_size)
5308c2ecf20Sopenharmony_ci			continue;
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci		if (right_pool == -1)
5338c2ecf20Sopenharmony_ci			right_pool = i;
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci		spin_lock_irqsave(&pool->lock, flags);
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci		if (!pool->in_use) {
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci			pool->in_use		= 1;
5408c2ecf20Sopenharmony_ci			kioc->pool_index	= i;
5418c2ecf20Sopenharmony_ci			kioc->buf_vaddr		= pool->vaddr;
5428c2ecf20Sopenharmony_ci			kioc->buf_paddr		= pool->paddr;
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&pool->lock, flags);
5458c2ecf20Sopenharmony_ci			return 0;
5468c2ecf20Sopenharmony_ci		}
5478c2ecf20Sopenharmony_ci		else {
5488c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&pool->lock, flags);
5498c2ecf20Sopenharmony_ci			continue;
5508c2ecf20Sopenharmony_ci		}
5518c2ecf20Sopenharmony_ci	}
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	/*
5548c2ecf20Sopenharmony_ci	 * If xferlen doesn't match any of our pools, return error
5558c2ecf20Sopenharmony_ci	 */
5568c2ecf20Sopenharmony_ci	if (right_pool == -1)
5578c2ecf20Sopenharmony_ci		return -EINVAL;
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	/*
5608c2ecf20Sopenharmony_ci	 * We did not get any buffer from the preallocated pool. Let us try
5618c2ecf20Sopenharmony_ci	 * to allocate one new buffer. NOTE: This is a blocking call.
5628c2ecf20Sopenharmony_ci	 */
5638c2ecf20Sopenharmony_ci	pool = &adp->dma_pool_list[right_pool];
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	spin_lock_irqsave(&pool->lock, flags);
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	kioc->pool_index	= right_pool;
5688c2ecf20Sopenharmony_ci	kioc->free_buf		= 1;
5698c2ecf20Sopenharmony_ci	kioc->buf_vaddr		= dma_pool_alloc(pool->handle, GFP_ATOMIC,
5708c2ecf20Sopenharmony_ci							&kioc->buf_paddr);
5718c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&pool->lock, flags);
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	if (!kioc->buf_vaddr)
5748c2ecf20Sopenharmony_ci		return -ENOMEM;
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	return 0;
5778c2ecf20Sopenharmony_ci}
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci/**
5808c2ecf20Sopenharmony_ci * mraid_mm_alloc_kioc - Returns a uioc_t from free list
5818c2ecf20Sopenharmony_ci * @adp	: Adapter softstate for this module
5828c2ecf20Sopenharmony_ci *
5838c2ecf20Sopenharmony_ci * The kioc_semaphore is initialized with number of kioc nodes in the
5848c2ecf20Sopenharmony_ci * free kioc pool. If the kioc pool is empty, this function blocks till
5858c2ecf20Sopenharmony_ci * a kioc becomes free.
5868c2ecf20Sopenharmony_ci */
5878c2ecf20Sopenharmony_cistatic uioc_t *
5888c2ecf20Sopenharmony_cimraid_mm_alloc_kioc(mraid_mmadp_t *adp)
5898c2ecf20Sopenharmony_ci{
5908c2ecf20Sopenharmony_ci	uioc_t			*kioc;
5918c2ecf20Sopenharmony_ci	struct list_head*	head;
5928c2ecf20Sopenharmony_ci	unsigned long		flags;
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	down(&adp->kioc_semaphore);
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci	spin_lock_irqsave(&adp->kioc_pool_lock, flags);
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci	head = &adp->kioc_pool;
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci	if (list_empty(head)) {
6018c2ecf20Sopenharmony_ci		up(&adp->kioc_semaphore);
6028c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&adp->kioc_pool_lock, flags);
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci		con_log(CL_ANN, ("megaraid cmm: kioc list empty!\n"));
6058c2ecf20Sopenharmony_ci		return NULL;
6068c2ecf20Sopenharmony_ci	}
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	kioc = list_entry(head->next, uioc_t, list);
6098c2ecf20Sopenharmony_ci	list_del_init(&kioc->list);
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&adp->kioc_pool_lock, flags);
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	memset((caddr_t)(unsigned long)kioc->cmdbuf, 0, sizeof(mbox64_t));
6148c2ecf20Sopenharmony_ci	memset((caddr_t) kioc->pthru32, 0, sizeof(mraid_passthru_t));
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci	kioc->buf_vaddr		= NULL;
6178c2ecf20Sopenharmony_ci	kioc->buf_paddr		= 0;
6188c2ecf20Sopenharmony_ci	kioc->pool_index	=-1;
6198c2ecf20Sopenharmony_ci	kioc->free_buf		= 0;
6208c2ecf20Sopenharmony_ci	kioc->user_data		= NULL;
6218c2ecf20Sopenharmony_ci	kioc->user_data_len	= 0;
6228c2ecf20Sopenharmony_ci	kioc->user_pthru	= NULL;
6238c2ecf20Sopenharmony_ci	kioc->timedout		= 0;
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci	return kioc;
6268c2ecf20Sopenharmony_ci}
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci/**
6298c2ecf20Sopenharmony_ci * mraid_mm_dealloc_kioc - Return kioc to free pool
6308c2ecf20Sopenharmony_ci * @adp		: Adapter softstate
6318c2ecf20Sopenharmony_ci * @kioc	: uioc_t node to be returned to free pool
6328c2ecf20Sopenharmony_ci */
6338c2ecf20Sopenharmony_cistatic void
6348c2ecf20Sopenharmony_cimraid_mm_dealloc_kioc(mraid_mmadp_t *adp, uioc_t *kioc)
6358c2ecf20Sopenharmony_ci{
6368c2ecf20Sopenharmony_ci	mm_dmapool_t	*pool;
6378c2ecf20Sopenharmony_ci	unsigned long	flags;
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	if (kioc->pool_index != -1) {
6408c2ecf20Sopenharmony_ci		pool = &adp->dma_pool_list[kioc->pool_index];
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci		/* This routine may be called in non-isr context also */
6438c2ecf20Sopenharmony_ci		spin_lock_irqsave(&pool->lock, flags);
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci		/*
6468c2ecf20Sopenharmony_ci		 * While attaching the dma buffer, if we didn't get the
6478c2ecf20Sopenharmony_ci		 * required buffer from the pool, we would have allocated
6488c2ecf20Sopenharmony_ci		 * it at the run time and set the free_buf flag. We must
6498c2ecf20Sopenharmony_ci		 * free that buffer. Otherwise, just mark that the buffer is
6508c2ecf20Sopenharmony_ci		 * not in use
6518c2ecf20Sopenharmony_ci		 */
6528c2ecf20Sopenharmony_ci		if (kioc->free_buf == 1)
6538c2ecf20Sopenharmony_ci			dma_pool_free(pool->handle, kioc->buf_vaddr,
6548c2ecf20Sopenharmony_ci							kioc->buf_paddr);
6558c2ecf20Sopenharmony_ci		else
6568c2ecf20Sopenharmony_ci			pool->in_use = 0;
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&pool->lock, flags);
6598c2ecf20Sopenharmony_ci	}
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	/* Return the kioc to the free pool */
6628c2ecf20Sopenharmony_ci	spin_lock_irqsave(&adp->kioc_pool_lock, flags);
6638c2ecf20Sopenharmony_ci	list_add(&kioc->list, &adp->kioc_pool);
6648c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&adp->kioc_pool_lock, flags);
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	/* increment the free kioc count */
6678c2ecf20Sopenharmony_ci	up(&adp->kioc_semaphore);
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	return;
6708c2ecf20Sopenharmony_ci}
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci/**
6738c2ecf20Sopenharmony_ci * lld_ioctl - Routine to issue ioctl to low level drvr
6748c2ecf20Sopenharmony_ci * @adp		: The adapter handle
6758c2ecf20Sopenharmony_ci * @kioc	: The ioctl packet with kernel addresses
6768c2ecf20Sopenharmony_ci */
6778c2ecf20Sopenharmony_cistatic int
6788c2ecf20Sopenharmony_cilld_ioctl(mraid_mmadp_t *adp, uioc_t *kioc)
6798c2ecf20Sopenharmony_ci{
6808c2ecf20Sopenharmony_ci	int			rval;
6818c2ecf20Sopenharmony_ci	struct uioc_timeout	timeout = { };
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci	kioc->status	= -ENODATA;
6848c2ecf20Sopenharmony_ci	rval		= adp->issue_uioc(adp->drvr_data, kioc, IOCTL_ISSUE);
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	if (rval) return rval;
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	/*
6898c2ecf20Sopenharmony_ci	 * Start the timer
6908c2ecf20Sopenharmony_ci	 */
6918c2ecf20Sopenharmony_ci	if (adp->timeout > 0) {
6928c2ecf20Sopenharmony_ci		timeout.uioc = kioc;
6938c2ecf20Sopenharmony_ci		timer_setup_on_stack(&timeout.timer, lld_timedout, 0);
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci		timeout.timer.expires	= jiffies + adp->timeout * HZ;
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci		add_timer(&timeout.timer);
6988c2ecf20Sopenharmony_ci	}
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	/*
7018c2ecf20Sopenharmony_ci	 * Wait till the low level driver completes the ioctl. After this
7028c2ecf20Sopenharmony_ci	 * call, the ioctl either completed successfully or timedout.
7038c2ecf20Sopenharmony_ci	 */
7048c2ecf20Sopenharmony_ci	wait_event(wait_q, (kioc->status != -ENODATA));
7058c2ecf20Sopenharmony_ci	if (timeout.timer.function) {
7068c2ecf20Sopenharmony_ci		del_timer_sync(&timeout.timer);
7078c2ecf20Sopenharmony_ci		destroy_timer_on_stack(&timeout.timer);
7088c2ecf20Sopenharmony_ci	}
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	/*
7118c2ecf20Sopenharmony_ci	 * If the command had timedout, we mark the controller offline
7128c2ecf20Sopenharmony_ci	 * before returning
7138c2ecf20Sopenharmony_ci	 */
7148c2ecf20Sopenharmony_ci	if (kioc->timedout) {
7158c2ecf20Sopenharmony_ci		adp->quiescent = 0;
7168c2ecf20Sopenharmony_ci	}
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	return kioc->status;
7198c2ecf20Sopenharmony_ci}
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci/**
7238c2ecf20Sopenharmony_ci * ioctl_done - callback from the low level driver
7248c2ecf20Sopenharmony_ci * @kioc	: completed ioctl packet
7258c2ecf20Sopenharmony_ci */
7268c2ecf20Sopenharmony_cistatic void
7278c2ecf20Sopenharmony_ciioctl_done(uioc_t *kioc)
7288c2ecf20Sopenharmony_ci{
7298c2ecf20Sopenharmony_ci	uint32_t	adapno;
7308c2ecf20Sopenharmony_ci	int		iterator;
7318c2ecf20Sopenharmony_ci	mraid_mmadp_t*	adapter;
7328c2ecf20Sopenharmony_ci	bool		is_found;
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci	/*
7358c2ecf20Sopenharmony_ci	 * When the kioc returns from driver, make sure it still doesn't
7368c2ecf20Sopenharmony_ci	 * have ENODATA in status. Otherwise, driver will hang on wait_event
7378c2ecf20Sopenharmony_ci	 * forever
7388c2ecf20Sopenharmony_ci	 */
7398c2ecf20Sopenharmony_ci	if (kioc->status == -ENODATA) {
7408c2ecf20Sopenharmony_ci		con_log(CL_ANN, (KERN_WARNING
7418c2ecf20Sopenharmony_ci			"megaraid cmm: lld didn't change status!\n"));
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci		kioc->status = -EINVAL;
7448c2ecf20Sopenharmony_ci	}
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci	/*
7478c2ecf20Sopenharmony_ci	 * Check if this kioc was timedout before. If so, nobody is waiting
7488c2ecf20Sopenharmony_ci	 * on this kioc. We don't have to wake up anybody. Instead, we just
7498c2ecf20Sopenharmony_ci	 * have to free the kioc
7508c2ecf20Sopenharmony_ci	 */
7518c2ecf20Sopenharmony_ci	if (kioc->timedout) {
7528c2ecf20Sopenharmony_ci		iterator	= 0;
7538c2ecf20Sopenharmony_ci		adapter		= NULL;
7548c2ecf20Sopenharmony_ci		adapno		= kioc->adapno;
7558c2ecf20Sopenharmony_ci		is_found	= false;
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci		con_log(CL_ANN, ( KERN_WARNING "megaraid cmm: completed "
7588c2ecf20Sopenharmony_ci					"ioctl that was timedout before\n"));
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci		list_for_each_entry(adapter, &adapters_list_g, list) {
7618c2ecf20Sopenharmony_ci			if (iterator++ == adapno) {
7628c2ecf20Sopenharmony_ci				is_found = true;
7638c2ecf20Sopenharmony_ci				break;
7648c2ecf20Sopenharmony_ci			}
7658c2ecf20Sopenharmony_ci		}
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci		kioc->timedout = 0;
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci		if (is_found)
7708c2ecf20Sopenharmony_ci			mraid_mm_dealloc_kioc( adapter, kioc );
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci	}
7738c2ecf20Sopenharmony_ci	else {
7748c2ecf20Sopenharmony_ci		wake_up(&wait_q);
7758c2ecf20Sopenharmony_ci	}
7768c2ecf20Sopenharmony_ci}
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci/**
7808c2ecf20Sopenharmony_ci * lld_timedout	- callback from the expired timer
7818c2ecf20Sopenharmony_ci * @t		: timer that timed out
7828c2ecf20Sopenharmony_ci */
7838c2ecf20Sopenharmony_cistatic void
7848c2ecf20Sopenharmony_cilld_timedout(struct timer_list *t)
7858c2ecf20Sopenharmony_ci{
7868c2ecf20Sopenharmony_ci	struct uioc_timeout *timeout = from_timer(timeout, t, timer);
7878c2ecf20Sopenharmony_ci	uioc_t *kioc	= timeout->uioc;
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	kioc->status 	= -ETIME;
7908c2ecf20Sopenharmony_ci	kioc->timedout	= 1;
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_ci	con_log(CL_ANN, (KERN_WARNING "megaraid cmm: ioctl timed out\n"));
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci	wake_up(&wait_q);
7958c2ecf20Sopenharmony_ci}
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci/**
7998c2ecf20Sopenharmony_ci * kioc_to_mimd	- Converter from new back to old format
8008c2ecf20Sopenharmony_ci * @kioc	: Kernel space IOCTL packet (successfully issued)
8018c2ecf20Sopenharmony_ci * @mimd	: User space MIMD packet
8028c2ecf20Sopenharmony_ci */
8038c2ecf20Sopenharmony_cistatic int
8048c2ecf20Sopenharmony_cikioc_to_mimd(uioc_t *kioc, mimd_t __user *mimd)
8058c2ecf20Sopenharmony_ci{
8068c2ecf20Sopenharmony_ci	mimd_t			kmimd;
8078c2ecf20Sopenharmony_ci	uint8_t			opcode;
8088c2ecf20Sopenharmony_ci	uint8_t			subopcode;
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci	mbox64_t		*mbox64;
8118c2ecf20Sopenharmony_ci	mraid_passthru_t	__user *upthru32;
8128c2ecf20Sopenharmony_ci	mraid_passthru_t	*kpthru32;
8138c2ecf20Sopenharmony_ci	mcontroller_t		cinfo;
8148c2ecf20Sopenharmony_ci	mraid_hba_info_t	*hinfo;
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci	if (copy_from_user(&kmimd, mimd, sizeof(mimd_t)))
8188c2ecf20Sopenharmony_ci		return (-EFAULT);
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci	opcode		= kmimd.ui.fcs.opcode;
8218c2ecf20Sopenharmony_ci	subopcode	= kmimd.ui.fcs.subopcode;
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci	if (opcode == 0x82) {
8248c2ecf20Sopenharmony_ci		switch (subopcode) {
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci		case MEGAIOC_QADAPINFO:
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci			hinfo = (mraid_hba_info_t *)(unsigned long)
8298c2ecf20Sopenharmony_ci					kioc->buf_vaddr;
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_ci			hinfo_to_cinfo(hinfo, &cinfo);
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci			if (copy_to_user(kmimd.data, &cinfo, sizeof(cinfo)))
8348c2ecf20Sopenharmony_ci				return (-EFAULT);
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci			return 0;
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci		default:
8398c2ecf20Sopenharmony_ci			return (-EINVAL);
8408c2ecf20Sopenharmony_ci		}
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci		return 0;
8438c2ecf20Sopenharmony_ci	}
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci	mbox64 = (mbox64_t *)(unsigned long)kioc->cmdbuf;
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci	if (kioc->user_pthru) {
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci		upthru32 = kioc->user_pthru;
8508c2ecf20Sopenharmony_ci		kpthru32 = kioc->pthru32;
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci		if (copy_to_user(&upthru32->scsistatus,
8538c2ecf20Sopenharmony_ci					&kpthru32->scsistatus,
8548c2ecf20Sopenharmony_ci					sizeof(uint8_t))) {
8558c2ecf20Sopenharmony_ci			return (-EFAULT);
8568c2ecf20Sopenharmony_ci		}
8578c2ecf20Sopenharmony_ci	}
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci	if (kioc->user_data) {
8608c2ecf20Sopenharmony_ci		if (copy_to_user(kioc->user_data, kioc->buf_vaddr,
8618c2ecf20Sopenharmony_ci					kioc->user_data_len)) {
8628c2ecf20Sopenharmony_ci			return (-EFAULT);
8638c2ecf20Sopenharmony_ci		}
8648c2ecf20Sopenharmony_ci	}
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci	if (copy_to_user(&mimd->mbox[17],
8678c2ecf20Sopenharmony_ci			&mbox64->mbox32.status, sizeof(uint8_t))) {
8688c2ecf20Sopenharmony_ci		return (-EFAULT);
8698c2ecf20Sopenharmony_ci	}
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci	return 0;
8728c2ecf20Sopenharmony_ci}
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_ci/**
8768c2ecf20Sopenharmony_ci * hinfo_to_cinfo - Convert new format hba info into old format
8778c2ecf20Sopenharmony_ci * @hinfo	: New format, more comprehensive adapter info
8788c2ecf20Sopenharmony_ci * @cinfo	: Old format adapter info to support mimd_t apps
8798c2ecf20Sopenharmony_ci */
8808c2ecf20Sopenharmony_cistatic void
8818c2ecf20Sopenharmony_cihinfo_to_cinfo(mraid_hba_info_t *hinfo, mcontroller_t *cinfo)
8828c2ecf20Sopenharmony_ci{
8838c2ecf20Sopenharmony_ci	if (!hinfo || !cinfo)
8848c2ecf20Sopenharmony_ci		return;
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ci	cinfo->base		= hinfo->baseport;
8878c2ecf20Sopenharmony_ci	cinfo->irq		= hinfo->irq;
8888c2ecf20Sopenharmony_ci	cinfo->numldrv		= hinfo->num_ldrv;
8898c2ecf20Sopenharmony_ci	cinfo->pcibus		= hinfo->pci_bus;
8908c2ecf20Sopenharmony_ci	cinfo->pcidev		= hinfo->pci_slot;
8918c2ecf20Sopenharmony_ci	cinfo->pcifun		= PCI_FUNC(hinfo->pci_dev_fn);
8928c2ecf20Sopenharmony_ci	cinfo->pciid		= hinfo->pci_device_id;
8938c2ecf20Sopenharmony_ci	cinfo->pcivendor	= hinfo->pci_vendor_id;
8948c2ecf20Sopenharmony_ci	cinfo->pcislot		= hinfo->pci_slot;
8958c2ecf20Sopenharmony_ci	cinfo->uid		= hinfo->unique_id;
8968c2ecf20Sopenharmony_ci}
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci/**
9008c2ecf20Sopenharmony_ci * mraid_mm_register_adp - Registration routine for low level drivers
9018c2ecf20Sopenharmony_ci * @lld_adp	: Adapter object
9028c2ecf20Sopenharmony_ci */
9038c2ecf20Sopenharmony_ciint
9048c2ecf20Sopenharmony_cimraid_mm_register_adp(mraid_mmadp_t *lld_adp)
9058c2ecf20Sopenharmony_ci{
9068c2ecf20Sopenharmony_ci	mraid_mmadp_t	*adapter;
9078c2ecf20Sopenharmony_ci	mbox64_t	*mbox_list;
9088c2ecf20Sopenharmony_ci	uioc_t		*kioc;
9098c2ecf20Sopenharmony_ci	uint32_t	rval;
9108c2ecf20Sopenharmony_ci	int		i;
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ci
9138c2ecf20Sopenharmony_ci	if (lld_adp->drvr_type != DRVRTYPE_MBOX)
9148c2ecf20Sopenharmony_ci		return (-EINVAL);
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci	adapter = kzalloc(sizeof(mraid_mmadp_t), GFP_KERNEL);
9178c2ecf20Sopenharmony_ci
9188c2ecf20Sopenharmony_ci	if (!adapter)
9198c2ecf20Sopenharmony_ci		return -ENOMEM;
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci	adapter->unique_id	= lld_adp->unique_id;
9238c2ecf20Sopenharmony_ci	adapter->drvr_type	= lld_adp->drvr_type;
9248c2ecf20Sopenharmony_ci	adapter->drvr_data	= lld_adp->drvr_data;
9258c2ecf20Sopenharmony_ci	adapter->pdev		= lld_adp->pdev;
9268c2ecf20Sopenharmony_ci	adapter->issue_uioc	= lld_adp->issue_uioc;
9278c2ecf20Sopenharmony_ci	adapter->timeout	= lld_adp->timeout;
9288c2ecf20Sopenharmony_ci	adapter->max_kioc	= lld_adp->max_kioc;
9298c2ecf20Sopenharmony_ci	adapter->quiescent	= 1;
9308c2ecf20Sopenharmony_ci
9318c2ecf20Sopenharmony_ci	/*
9328c2ecf20Sopenharmony_ci	 * Allocate single blocks of memory for all required kiocs,
9338c2ecf20Sopenharmony_ci	 * mailboxes and passthru structures.
9348c2ecf20Sopenharmony_ci	 */
9358c2ecf20Sopenharmony_ci	adapter->kioc_list	= kmalloc_array(lld_adp->max_kioc,
9368c2ecf20Sopenharmony_ci						  sizeof(uioc_t),
9378c2ecf20Sopenharmony_ci						  GFP_KERNEL);
9388c2ecf20Sopenharmony_ci	adapter->mbox_list	= kmalloc_array(lld_adp->max_kioc,
9398c2ecf20Sopenharmony_ci						  sizeof(mbox64_t),
9408c2ecf20Sopenharmony_ci						  GFP_KERNEL);
9418c2ecf20Sopenharmony_ci	adapter->pthru_dma_pool = dma_pool_create("megaraid mm pthru pool",
9428c2ecf20Sopenharmony_ci						&adapter->pdev->dev,
9438c2ecf20Sopenharmony_ci						sizeof(mraid_passthru_t),
9448c2ecf20Sopenharmony_ci						16, 0);
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci	if (!adapter->kioc_list || !adapter->mbox_list ||
9478c2ecf20Sopenharmony_ci			!adapter->pthru_dma_pool) {
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_ci		con_log(CL_ANN, (KERN_WARNING
9508c2ecf20Sopenharmony_ci			"megaraid cmm: out of memory, %s %d\n", __func__,
9518c2ecf20Sopenharmony_ci			__LINE__));
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_ci		rval = (-ENOMEM);
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci		goto memalloc_error;
9568c2ecf20Sopenharmony_ci	}
9578c2ecf20Sopenharmony_ci
9588c2ecf20Sopenharmony_ci	/*
9598c2ecf20Sopenharmony_ci	 * Slice kioc_list and make a kioc_pool with the individiual kiocs
9608c2ecf20Sopenharmony_ci	 */
9618c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&adapter->kioc_pool);
9628c2ecf20Sopenharmony_ci	spin_lock_init(&adapter->kioc_pool_lock);
9638c2ecf20Sopenharmony_ci	sema_init(&adapter->kioc_semaphore, lld_adp->max_kioc);
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_ci	mbox_list	= (mbox64_t *)adapter->mbox_list;
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_ci	for (i = 0; i < lld_adp->max_kioc; i++) {
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_ci		kioc		= adapter->kioc_list + i;
9708c2ecf20Sopenharmony_ci		kioc->cmdbuf	= (uint64_t)(unsigned long)(mbox_list + i);
9718c2ecf20Sopenharmony_ci		kioc->pthru32	= dma_pool_alloc(adapter->pthru_dma_pool,
9728c2ecf20Sopenharmony_ci						GFP_KERNEL, &kioc->pthru32_h);
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_ci		if (!kioc->pthru32) {
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_ci			con_log(CL_ANN, (KERN_WARNING
9778c2ecf20Sopenharmony_ci				"megaraid cmm: out of memory, %s %d\n",
9788c2ecf20Sopenharmony_ci					__func__, __LINE__));
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci			rval = (-ENOMEM);
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci			goto pthru_dma_pool_error;
9838c2ecf20Sopenharmony_ci		}
9848c2ecf20Sopenharmony_ci
9858c2ecf20Sopenharmony_ci		list_add_tail(&kioc->list, &adapter->kioc_pool);
9868c2ecf20Sopenharmony_ci	}
9878c2ecf20Sopenharmony_ci
9888c2ecf20Sopenharmony_ci	// Setup the dma pools for data buffers
9898c2ecf20Sopenharmony_ci	if ((rval = mraid_mm_setup_dma_pools(adapter)) != 0) {
9908c2ecf20Sopenharmony_ci		goto dma_pool_error;
9918c2ecf20Sopenharmony_ci	}
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_ci	list_add_tail(&adapter->list, &adapters_list_g);
9948c2ecf20Sopenharmony_ci
9958c2ecf20Sopenharmony_ci	adapters_count_g++;
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci	return 0;
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_cidma_pool_error:
10008c2ecf20Sopenharmony_ci	/* Do nothing */
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_cipthru_dma_pool_error:
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_ci	for (i = 0; i < lld_adp->max_kioc; i++) {
10058c2ecf20Sopenharmony_ci		kioc = adapter->kioc_list + i;
10068c2ecf20Sopenharmony_ci		if (kioc->pthru32) {
10078c2ecf20Sopenharmony_ci			dma_pool_free(adapter->pthru_dma_pool, kioc->pthru32,
10088c2ecf20Sopenharmony_ci				kioc->pthru32_h);
10098c2ecf20Sopenharmony_ci		}
10108c2ecf20Sopenharmony_ci	}
10118c2ecf20Sopenharmony_ci
10128c2ecf20Sopenharmony_cimemalloc_error:
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci	kfree(adapter->kioc_list);
10158c2ecf20Sopenharmony_ci	kfree(adapter->mbox_list);
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci	dma_pool_destroy(adapter->pthru_dma_pool);
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_ci	kfree(adapter);
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_ci	return rval;
10228c2ecf20Sopenharmony_ci}
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_ci
10258c2ecf20Sopenharmony_ci/**
10268c2ecf20Sopenharmony_ci * mraid_mm_adapter_app_handle - return the application handle for this adapter
10278c2ecf20Sopenharmony_ci * @unique_id	: adapter unique identifier
10288c2ecf20Sopenharmony_ci *
10298c2ecf20Sopenharmony_ci * For the given driver data, locate the adapter in our global list and
10308c2ecf20Sopenharmony_ci * return the corresponding handle, which is also used by applications to
10318c2ecf20Sopenharmony_ci * uniquely identify an adapter.
10328c2ecf20Sopenharmony_ci *
10338c2ecf20Sopenharmony_ci * Return adapter handle if found in the list.
10348c2ecf20Sopenharmony_ci * Return 0 if adapter could not be located, should never happen though.
10358c2ecf20Sopenharmony_ci */
10368c2ecf20Sopenharmony_ciuint32_t
10378c2ecf20Sopenharmony_cimraid_mm_adapter_app_handle(uint32_t unique_id)
10388c2ecf20Sopenharmony_ci{
10398c2ecf20Sopenharmony_ci	mraid_mmadp_t	*adapter;
10408c2ecf20Sopenharmony_ci	mraid_mmadp_t	*tmp;
10418c2ecf20Sopenharmony_ci	int		index = 0;
10428c2ecf20Sopenharmony_ci
10438c2ecf20Sopenharmony_ci	list_for_each_entry_safe(adapter, tmp, &adapters_list_g, list) {
10448c2ecf20Sopenharmony_ci
10458c2ecf20Sopenharmony_ci		if (adapter->unique_id == unique_id) {
10468c2ecf20Sopenharmony_ci
10478c2ecf20Sopenharmony_ci			return MKADAP(index);
10488c2ecf20Sopenharmony_ci		}
10498c2ecf20Sopenharmony_ci
10508c2ecf20Sopenharmony_ci		index++;
10518c2ecf20Sopenharmony_ci	}
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_ci	return 0;
10548c2ecf20Sopenharmony_ci}
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_ci
10578c2ecf20Sopenharmony_ci/**
10588c2ecf20Sopenharmony_ci * mraid_mm_setup_dma_pools - Set up dma buffer pools per adapter
10598c2ecf20Sopenharmony_ci * @adp	: Adapter softstate
10608c2ecf20Sopenharmony_ci *
10618c2ecf20Sopenharmony_ci * We maintain a pool of dma buffers per each adapter. Each pool has one
10628c2ecf20Sopenharmony_ci * buffer. E.g, we may have 5 dma pools - one each for 4k, 8k ... 64k buffers.
10638c2ecf20Sopenharmony_ci * We have just one 4k buffer in 4k pool, one 8k buffer in 8k pool etc. We
10648c2ecf20Sopenharmony_ci * dont' want to waste too much memory by allocating more buffers per each
10658c2ecf20Sopenharmony_ci * pool.
10668c2ecf20Sopenharmony_ci */
10678c2ecf20Sopenharmony_cistatic int
10688c2ecf20Sopenharmony_cimraid_mm_setup_dma_pools(mraid_mmadp_t *adp)
10698c2ecf20Sopenharmony_ci{
10708c2ecf20Sopenharmony_ci	mm_dmapool_t	*pool;
10718c2ecf20Sopenharmony_ci	int		bufsize;
10728c2ecf20Sopenharmony_ci	int		i;
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_ci	/*
10758c2ecf20Sopenharmony_ci	 * Create MAX_DMA_POOLS number of pools
10768c2ecf20Sopenharmony_ci	 */
10778c2ecf20Sopenharmony_ci	bufsize = MRAID_MM_INIT_BUFF_SIZE;
10788c2ecf20Sopenharmony_ci
10798c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_DMA_POOLS; i++){
10808c2ecf20Sopenharmony_ci
10818c2ecf20Sopenharmony_ci		pool = &adp->dma_pool_list[i];
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ci		pool->buf_size = bufsize;
10848c2ecf20Sopenharmony_ci		spin_lock_init(&pool->lock);
10858c2ecf20Sopenharmony_ci
10868c2ecf20Sopenharmony_ci		pool->handle = dma_pool_create("megaraid mm data buffer",
10878c2ecf20Sopenharmony_ci						&adp->pdev->dev, bufsize,
10888c2ecf20Sopenharmony_ci						16, 0);
10898c2ecf20Sopenharmony_ci
10908c2ecf20Sopenharmony_ci		if (!pool->handle) {
10918c2ecf20Sopenharmony_ci			goto dma_pool_setup_error;
10928c2ecf20Sopenharmony_ci		}
10938c2ecf20Sopenharmony_ci
10948c2ecf20Sopenharmony_ci		pool->vaddr = dma_pool_alloc(pool->handle, GFP_KERNEL,
10958c2ecf20Sopenharmony_ci							&pool->paddr);
10968c2ecf20Sopenharmony_ci
10978c2ecf20Sopenharmony_ci		if (!pool->vaddr)
10988c2ecf20Sopenharmony_ci			goto dma_pool_setup_error;
10998c2ecf20Sopenharmony_ci
11008c2ecf20Sopenharmony_ci		bufsize = bufsize * 2;
11018c2ecf20Sopenharmony_ci	}
11028c2ecf20Sopenharmony_ci
11038c2ecf20Sopenharmony_ci	return 0;
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_cidma_pool_setup_error:
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_ci	mraid_mm_teardown_dma_pools(adp);
11088c2ecf20Sopenharmony_ci	return (-ENOMEM);
11098c2ecf20Sopenharmony_ci}
11108c2ecf20Sopenharmony_ci
11118c2ecf20Sopenharmony_ci
11128c2ecf20Sopenharmony_ci/**
11138c2ecf20Sopenharmony_ci * mraid_mm_unregister_adp - Unregister routine for low level drivers
11148c2ecf20Sopenharmony_ci * @unique_id	: UID of the adpater
11158c2ecf20Sopenharmony_ci *
11168c2ecf20Sopenharmony_ci * Assumes no outstanding ioctls to llds.
11178c2ecf20Sopenharmony_ci */
11188c2ecf20Sopenharmony_ciint
11198c2ecf20Sopenharmony_cimraid_mm_unregister_adp(uint32_t unique_id)
11208c2ecf20Sopenharmony_ci{
11218c2ecf20Sopenharmony_ci	mraid_mmadp_t	*adapter;
11228c2ecf20Sopenharmony_ci	mraid_mmadp_t	*tmp;
11238c2ecf20Sopenharmony_ci
11248c2ecf20Sopenharmony_ci	list_for_each_entry_safe(adapter, tmp, &adapters_list_g, list) {
11258c2ecf20Sopenharmony_ci
11268c2ecf20Sopenharmony_ci
11278c2ecf20Sopenharmony_ci		if (adapter->unique_id == unique_id) {
11288c2ecf20Sopenharmony_ci
11298c2ecf20Sopenharmony_ci			adapters_count_g--;
11308c2ecf20Sopenharmony_ci
11318c2ecf20Sopenharmony_ci			list_del_init(&adapter->list);
11328c2ecf20Sopenharmony_ci
11338c2ecf20Sopenharmony_ci			mraid_mm_free_adp_resources(adapter);
11348c2ecf20Sopenharmony_ci
11358c2ecf20Sopenharmony_ci			kfree(adapter);
11368c2ecf20Sopenharmony_ci
11378c2ecf20Sopenharmony_ci			con_log(CL_ANN, (
11388c2ecf20Sopenharmony_ci				"megaraid cmm: Unregistered one adapter:%#x\n",
11398c2ecf20Sopenharmony_ci				unique_id));
11408c2ecf20Sopenharmony_ci
11418c2ecf20Sopenharmony_ci			return 0;
11428c2ecf20Sopenharmony_ci		}
11438c2ecf20Sopenharmony_ci	}
11448c2ecf20Sopenharmony_ci
11458c2ecf20Sopenharmony_ci	return (-ENODEV);
11468c2ecf20Sopenharmony_ci}
11478c2ecf20Sopenharmony_ci
11488c2ecf20Sopenharmony_ci/**
11498c2ecf20Sopenharmony_ci * mraid_mm_free_adp_resources - Free adapter softstate
11508c2ecf20Sopenharmony_ci * @adp	: Adapter softstate
11518c2ecf20Sopenharmony_ci */
11528c2ecf20Sopenharmony_cistatic void
11538c2ecf20Sopenharmony_cimraid_mm_free_adp_resources(mraid_mmadp_t *adp)
11548c2ecf20Sopenharmony_ci{
11558c2ecf20Sopenharmony_ci	uioc_t	*kioc;
11568c2ecf20Sopenharmony_ci	int	i;
11578c2ecf20Sopenharmony_ci
11588c2ecf20Sopenharmony_ci	mraid_mm_teardown_dma_pools(adp);
11598c2ecf20Sopenharmony_ci
11608c2ecf20Sopenharmony_ci	for (i = 0; i < adp->max_kioc; i++) {
11618c2ecf20Sopenharmony_ci
11628c2ecf20Sopenharmony_ci		kioc = adp->kioc_list + i;
11638c2ecf20Sopenharmony_ci
11648c2ecf20Sopenharmony_ci		dma_pool_free(adp->pthru_dma_pool, kioc->pthru32,
11658c2ecf20Sopenharmony_ci				kioc->pthru32_h);
11668c2ecf20Sopenharmony_ci	}
11678c2ecf20Sopenharmony_ci
11688c2ecf20Sopenharmony_ci	kfree(adp->kioc_list);
11698c2ecf20Sopenharmony_ci	kfree(adp->mbox_list);
11708c2ecf20Sopenharmony_ci
11718c2ecf20Sopenharmony_ci	dma_pool_destroy(adp->pthru_dma_pool);
11728c2ecf20Sopenharmony_ci
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_ci	return;
11758c2ecf20Sopenharmony_ci}
11768c2ecf20Sopenharmony_ci
11778c2ecf20Sopenharmony_ci
11788c2ecf20Sopenharmony_ci/**
11798c2ecf20Sopenharmony_ci * mraid_mm_teardown_dma_pools - Free all per adapter dma buffers
11808c2ecf20Sopenharmony_ci * @adp	: Adapter softstate
11818c2ecf20Sopenharmony_ci */
11828c2ecf20Sopenharmony_cistatic void
11838c2ecf20Sopenharmony_cimraid_mm_teardown_dma_pools(mraid_mmadp_t *adp)
11848c2ecf20Sopenharmony_ci{
11858c2ecf20Sopenharmony_ci	int		i;
11868c2ecf20Sopenharmony_ci	mm_dmapool_t	*pool;
11878c2ecf20Sopenharmony_ci
11888c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_DMA_POOLS; i++) {
11898c2ecf20Sopenharmony_ci
11908c2ecf20Sopenharmony_ci		pool = &adp->dma_pool_list[i];
11918c2ecf20Sopenharmony_ci
11928c2ecf20Sopenharmony_ci		if (pool->handle) {
11938c2ecf20Sopenharmony_ci
11948c2ecf20Sopenharmony_ci			if (pool->vaddr)
11958c2ecf20Sopenharmony_ci				dma_pool_free(pool->handle, pool->vaddr,
11968c2ecf20Sopenharmony_ci							pool->paddr);
11978c2ecf20Sopenharmony_ci
11988c2ecf20Sopenharmony_ci			dma_pool_destroy(pool->handle);
11998c2ecf20Sopenharmony_ci			pool->handle = NULL;
12008c2ecf20Sopenharmony_ci		}
12018c2ecf20Sopenharmony_ci	}
12028c2ecf20Sopenharmony_ci
12038c2ecf20Sopenharmony_ci	return;
12048c2ecf20Sopenharmony_ci}
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_ci/**
12078c2ecf20Sopenharmony_ci * mraid_mm_init	- Module entry point
12088c2ecf20Sopenharmony_ci */
12098c2ecf20Sopenharmony_cistatic int __init
12108c2ecf20Sopenharmony_cimraid_mm_init(void)
12118c2ecf20Sopenharmony_ci{
12128c2ecf20Sopenharmony_ci	int err;
12138c2ecf20Sopenharmony_ci
12148c2ecf20Sopenharmony_ci	// Announce the driver version
12158c2ecf20Sopenharmony_ci	con_log(CL_ANN, (KERN_INFO "megaraid cmm: %s %s\n",
12168c2ecf20Sopenharmony_ci		LSI_COMMON_MOD_VERSION, LSI_COMMON_MOD_EXT_VERSION));
12178c2ecf20Sopenharmony_ci
12188c2ecf20Sopenharmony_ci	err = misc_register(&megaraid_mm_dev);
12198c2ecf20Sopenharmony_ci	if (err < 0) {
12208c2ecf20Sopenharmony_ci		con_log(CL_ANN, ("megaraid cmm: cannot register misc device\n"));
12218c2ecf20Sopenharmony_ci		return err;
12228c2ecf20Sopenharmony_ci	}
12238c2ecf20Sopenharmony_ci
12248c2ecf20Sopenharmony_ci	init_waitqueue_head(&wait_q);
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&adapters_list_g);
12278c2ecf20Sopenharmony_ci
12288c2ecf20Sopenharmony_ci	return 0;
12298c2ecf20Sopenharmony_ci}
12308c2ecf20Sopenharmony_ci
12318c2ecf20Sopenharmony_ci
12328c2ecf20Sopenharmony_ci/**
12338c2ecf20Sopenharmony_ci * mraid_mm_exit	- Module exit point
12348c2ecf20Sopenharmony_ci */
12358c2ecf20Sopenharmony_cistatic void __exit
12368c2ecf20Sopenharmony_cimraid_mm_exit(void)
12378c2ecf20Sopenharmony_ci{
12388c2ecf20Sopenharmony_ci	con_log(CL_DLEVEL1 , ("exiting common mod\n"));
12398c2ecf20Sopenharmony_ci
12408c2ecf20Sopenharmony_ci	misc_deregister(&megaraid_mm_dev);
12418c2ecf20Sopenharmony_ci}
12428c2ecf20Sopenharmony_ci
12438c2ecf20Sopenharmony_cimodule_init(mraid_mm_init);
12448c2ecf20Sopenharmony_cimodule_exit(mraid_mm_exit);
12458c2ecf20Sopenharmony_ci
12468c2ecf20Sopenharmony_ci/* vi: set ts=8 sw=8 tw=78: */
1247