/* * --------------------------------------------------------------------------- * FILE: io.c * * PURPOSE: * This file contains routines that the SDIO driver can call when a * UniFi card is first inserted (or detected) and removed. * * When used with sdioemb, the udev scripts (at least on Ubuntu) don't * recognise a UniFi being added to the system. This is because sdioemb * does not register itself as a device_driver, it uses it's own code * to handle insert and remove. * To have Ubuntu recognise UniFi, edit /etc/udev/rules.d/85-ifupdown.rules * to change this line: * SUBSYSTEM=="net", DRIVERS=="?*", GOTO="net_start" * to these: * #SUBSYSTEM=="net", DRIVERS=="?*", GOTO="net_start" * SUBSYSTEM=="net", GOTO="net_start" * * Then you can add a stanza to /etc/network/interfaces like this: * auto eth1 * iface eth1 inet dhcp * wpa-conf /etc/wpa_supplicant.conf * This will then automatically associate when a car dis inserted. * * Copyright (C) 2006-2009 by Cambridge Silicon Radio Ltd. * * Refer to LICENSE.txt included with this source code for details on * the license terms. * * --------------------------------------------------------------------------- */ #include #include #include "csr_wifi_hip_unifi.h" #include "csr_wifi_hip_unifiversion.h" #include "csr_wifi_hip_unifi_udi.h" /* for unifi_print_status() */ #include "unifiio.h" #include "unifi_priv.h" /* * Array of pointers to context structs for unifi devices that are present. * The index in the array corresponds to the wlan interface number * (if "wlan*" is used). If "eth*" is used, the eth* numbers are allocated * after any Ethernet cards. * * The Arasan PCI-SDIO controller card supported by this driver has 2 slots, * hence a max of 2 devices. */ static unifi_priv_t *Unifi_instances[MAX_UNIFI_DEVS]; /* Array of pointers to netdev objects used by the UniFi driver, as there * are now many per instance. This is used to determine which netdev events * are for UniFi as opposed to other net interfaces. */ static netInterface_priv_t *Unifi_netdev_instances[MAX_UNIFI_DEVS * CSR_WIFI_NUM_INTERFACES]; /* * Array to hold the status of each unifi device in each slot. * We only process an insert event when In_use[] for the slot is * UNIFI_DEV_NOT_IN_USE. Otherwise, it means that the slot is in use or * we are in the middle of a cleanup (the action on unplug). */ #define UNIFI_DEV_NOT_IN_USE 0 #define UNIFI_DEV_IN_USE 1 #define UNIFI_DEV_CLEANUP 2 static int In_use[MAX_UNIFI_DEVS]; /* * Mutex to prevent UDI clients to open the character device before the priv * is created and initialised. */ DEFINE_SEMAPHORE(Unifi_instance_mutex); /* * When the device is removed, unregister waits on Unifi_cleanup_wq * until all the UDI clients release the character device. */ DECLARE_WAIT_QUEUE_HEAD(Unifi_cleanup_wq); #ifdef CONFIG_PROC_FS /* * seq_file wrappers for procfile show routines. */ static int uf_proc_show(struct seq_file *m, void *v); #define UNIFI_DEBUG_TXT_BUFFER (8 * 1024) static int uf_proc_open(struct inode *inode, struct file *file) { return single_open_size(file, uf_proc_show, PDE_DATA(inode), UNIFI_DEBUG_TXT_BUFFER); } static const struct file_operations uf_proc_fops = { .open = uf_proc_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; #endif /* CONFIG_PROC_FS */ #ifdef CSR_WIFI_RX_PATH_SPLIT static CsrResult signal_buffer_init(unifi_priv_t * priv, int size) { int i; priv->rxSignalBuffer.writePointer = priv->rxSignalBuffer.readPointer = 0; priv->rxSignalBuffer.size = size; /* Allocating Memory for Signal primitive pointer */ for(i=0; irxSignalBuffer.rx_buff[i].sig_len=0; priv->rxSignalBuffer.rx_buff[i].bufptr = kmalloc(UNIFI_PACKED_SIGBUF_SIZE, GFP_KERNEL); if (priv->rxSignalBuffer.rx_buff[i].bufptr == NULL) { int j; unifi_error(priv,"signal_buffer_init:Failed to Allocate shared memory for T-H signals \n"); for(j=0;jrxSignalBuffer.rx_buff[j].sig_len=0; kfree(priv->rxSignalBuffer.rx_buff[j].bufptr); priv->rxSignalBuffer.rx_buff[j].bufptr = NULL; } return -1; } } return 0; } static void signal_buffer_free(unifi_priv_t * priv, int size) { int i; for(i=0; irxSignalBuffer.rx_buff[i].sig_len=0; kfree(priv->rxSignalBuffer.rx_buff[i].bufptr); priv->rxSignalBuffer.rx_buff[i].bufptr = NULL; } } #endif /* * --------------------------------------------------------------------------- * uf_register_netdev * * Registers the network interface, installes the qdisc, * and registers the inet handler. * In the porting exercise, register the driver to the network * stack if necessary. * * Arguments: * priv Pointer to driver context. * * Returns: * O on success, non-zero otherwise. * * Notes: * We will only unregister when the card is ejected, so we must * only do it once. * --------------------------------------------------------------------------- */ int uf_register_netdev(unifi_priv_t *priv, int interfaceTag) { int r; netInterface_priv_t *interfacePriv = priv->interfacePriv[interfaceTag]; if (interfaceTag >= CSR_WIFI_NUM_INTERFACES) { unifi_error(priv, "uf_register_netdev bad interfaceTag\n"); return -EINVAL; } /* * Allocates a device number and registers device with the network * stack. */ unifi_trace(priv, UDBG5, "uf_register_netdev: netdev %d - 0x%p\n", interfaceTag, priv->netdev[interfaceTag]); r = register_netdev(priv->netdev[interfaceTag]); if (r) { unifi_error(priv, "Failed to register net device\n"); return -EINVAL; } /* The device is registed */ interfacePriv->netdev_registered = 1; #ifdef CSR_SUPPORT_SME /* * Register the inet handler; it notifies us for changes in the IP address. */ uf_register_inet_notifier(); #endif /* CSR_SUPPORT_SME */ unifi_notice(priv, "unifi%d is %s\n", priv->instance, priv->netdev[interfaceTag]->name); return 0; } /* uf_register_netdev */ /* * --------------------------------------------------------------------------- * uf_unregister_netdev * * Unregisters the network interface and the inet handler. * * Arguments: * priv Pointer to driver context. * * Returns: * None. * * --------------------------------------------------------------------------- */ void uf_unregister_netdev(unifi_priv_t *priv) { int i=0; #ifdef CSR_SUPPORT_SME /* Unregister the inet handler... */ uf_unregister_inet_notifier(); #endif /* CSR_SUPPORT_SME */ for (i=0; iinterfacePriv[i]; if (interfacePriv->netdev_registered) { unifi_trace(priv, UDBG5, "uf_unregister_netdev: netdev %d - 0x%p\n", i, priv->netdev[i]); /* ... and the netdev */ unregister_netdev(priv->netdev[i]); interfacePriv->netdev_registered = 0; } interfacePriv->interfaceMode = 0; /* Enable all queues by default */ interfacePriv->queueEnabled[0] = 1; interfacePriv->queueEnabled[1] = 1; interfacePriv->queueEnabled[2] = 1; interfacePriv->queueEnabled[3] = 1; } priv->totalInterfaceCount = 0; } /* uf_unregister_netdev() */ /* * --------------------------------------------------------------------------- * register_unifi_sdio * * This function is called from the Probe (or equivalent) method of * the SDIO driver when a UniFi card is detected. * We allocate the Linux net_device struct, initialise the HIP core * lib, create the char device nodes and start the userspace helper * to initialise the device. * * Arguments: * sdio_dev Pointer to SDIO context handle to use for all * SDIO ops. * bus_id A small number indicating the SDIO card position on the * bus. Typically this is the slot number, e.g. 0, 1 etc. * Valid values are 0 to MAX_UNIFI_DEVS-1. * dev Pointer to kernel device manager struct. * * Returns: * Pointer to the unifi instance, or NULL on error. * --------------------------------------------------------------------------- */ static unifi_priv_t * register_unifi_sdio(CsrSdioFunction *sdio_dev, int bus_id, struct device *dev) { unifi_priv_t *priv = NULL; int r = -1; CsrResult csrResult; if ((bus_id < 0) || (bus_id >= MAX_UNIFI_DEVS)) { unifi_error(priv, "register_unifi_sdio: invalid device %d\n", bus_id); return NULL; } down(&Unifi_instance_mutex); if (In_use[bus_id] != UNIFI_DEV_NOT_IN_USE) { unifi_error(priv, "register_unifi_sdio: device %d is already in use\n", bus_id); goto failed0; } /* Allocate device private and net_device structs */ priv = uf_alloc_netdevice(sdio_dev, bus_id); if (priv == NULL) { unifi_error(priv, "Failed to allocate driver private\n"); goto failed0; } priv->unifi_device = dev; SET_NETDEV_DEV(priv->netdev[0], dev); /* We are not ready to send data yet. */ netif_carrier_off(priv->netdev[0]); /* Allocate driver context. */ priv->card = unifi_alloc_card(priv->sdio, priv); if (priv->card == NULL) { unifi_error(priv, "Failed to allocate UniFi driver card struct.\n"); goto failed1; } if (Unifi_instances[bus_id]) { unifi_error(priv, "Internal error: instance for slot %d is already taken\n", bus_id); } Unifi_instances[bus_id] = priv; In_use[bus_id] = UNIFI_DEV_IN_USE; /* Save the netdev_priv for use by the netdev event callback mechanism */ Unifi_netdev_instances[bus_id * CSR_WIFI_NUM_INTERFACES] = netdev_priv(priv->netdev[0]); /* Initialise the mini-coredump capture buffers */ csrResult = unifi_coredump_init(priv->card, (u16)coredump_max); if (csrResult != CSR_RESULT_SUCCESS) { unifi_error(priv, "Couldn't allocate mini-coredump buffers\n"); } /* Create the character device nodes */ r = uf_create_device_nodes(priv, bus_id); if (r) { goto failed1; } /* * We use the slot number as unifi device index. */ scnprintf(priv->proc_entry_name, 64, "driver/unifi%d", priv->instance); /* * The following complex casting is in place in order to eliminate 64-bit compilation warning * "cast to/from pointer from/to integer of different size" */ if (!proc_create_data(priv->proc_entry_name, 0, NULL, &uf_proc_fops, (void *)(long)priv->instance)) { unifi_error(priv, "unifi: can't create /proc/driver/unifi\n"); } /* Allocate the net_device for interfaces other than 0. */ { int i; priv->totalInterfaceCount =0; for(i=1;inetdev[i], dev); /* We are not ready to send data yet. */ netif_carrier_off(priv->netdev[i]); /* Save the netdev_priv for use by the netdev event callback mechanism */ Unifi_netdev_instances[bus_id * CSR_WIFI_NUM_INTERFACES + i] = netdev_priv(priv->netdev[i]); } } for(i=0;iinterfacePriv[i]; interfacePriv->netdev_registered=0; } } #ifdef CSR_WIFI_RX_PATH_SPLIT if (signal_buffer_init(priv, CSR_WIFI_RX_SIGNAL_BUFFER_SIZE)) { unifi_error(priv,"Failed to allocate shared memory for T-H signals\n"); goto failed2; } priv->rx_workqueue = create_singlethread_workqueue("rx_workq"); if (priv->rx_workqueue == NULL) { unifi_error(priv,"create_singlethread_workqueue failed \n"); goto failed3; } INIT_WORK(&priv->rx_work_struct, rx_wq_handler); #endif #ifdef CSR_WIFI_HIP_DEBUG_OFFLINE if (log_hip_signals) { uf_register_hip_offline_debug(priv); } #endif /* Initialise the SME related threads and parameters */ r = uf_sme_init(priv); if (r) { unifi_error(priv, "SME initialisation failed.\n"); goto failed4; } /* * Run the userspace helper program (unififw) to perform * the device initialisation. */ unifi_trace(priv, UDBG1, "run UniFi helper app...\n"); r = uf_run_unifihelper(priv); if (r) { unifi_notice(priv, "unable to run UniFi helper app\n"); /* Not a fatal error. */ } up(&Unifi_instance_mutex); return priv; failed4: #ifdef CSR_WIFI_HIP_DEBUG_OFFLINE if (log_hip_signals) { uf_unregister_hip_offline_debug(priv); } #endif #ifdef CSR_WIFI_RX_PATH_SPLIT flush_workqueue(priv->rx_workqueue); destroy_workqueue(priv->rx_workqueue); failed3: signal_buffer_free(priv,CSR_WIFI_RX_SIGNAL_BUFFER_SIZE); failed2: #endif /* Remove the device nodes */ uf_destroy_device_nodes(priv); failed1: /* Deregister priv->netdev_client */ ul_deregister_client(priv->netdev_client); failed0: if (priv && priv->card) { unifi_coredump_free(priv->card); unifi_free_card(priv->card); } if (priv) { uf_free_netdevice(priv); } up(&Unifi_instance_mutex); return NULL; } /* register_unifi_sdio() */ /* * --------------------------------------------------------------------------- * ask_unifi_sdio_cleanup * * We can not free our private context, until all the char device * clients have closed the file handles. unregister_unifi_sdio() which * is called when a card is removed, waits on Unifi_cleanup_wq until * the reference count becomes zero. It is time to wake it up now. * * Arguments: * priv Pointer to driver context. * * Returns: * None. * --------------------------------------------------------------------------- */ static void ask_unifi_sdio_cleanup(unifi_priv_t *priv) { /* * Now clear the flag that says the old instance is in use. * This is used to prevent a new instance being started before old * one has finshed closing down, for example if bounce makes the card * appear to be ejected and re-inserted quickly. */ In_use[priv->instance] = UNIFI_DEV_CLEANUP; unifi_trace(NULL, UDBG5, "ask_unifi_sdio_cleanup: wake up cleanup workqueue.\n"); wake_up(&Unifi_cleanup_wq); } /* ask_unifi_sdio_cleanup() */ /* * --------------------------------------------------------------------------- * cleanup_unifi_sdio * * Release any resources owned by a unifi instance. * * Arguments: * priv Pointer to the instance to free. * * Returns: * None. * --------------------------------------------------------------------------- */ static void cleanup_unifi_sdio(unifi_priv_t *priv) { int priv_instance; int i; static const CsrWifiMacAddress broadcast_address = {{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}; /* Remove the device nodes */ uf_destroy_device_nodes(priv); /* Mark this device as gone away by NULLing the entry in Unifi_instances */ Unifi_instances[priv->instance] = NULL; unifi_trace(priv, UDBG5, "cleanup_unifi_sdio: remove_proc_entry\n"); /* * Free the children of priv before unifi_free_netdevice() frees * the priv struct */ remove_proc_entry(priv->proc_entry_name, 0); /* Unregister netdev as a client. */ if (priv->netdev_client) { unifi_trace(priv, UDBG2, "Netdev client (id:%d s:0x%X) is unregistered\n", priv->netdev_client->client_id, priv->netdev_client->sender_id); ul_deregister_client(priv->netdev_client); } /* Destroy the SME related threads and parameters */ uf_sme_deinit(priv); #ifdef CSR_SME_USERSPACE priv->smepriv = NULL; #endif #ifdef CSR_WIFI_HIP_DEBUG_OFFLINE if (log_hip_signals) { uf_unregister_hip_offline_debug(priv); } #endif /* Free any packets left in the Rx queues */ for(i=0;icard) { unifi_trace(priv, UDBG5, "cleanup_unifi_sdio: free card\n"); unifi_coredump_free(priv->card); unifi_free_card(priv->card); priv->card = NULL; } /* * Unregister the network device. * We can not unregister the netdev before we release * all pending packets in the core. */ uf_unregister_netdev(priv); priv->totalInterfaceCount = 0; /* Clear the table of registered netdev_priv's */ for (i = 0; i < CSR_WIFI_NUM_INTERFACES; i++) { Unifi_netdev_instances[priv->instance * CSR_WIFI_NUM_INTERFACES + i] = NULL; } unifi_trace(priv, UDBG5, "cleanup_unifi_sdio: uf_free_netdevice\n"); /* * When uf_free_netdevice() returns, the priv is invalid * so we need to remember the instance to clear the global flag later. */ priv_instance = priv->instance; #ifdef CSR_WIFI_RX_PATH_SPLIT flush_workqueue(priv->rx_workqueue); destroy_workqueue(priv->rx_workqueue); signal_buffer_free(priv,CSR_WIFI_RX_SIGNAL_BUFFER_SIZE); #endif /* Priv is freed as part of the net_device */ uf_free_netdevice(priv); /* * Now clear the flag that says the old instance is in use. * This is used to prevent a new instance being started before old * one has finshed closing down, for example if bounce makes the card * appear to be ejected and re-inserted quickly. */ In_use[priv_instance] = UNIFI_DEV_NOT_IN_USE; unifi_trace(NULL, UDBG5, "cleanup_unifi_sdio: DONE.\n"); } /* cleanup_unifi_sdio() */ /* * --------------------------------------------------------------------------- * unregister_unifi_sdio * * Call from SDIO driver when it detects that UniFi has been removed. * * Arguments: * bus_id Number of the card that was ejected. * * Returns: * None. * --------------------------------------------------------------------------- */ static void unregister_unifi_sdio(int bus_id) { unifi_priv_t *priv; int interfaceTag=0; u8 reason = CONFIG_IND_EXIT; if ((bus_id < 0) || (bus_id >= MAX_UNIFI_DEVS)) { unifi_error(NULL, "unregister_unifi_sdio: invalid device %d\n", bus_id); return; } priv = Unifi_instances[bus_id]; if (priv == NULL) { unifi_error(priv, "unregister_unifi_sdio: device %d is not registered\n", bus_id); return; } /* Stop the network traffic before freeing the core. */ for(interfaceTag=0;interfaceTagtotalInterfaceCount;interfaceTag++) { netInterface_priv_t *interfacePriv = priv->interfacePriv[interfaceTag]; if(interfacePriv->netdev_registered) { netif_carrier_off(priv->netdev[interfaceTag]); netif_tx_stop_all_queues(priv->netdev[interfaceTag]); } } #ifdef CSR_NATIVE_LINUX /* * If the unifi thread was started, signal it to stop. This * should cause any userspace processes with open unifi device to * close them. */ uf_stop_thread(priv, &priv->bh_thread); /* Unregister the interrupt handler */ if (csr_sdio_linux_remove_irq(priv->sdio)) { unifi_notice(priv, "csr_sdio_linux_remove_irq failed to talk to card.\n"); } /* Ensure no MLME functions are waiting on a the mlme_event semaphore. */ uf_abort_mlme(priv); #endif /* CSR_NATIVE_LINUX */ ul_log_config_ind(priv, &reason, sizeof(u8)); /* Deregister the UDI hook from the core. */ unifi_remove_udi_hook(priv->card, logging_handler); uf_put_instance(bus_id); /* * Wait until the device is cleaned up. i.e., when all userspace * processes have closed any open unifi devices. */ wait_event(Unifi_cleanup_wq, In_use[bus_id] == UNIFI_DEV_CLEANUP); unifi_trace(NULL, UDBG5, "Received clean up event\n"); /* Now we can free the private context and the char device nodes */ cleanup_unifi_sdio(priv); } /* unregister_unifi_sdio() */ /* * --------------------------------------------------------------------------- * uf_find_instance * * Find the context structure for a given UniFi device instance. * * Arguments: * inst The instance number to look for. * * Returns: * None. * --------------------------------------------------------------------------- */ unifi_priv_t * uf_find_instance(int inst) { if ((inst < 0) || (inst >= MAX_UNIFI_DEVS)) { return NULL; } return Unifi_instances[inst]; } /* uf_find_instance() */ /* * --------------------------------------------------------------------------- * uf_find_priv * * Find the device instance for a given context structure. * * Arguments: * priv The context structure pointer to look for. * * Returns: * index of instance, -1 otherwise. * --------------------------------------------------------------------------- */ int uf_find_priv(unifi_priv_t *priv) { int inst; if (!priv) { return -1; } for (inst = 0; inst < MAX_UNIFI_DEVS; inst++) { if (Unifi_instances[inst] == priv) { return inst; } } return -1; } /* uf_find_priv() */ /* * --------------------------------------------------------------------------- * uf_find_netdev_priv * * Find the device instance for a given netdev context structure. * * Arguments: * priv The context structure pointer to look for. * * Returns: * index of instance, -1 otherwise. * --------------------------------------------------------------------------- */ int uf_find_netdev_priv(netInterface_priv_t *priv) { int inst; if (!priv) { return -1; } for (inst = 0; inst < MAX_UNIFI_DEVS * CSR_WIFI_NUM_INTERFACES; inst++) { if (Unifi_netdev_instances[inst] == priv) { return inst; } } return -1; } /* uf_find_netdev_priv() */ /* * --------------------------------------------------------------------------- * uf_get_instance * * Find the context structure for a given UniFi device instance * and increment the reference count. * * Arguments: * inst The instance number to look for. * * Returns: * Pointer to the instance or NULL if no instance exists. * --------------------------------------------------------------------------- */ unifi_priv_t * uf_get_instance(int inst) { unifi_priv_t *priv; down(&Unifi_instance_mutex); priv = uf_find_instance(inst); if (priv) { priv->ref_count++; } up(&Unifi_instance_mutex); return priv; } /* * --------------------------------------------------------------------------- * uf_put_instance * * Decrement the context reference count, freeing resources and * shutting down the driver when the count reaches zero. * * Arguments: * inst The instance number to look for. * * Returns: * Pointer to the instance or NULL if no instance exists. * --------------------------------------------------------------------------- */ void uf_put_instance(int inst) { unifi_priv_t *priv; down(&Unifi_instance_mutex); priv = uf_find_instance(inst); if (priv) { priv->ref_count--; if (priv->ref_count == 0) { ask_unifi_sdio_cleanup(priv); } } up(&Unifi_instance_mutex); } /* * --------------------------------------------------------------------------- * uf_proc_show * * Read method for driver node in /proc/driver/unifi0 * * Arguments: * page * start * offset * count * eof * data * * Returns: * None. * --------------------------------------------------------------------------- */ #ifdef CONFIG_PROC_FS static int uf_proc_show(struct seq_file *m, void *v) { unifi_priv_t *priv; int i; /* * The following complex casting is in place in order to eliminate * 64-bit compilation warning "cast to/from pointer from/to integer of * different size" */ priv = uf_find_instance((long)m->private); if (!priv) return 0; seq_printf(m, "UniFi SDIO Driver: %s %s %s\n", CSR_WIFI_VERSION, __DATE__, __TIME__); #ifdef CSR_SME_USERSPACE seq_puts(m, "SME: CSR userspace "); #ifdef CSR_SUPPORT_WEXT seq_puts(m, "with WEXT support\n"); #else seq_putc(m, '\n'); #endif /* CSR_SUPPORT_WEXT */ #endif /* CSR_SME_USERSPACE */ #ifdef CSR_NATIVE_LINUX seq_puts(m, "SME: native\n"); #endif #ifdef CSR_SUPPORT_SME seq_printf(m, "Firmware (ROM) build:%u, Patch:%u\n", priv->card_info.fw_build, priv->sme_versions.firmwarePatch); #endif unifi_print_status(priv->card, m); seq_printf(m, "Last dbg str: %s\n", priv->last_debug_string); seq_puts(m, "Last dbg16:"); for (i = 0; i < 8; i++) seq_printf(m, " %04X", priv->last_debug_word16[i]); seq_putc(m, '\n'); seq_puts(m, " "); for (; i < 16; i++) seq_printf(m, " %04X", priv->last_debug_word16[i]); seq_putc(m, '\n'); return 0; } #endif static void uf_lx_suspend(CsrSdioFunction *sdio_ctx) { unifi_priv_t *priv = sdio_ctx->driverData; unifi_suspend(priv); CsrSdioSuspendAcknowledge(sdio_ctx, CSR_RESULT_SUCCESS); } static void uf_lx_resume(CsrSdioFunction *sdio_ctx) { unifi_priv_t *priv = sdio_ctx->driverData; unifi_resume(priv); CsrSdioResumeAcknowledge(sdio_ctx, CSR_RESULT_SUCCESS); } static int active_slot = MAX_UNIFI_DEVS; static struct device *os_devices[MAX_UNIFI_DEVS]; void uf_add_os_device(int bus_id, struct device *os_device) { if ((bus_id < 0) || (bus_id >= MAX_UNIFI_DEVS)) { unifi_error(NULL, "uf_add_os_device: invalid device %d\n", bus_id); return; } active_slot = bus_id; os_devices[bus_id] = os_device; } /* uf_add_os_device() */ void uf_remove_os_device(int bus_id) { if ((bus_id < 0) || (bus_id >= MAX_UNIFI_DEVS)) { unifi_error(NULL, "uf_remove_os_device: invalid device %d\n", bus_id); return; } active_slot = bus_id; os_devices[bus_id] = NULL; } /* uf_remove_os_device() */ static void uf_sdio_inserted(CsrSdioFunction *sdio_ctx) { unifi_priv_t *priv; unifi_trace(NULL, UDBG5, "uf_sdio_inserted(0x%p), slot_id=%d, dev=%p\n", sdio_ctx, active_slot, os_devices[active_slot]); priv = register_unifi_sdio(sdio_ctx, active_slot, os_devices[active_slot]); if (priv == NULL) { CsrSdioInsertedAcknowledge(sdio_ctx, CSR_RESULT_FAILURE); return; } sdio_ctx->driverData = priv; CsrSdioInsertedAcknowledge(sdio_ctx, CSR_RESULT_SUCCESS); } /* uf_sdio_inserted() */ static void uf_sdio_removed(CsrSdioFunction *sdio_ctx) { unregister_unifi_sdio(active_slot); CsrSdioRemovedAcknowledge(sdio_ctx); } /* uf_sdio_removed() */ static void uf_sdio_dsr_handler(CsrSdioFunction *sdio_ctx) { unifi_priv_t *priv = sdio_ctx->driverData; unifi_sdio_interrupt_handler(priv->card); } /* uf_sdio_dsr_handler() */ /* * --------------------------------------------------------------------------- * uf_sdio_int_handler * * Interrupt callback function for SDIO interrupts. * This is called in kernel context (i.e. not interrupt context). * We retrieve the unifi context pointer and call the main UniFi * interrupt handler. * * Arguments: * fdev SDIO context pointer * * Returns: * None. * --------------------------------------------------------------------------- */ static CsrSdioInterruptDsrCallback uf_sdio_int_handler(CsrSdioFunction *sdio_ctx) { return uf_sdio_dsr_handler; } /* uf_sdio_int_handler() */ static CsrSdioFunctionId unifi_ids[] = { { .manfId = SDIO_MANF_ID_CSR, .cardId = SDIO_CARD_ID_UNIFI_3, .sdioFunction = SDIO_WLAN_FUNC_ID_UNIFI_3, .sdioInterface = CSR_SDIO_ANY_SDIO_INTERFACE, }, { .manfId = SDIO_MANF_ID_CSR, .cardId = SDIO_CARD_ID_UNIFI_4, .sdioFunction = SDIO_WLAN_FUNC_ID_UNIFI_4, .sdioInterface = CSR_SDIO_ANY_SDIO_INTERFACE, } }; /* * Structure to register with the glue layer. */ static CsrSdioFunctionDriver unifi_sdioFunction_drv = { .inserted = uf_sdio_inserted, .removed = uf_sdio_removed, .intr = uf_sdio_int_handler, .suspend = uf_lx_suspend, .resume = uf_lx_resume, .ids = unifi_ids, .idsCount = sizeof(unifi_ids) / sizeof(unifi_ids[0]) }; /* * --------------------------------------------------------------------------- * uf_sdio_load * uf_sdio_unload * * These functions are called from the main module load and unload * functions. They perform the appropriate operations for the monolithic * driver. * * Arguments: * None. * * Returns: * None. * --------------------------------------------------------------------------- */ int __init uf_sdio_load(void) { CsrResult csrResult; csrResult = CsrSdioFunctionDriverRegister(&unifi_sdioFunction_drv); if (csrResult != CSR_RESULT_SUCCESS) { unifi_error(NULL, "Failed to register UniFi SDIO driver: csrResult=%d\n", csrResult); return -EIO; } return 0; } /* uf_sdio_load() */ void __exit uf_sdio_unload(void) { CsrSdioFunctionDriverUnregister(&unifi_sdioFunction_drv); } /* uf_sdio_unload() */