| /** |
| * Copyright (c) 2014 Redpine Signals Inc. |
| * |
| * Permission to use, copy, modify, and/or distribute this software for any |
| * purpose with or without fee is hereby granted, provided that the above |
| * copyright notice and this permission notice appear in all copies. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
| * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
| * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
| * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| * |
| */ |
| |
| #include <linux/module.h> |
| #include "rsi_usb.h" |
| |
| /** |
| * rsi_usb_card_write() - This function writes to the USB Card. |
| * @adapter: Pointer to the adapter structure. |
| * @buf: Pointer to the buffer from where the data has to be taken. |
| * @len: Length to be written. |
| * @endpoint: Type of endpoint. |
| * |
| * Return: status: 0 on success, a negative error code on failure. |
| */ |
| static int rsi_usb_card_write(struct rsi_hw *adapter, |
| void *buf, |
| u16 len, |
| u8 endpoint) |
| { |
| struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; |
| int status; |
| s32 transfer; |
| |
| status = usb_bulk_msg(dev->usbdev, |
| usb_sndbulkpipe(dev->usbdev, |
| dev->bulkout_endpoint_addr[endpoint - 1]), |
| buf, |
| len, |
| &transfer, |
| HZ * 5); |
| |
| if (status < 0) { |
| rsi_dbg(ERR_ZONE, |
| "Card write failed with error code :%10d\n", status); |
| dev->write_fail = 1; |
| } |
| return status; |
| } |
| |
| /** |
| * rsi_write_multiple() - This function writes multiple bytes of information |
| * to the USB card. |
| * @adapter: Pointer to the adapter structure. |
| * @addr: Address of the register. |
| * @data: Pointer to the data that has to be written. |
| * @count: Number of multiple bytes to be written. |
| * |
| * Return: 0 on success, a negative error code on failure. |
| */ |
| static int rsi_write_multiple(struct rsi_hw *adapter, |
| u8 endpoint, |
| u8 *data, |
| u32 count) |
| { |
| struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; |
| u8 *seg = dev->tx_buffer; |
| |
| if (dev->write_fail) |
| return 0; |
| |
| if (endpoint == MGMT_EP) { |
| memset(seg, 0, RSI_USB_TX_HEAD_ROOM); |
| memcpy(seg + RSI_USB_TX_HEAD_ROOM, data, count); |
| } else { |
| seg = ((u8 *)data - RSI_USB_TX_HEAD_ROOM); |
| } |
| |
| return rsi_usb_card_write(adapter, |
| seg, |
| count + RSI_USB_TX_HEAD_ROOM, |
| endpoint); |
| } |
| |
| /** |
| * rsi_find_bulk_in_and_out_endpoints() - This function initializes the bulk |
| * endpoints to the device. |
| * @interface: Pointer to the USB interface structure. |
| * @adapter: Pointer to the adapter structure. |
| * |
| * Return: ret_val: 0 on success, -ENOMEM on failure. |
| */ |
| static int rsi_find_bulk_in_and_out_endpoints(struct usb_interface *interface, |
| struct rsi_hw *adapter) |
| { |
| struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; |
| struct usb_host_interface *iface_desc; |
| struct usb_endpoint_descriptor *endpoint; |
| __le16 buffer_size; |
| int ii, bep_found = 0; |
| |
| iface_desc = &(interface->altsetting[0]); |
| |
| for (ii = 0; ii < iface_desc->desc.bNumEndpoints; ++ii) { |
| endpoint = &(iface_desc->endpoint[ii].desc); |
| |
| if ((!(dev->bulkin_endpoint_addr)) && |
| (endpoint->bEndpointAddress & USB_DIR_IN) && |
| ((endpoint->bmAttributes & |
| USB_ENDPOINT_XFERTYPE_MASK) == |
| USB_ENDPOINT_XFER_BULK)) { |
| buffer_size = endpoint->wMaxPacketSize; |
| dev->bulkin_size = buffer_size; |
| dev->bulkin_endpoint_addr = |
| endpoint->bEndpointAddress; |
| } |
| |
| if (!dev->bulkout_endpoint_addr[bep_found] && |
| !(endpoint->bEndpointAddress & USB_DIR_IN) && |
| ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == |
| USB_ENDPOINT_XFER_BULK)) { |
| dev->bulkout_endpoint_addr[bep_found] = |
| endpoint->bEndpointAddress; |
| buffer_size = endpoint->wMaxPacketSize; |
| dev->bulkout_size[bep_found] = buffer_size; |
| bep_found++; |
| } |
| |
| if (bep_found >= MAX_BULK_EP) |
| break; |
| } |
| |
| if (!(dev->bulkin_endpoint_addr) && |
| (dev->bulkout_endpoint_addr[0])) |
| return -EINVAL; |
| |
| return 0; |
| } |
| |
| /* rsi_usb_reg_read() - This function reads data from given register address. |
| * @usbdev: Pointer to the usb_device structure. |
| * @reg: Address of the register to be read. |
| * @value: Value to be read. |
| * @len: length of data to be read. |
| * |
| * Return: status: 0 on success, a negative error code on failure. |
| */ |
| static int rsi_usb_reg_read(struct usb_device *usbdev, |
| u32 reg, |
| u16 *value, |
| u16 len) |
| { |
| u8 *buf; |
| int status = -ENOMEM; |
| |
| buf = kmalloc(0x04, GFP_KERNEL); |
| if (!buf) |
| return status; |
| |
| status = usb_control_msg(usbdev, |
| usb_rcvctrlpipe(usbdev, 0), |
| USB_VENDOR_REGISTER_READ, |
| USB_TYPE_VENDOR, |
| ((reg & 0xffff0000) >> 16), (reg & 0xffff), |
| (void *)buf, |
| len, |
| HZ * 5); |
| |
| *value = (buf[0] | (buf[1] << 8)); |
| if (status < 0) { |
| rsi_dbg(ERR_ZONE, |
| "%s: Reg read failed with error code :%d\n", |
| __func__, status); |
| } |
| kfree(buf); |
| |
| return status; |
| } |
| |
| /** |
| * rsi_usb_reg_write() - This function writes the given data into the given |
| * register address. |
| * @usbdev: Pointer to the usb_device structure. |
| * @reg: Address of the register. |
| * @value: Value to write. |
| * @len: Length of data to be written. |
| * |
| * Return: status: 0 on success, a negative error code on failure. |
| */ |
| static int rsi_usb_reg_write(struct usb_device *usbdev, |
| u32 reg, |
| u16 value, |
| u16 len) |
| { |
| u8 *usb_reg_buf; |
| int status = -ENOMEM; |
| |
| usb_reg_buf = kmalloc(0x04, GFP_KERNEL); |
| if (!usb_reg_buf) |
| return status; |
| |
| usb_reg_buf[0] = (value & 0x00ff); |
| usb_reg_buf[1] = (value & 0xff00) >> 8; |
| usb_reg_buf[2] = 0x0; |
| usb_reg_buf[3] = 0x0; |
| |
| status = usb_control_msg(usbdev, |
| usb_sndctrlpipe(usbdev, 0), |
| USB_VENDOR_REGISTER_WRITE, |
| USB_TYPE_VENDOR, |
| ((reg & 0xffff0000) >> 16), |
| (reg & 0xffff), |
| (void *)usb_reg_buf, |
| len, |
| HZ * 5); |
| if (status < 0) { |
| rsi_dbg(ERR_ZONE, |
| "%s: Reg write failed with error code :%d\n", |
| __func__, status); |
| } |
| kfree(usb_reg_buf); |
| |
| return status; |
| } |
| |
| /** |
| * rsi_rx_done_handler() - This function is called when a packet is received |
| * from USB stack. This is callback to recieve done. |
| * @urb: Received URB. |
| * |
| * Return: None. |
| */ |
| static void rsi_rx_done_handler(struct urb *urb) |
| { |
| struct rsi_hw *adapter = urb->context; |
| struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; |
| |
| if (urb->status) |
| return; |
| |
| rsi_set_event(&dev->rx_thread.event); |
| } |
| |
| /** |
| * rsi_rx_urb_submit() - This function submits the given URB to the USB stack. |
| * @adapter: Pointer to the adapter structure. |
| * |
| * Return: 0 on success, a negative error code on failure. |
| */ |
| static int rsi_rx_urb_submit(struct rsi_hw *adapter) |
| { |
| struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; |
| struct urb *urb = dev->rx_usb_urb[0]; |
| int status; |
| |
| usb_fill_bulk_urb(urb, |
| dev->usbdev, |
| usb_rcvbulkpipe(dev->usbdev, |
| dev->bulkin_endpoint_addr), |
| urb->transfer_buffer, |
| 3000, |
| rsi_rx_done_handler, |
| adapter); |
| |
| status = usb_submit_urb(urb, GFP_KERNEL); |
| if (status) |
| rsi_dbg(ERR_ZONE, "%s: Failed in urb submission\n", __func__); |
| |
| return status; |
| } |
| |
| /** |
| * rsi_usb_write_register_multiple() - This function writes multiple bytes of |
| * information to multiple registers. |
| * @adapter: Pointer to the adapter structure. |
| * @addr: Address of the register. |
| * @data: Pointer to the data that has to be written. |
| * @count: Number of multiple bytes to be written on to the registers. |
| * |
| * Return: status: 0 on success, a negative error code on failure. |
| */ |
| int rsi_usb_write_register_multiple(struct rsi_hw *adapter, |
| u32 addr, |
| u8 *data, |
| u32 count) |
| { |
| struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; |
| u8 *buf; |
| u8 transfer; |
| int status = 0; |
| |
| buf = kzalloc(4096, GFP_KERNEL); |
| if (!buf) |
| return -ENOMEM; |
| |
| while (count) { |
| transfer = (u8)(min_t(u32, count, 4096)); |
| memcpy(buf, data, transfer); |
| status = usb_control_msg(dev->usbdev, |
| usb_sndctrlpipe(dev->usbdev, 0), |
| USB_VENDOR_REGISTER_WRITE, |
| USB_TYPE_VENDOR, |
| ((addr & 0xffff0000) >> 16), |
| (addr & 0xffff), |
| (void *)buf, |
| transfer, |
| HZ * 5); |
| if (status < 0) { |
| rsi_dbg(ERR_ZONE, |
| "Reg write failed with error code :%d\n", |
| status); |
| } else { |
| count -= transfer; |
| data += transfer; |
| addr += transfer; |
| } |
| } |
| |
| kfree(buf); |
| return 0; |
| } |
| |
| /** |
| *rsi_usb_host_intf_write_pkt() - This function writes the packet to the |
| * USB card. |
| * @adapter: Pointer to the adapter structure. |
| * @pkt: Pointer to the data to be written on to the card. |
| * @len: Length of the data to be written on to the card. |
| * |
| * Return: 0 on success, a negative error code on failure. |
| */ |
| static int rsi_usb_host_intf_write_pkt(struct rsi_hw *adapter, |
| u8 *pkt, |
| u32 len) |
| { |
| u32 queueno = ((pkt[1] >> 4) & 0xf); |
| u8 endpoint; |
| |
| endpoint = ((queueno == RSI_WIFI_MGMT_Q) ? MGMT_EP : DATA_EP); |
| |
| return rsi_write_multiple(adapter, |
| endpoint, |
| (u8 *)pkt, |
| len); |
| } |
| |
| /** |
| * rsi_deinit_usb_interface() - This function deinitializes the usb interface. |
| * @adapter: Pointer to the adapter structure. |
| * |
| * Return: None. |
| */ |
| static void rsi_deinit_usb_interface(struct rsi_hw *adapter) |
| { |
| struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; |
| |
| rsi_kill_thread(&dev->rx_thread); |
| usb_free_urb(dev->rx_usb_urb[0]); |
| kfree(adapter->priv->rx_data_pkt); |
| kfree(dev->tx_buffer); |
| } |
| |
| /** |
| * rsi_init_usb_interface() - This function initializes the usb interface. |
| * @adapter: Pointer to the adapter structure. |
| * @pfunction: Pointer to USB interface structure. |
| * |
| * Return: 0 on success, a negative error code on failure. |
| */ |
| static int rsi_init_usb_interface(struct rsi_hw *adapter, |
| struct usb_interface *pfunction) |
| { |
| struct rsi_91x_usbdev *rsi_dev; |
| struct rsi_common *common = adapter->priv; |
| int status; |
| |
| rsi_dev = kzalloc(sizeof(*rsi_dev), GFP_KERNEL); |
| if (!rsi_dev) |
| return -ENOMEM; |
| |
| adapter->rsi_dev = rsi_dev; |
| rsi_dev->usbdev = interface_to_usbdev(pfunction); |
| |
| if (rsi_find_bulk_in_and_out_endpoints(pfunction, adapter)) |
| return -EINVAL; |
| |
| adapter->device = &pfunction->dev; |
| usb_set_intfdata(pfunction, adapter); |
| |
| common->rx_data_pkt = kmalloc(2048, GFP_KERNEL); |
| if (!common->rx_data_pkt) { |
| rsi_dbg(ERR_ZONE, "%s: Failed to allocate memory\n", |
| __func__); |
| return -ENOMEM; |
| } |
| |
| rsi_dev->tx_buffer = kmalloc(2048, GFP_KERNEL); |
| if (!rsi_dev->tx_buffer) { |
| status = -ENOMEM; |
| goto fail_tx; |
| } |
| rsi_dev->rx_usb_urb[0] = usb_alloc_urb(0, GFP_KERNEL); |
| if (!rsi_dev->rx_usb_urb[0]) { |
| status = -ENOMEM; |
| goto fail_rx; |
| } |
| rsi_dev->rx_usb_urb[0]->transfer_buffer = adapter->priv->rx_data_pkt; |
| rsi_dev->tx_blk_size = 252; |
| |
| /* Initializing function callbacks */ |
| adapter->rx_urb_submit = rsi_rx_urb_submit; |
| adapter->host_intf_write_pkt = rsi_usb_host_intf_write_pkt; |
| adapter->check_hw_queue_status = rsi_usb_check_queue_status; |
| adapter->determine_event_timeout = rsi_usb_event_timeout; |
| |
| rsi_init_event(&rsi_dev->rx_thread.event); |
| status = rsi_create_kthread(common, &rsi_dev->rx_thread, |
| rsi_usb_rx_thread, "RX-Thread"); |
| if (status) { |
| rsi_dbg(ERR_ZONE, "%s: Unable to init rx thrd\n", __func__); |
| goto fail_thread; |
| } |
| |
| #ifdef CONFIG_RSI_DEBUGFS |
| /* In USB, one less than the MAX_DEBUGFS_ENTRIES entries is required */ |
| adapter->num_debugfs_entries = (MAX_DEBUGFS_ENTRIES - 1); |
| #endif |
| |
| rsi_dbg(INIT_ZONE, "%s: Enabled the interface\n", __func__); |
| return 0; |
| |
| fail_thread: |
| usb_free_urb(rsi_dev->rx_usb_urb[0]); |
| fail_rx: |
| kfree(rsi_dev->tx_buffer); |
| fail_tx: |
| kfree(common->rx_data_pkt); |
| return status; |
| } |
| |
| /** |
| * rsi_probe() - This function is called by kernel when the driver provided |
| * Vendor and device IDs are matched. All the initialization |
| * work is done here. |
| * @pfunction: Pointer to the USB interface structure. |
| * @id: Pointer to the usb_device_id structure. |
| * |
| * Return: 0 on success, a negative error code on failure. |
| */ |
| static int rsi_probe(struct usb_interface *pfunction, |
| const struct usb_device_id *id) |
| { |
| struct rsi_hw *adapter; |
| struct rsi_91x_usbdev *dev; |
| u16 fw_status; |
| int status; |
| |
| rsi_dbg(INIT_ZONE, "%s: Init function called\n", __func__); |
| |
| adapter = rsi_91x_init(); |
| if (!adapter) { |
| rsi_dbg(ERR_ZONE, "%s: Failed to init os intf ops\n", |
| __func__); |
| return -ENOMEM; |
| } |
| |
| status = rsi_init_usb_interface(adapter, pfunction); |
| if (status) { |
| rsi_dbg(ERR_ZONE, "%s: Failed to init usb interface\n", |
| __func__); |
| goto err; |
| } |
| |
| rsi_dbg(ERR_ZONE, "%s: Initialized os intf ops\n", __func__); |
| |
| dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; |
| |
| status = rsi_usb_reg_read(dev->usbdev, FW_STATUS_REG, &fw_status, 2); |
| if (status) |
| goto err1; |
| else |
| fw_status &= 1; |
| |
| if (!fw_status) { |
| status = rsi_usb_device_init(adapter->priv); |
| if (status) { |
| rsi_dbg(ERR_ZONE, "%s: Failed in device init\n", |
| __func__); |
| goto err1; |
| } |
| |
| status = rsi_usb_reg_write(dev->usbdev, |
| USB_INTERNAL_REG_1, |
| RSI_USB_READY_MAGIC_NUM, 1); |
| if (status) |
| goto err1; |
| rsi_dbg(INIT_ZONE, "%s: Performed device init\n", __func__); |
| } |
| |
| status = rsi_rx_urb_submit(adapter); |
| if (status) |
| goto err1; |
| |
| return 0; |
| err1: |
| rsi_deinit_usb_interface(adapter); |
| err: |
| rsi_91x_deinit(adapter); |
| rsi_dbg(ERR_ZONE, "%s: Failed in probe...Exiting\n", __func__); |
| return status; |
| } |
| |
| /** |
| * rsi_disconnect() - This function performs the reverse of the probe function, |
| * it deintialize the driver structure. |
| * @pfunction: Pointer to the USB interface structure. |
| * |
| * Return: None. |
| */ |
| static void rsi_disconnect(struct usb_interface *pfunction) |
| { |
| struct rsi_hw *adapter = usb_get_intfdata(pfunction); |
| |
| if (!adapter) |
| return; |
| |
| rsi_mac80211_detach(adapter); |
| rsi_deinit_usb_interface(adapter); |
| rsi_91x_deinit(adapter); |
| |
| rsi_dbg(INFO_ZONE, "%s: Deinitialization completed\n", __func__); |
| } |
| |
| #ifdef CONFIG_PM |
| static int rsi_suspend(struct usb_interface *intf, pm_message_t message) |
| { |
| /* Not yet implemented */ |
| return -ENOSYS; |
| } |
| |
| static int rsi_resume(struct usb_interface *intf) |
| { |
| /* Not yet implemented */ |
| return -ENOSYS; |
| } |
| #endif |
| |
| static const struct usb_device_id rsi_dev_table[] = { |
| { USB_DEVICE(0x0303, 0x0100) }, |
| { USB_DEVICE(0x041B, 0x0301) }, |
| { USB_DEVICE(0x041B, 0x0201) }, |
| { USB_DEVICE(0x041B, 0x9330) }, |
| { /* Blank */}, |
| }; |
| |
| static struct usb_driver rsi_driver = { |
| .name = "RSI-USB WLAN", |
| .probe = rsi_probe, |
| .disconnect = rsi_disconnect, |
| .id_table = rsi_dev_table, |
| #ifdef CONFIG_PM |
| .suspend = rsi_suspend, |
| .resume = rsi_resume, |
| #endif |
| }; |
| |
| module_usb_driver(rsi_driver); |
| |
| MODULE_AUTHOR("Redpine Signals Inc"); |
| MODULE_DESCRIPTION("Common USB layer for RSI drivers"); |
| MODULE_SUPPORTED_DEVICE("RSI-91x"); |
| MODULE_DEVICE_TABLE(usb, rsi_dev_table); |
| MODULE_FIRMWARE(FIRMWARE_RSI9113); |
| MODULE_VERSION("0.1"); |
| MODULE_LICENSE("Dual BSD/GPL"); |