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