1f21e6c58SLee Jones /*
2dad0d04fSFariya Fatima  * Copyright (c) 2014 Redpine Signals Inc.
3dad0d04fSFariya Fatima  *
4dad0d04fSFariya Fatima  * Permission to use, copy, modify, and/or distribute this software for any
5dad0d04fSFariya Fatima  * purpose with or without fee is hereby granted, provided that the above
6dad0d04fSFariya Fatima  * copyright notice and this permission notice appear in all copies.
7dad0d04fSFariya Fatima  *
8dad0d04fSFariya Fatima  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9dad0d04fSFariya Fatima  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10dad0d04fSFariya Fatima  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11dad0d04fSFariya Fatima  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12dad0d04fSFariya Fatima  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13dad0d04fSFariya Fatima  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14dad0d04fSFariya Fatima  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15dad0d04fSFariya Fatima  *
16dad0d04fSFariya Fatima  */
17dad0d04fSFariya Fatima 
18dad0d04fSFariya Fatima #include <linux/module.h>
19b9b9f9feSJohan Hovold #include <linux/types.h>
202108df3cSPrameela Rani Garnepudi #include <net/rsi_91x.h>
21dad0d04fSFariya Fatima #include "rsi_usb.h"
22b78e91bcSPrameela Rani Garnepudi #include "rsi_hal.h"
232108df3cSPrameela Rani Garnepudi #include "rsi_coex.h"
24dad0d04fSFariya Fatima 
25898b2553SPrameela Rani Garnepudi /* Default operating mode is wlan STA + BT */
26898b2553SPrameela Rani Garnepudi static u16 dev_oper_mode = DEV_OPMODE_STA_BT_DUAL;
27898b2553SPrameela Rani Garnepudi module_param(dev_oper_mode, ushort, 0444);
2831f97cf9SMarek Vasut MODULE_PARM_DESC(dev_oper_mode, DEV_OPMODE_PARAM_DESC);
29898b2553SPrameela Rani Garnepudi 
30b9b9f9feSJohan Hovold static int rsi_rx_urb_submit(struct rsi_hw *adapter, u8 ep_num, gfp_t flags);
31a1854faeSPrameela Rani Garnepudi 
32dad0d04fSFariya Fatima /**
33dad0d04fSFariya Fatima  * rsi_usb_card_write() - This function writes to the USB Card.
34dad0d04fSFariya Fatima  * @adapter: Pointer to the adapter structure.
35dad0d04fSFariya Fatima  * @buf: Pointer to the buffer from where the data has to be taken.
36dad0d04fSFariya Fatima  * @len: Length to be written.
37dad0d04fSFariya Fatima  * @endpoint: Type of endpoint.
38dad0d04fSFariya Fatima  *
3950591c60SAlexey Khoroshilov  * Return: status: 0 on success, a negative error code on failure.
40dad0d04fSFariya Fatima  */
rsi_usb_card_write(struct rsi_hw * adapter,u8 * buf,u16 len,u8 endpoint)41dad0d04fSFariya Fatima static int rsi_usb_card_write(struct rsi_hw *adapter,
42ed833be6SKarun Eagalapati 			      u8 *buf,
43dad0d04fSFariya Fatima 			      u16 len,
44dad0d04fSFariya Fatima 			      u8 endpoint)
45dad0d04fSFariya Fatima {
46*f543235cSWu Yunchuan 	struct rsi_91x_usbdev *dev = adapter->rsi_dev;
47dad0d04fSFariya Fatima 	int status;
48ed833be6SKarun Eagalapati 	u8 *seg = dev->tx_buffer;
49ed833be6SKarun Eagalapati 	int transfer;
50ed833be6SKarun Eagalapati 	int ep = dev->bulkout_endpoint_addr[endpoint - 1];
51dad0d04fSFariya Fatima 
52ed833be6SKarun Eagalapati 	memset(seg, 0, len + RSI_USB_TX_HEAD_ROOM);
53ed833be6SKarun Eagalapati 	memcpy(seg + RSI_USB_TX_HEAD_ROOM, buf, len);
54ed833be6SKarun Eagalapati 	len += RSI_USB_TX_HEAD_ROOM;
55ed833be6SKarun Eagalapati 	transfer = len;
56dad0d04fSFariya Fatima 	status = usb_bulk_msg(dev->usbdev,
57ed833be6SKarun Eagalapati 			      usb_sndbulkpipe(dev->usbdev, ep),
58ed833be6SKarun Eagalapati 			      (void *)seg,
59ed833be6SKarun Eagalapati 			      (int)len,
60dad0d04fSFariya Fatima 			      &transfer,
61541fd20cSJohan Hovold 			      USB_CTRL_SET_TIMEOUT);
62dad0d04fSFariya Fatima 
63dad0d04fSFariya Fatima 	if (status < 0) {
64dad0d04fSFariya Fatima 		rsi_dbg(ERR_ZONE,
65dad0d04fSFariya Fatima 			"Card write failed with error code :%10d\n", status);
66dad0d04fSFariya Fatima 		dev->write_fail = 1;
67dad0d04fSFariya Fatima 	}
68dad0d04fSFariya Fatima 	return status;
69dad0d04fSFariya Fatima }
70dad0d04fSFariya Fatima 
71dad0d04fSFariya Fatima /**
72dad0d04fSFariya Fatima  * rsi_write_multiple() - This function writes multiple bytes of information
73dad0d04fSFariya Fatima  *			  to the USB card.
74dad0d04fSFariya Fatima  * @adapter: Pointer to the adapter structure.
75f21e6c58SLee Jones  * @endpoint: Type of endpoint.
76dad0d04fSFariya Fatima  * @data: Pointer to the data that has to be written.
77dad0d04fSFariya Fatima  * @count: Number of multiple bytes to be written.
78dad0d04fSFariya Fatima  *
7950591c60SAlexey Khoroshilov  * Return: 0 on success, a negative error code on failure.
80dad0d04fSFariya Fatima  */
rsi_write_multiple(struct rsi_hw * adapter,u8 endpoint,u8 * data,u32 count)81dad0d04fSFariya Fatima static int rsi_write_multiple(struct rsi_hw *adapter,
82dad0d04fSFariya Fatima 			      u8 endpoint,
83dad0d04fSFariya Fatima 			      u8 *data,
84dad0d04fSFariya Fatima 			      u32 count)
85dad0d04fSFariya Fatima {
866508497cSColin Ian King 	struct rsi_91x_usbdev *dev;
87ed833be6SKarun Eagalapati 
88ed833be6SKarun Eagalapati 	if (!adapter)
89ed833be6SKarun Eagalapati 		return -ENODEV;
90ed833be6SKarun Eagalapati 
91ed833be6SKarun Eagalapati 	if (endpoint == 0)
92ed833be6SKarun Eagalapati 		return -EINVAL;
93dad0d04fSFariya Fatima 
94*f543235cSWu Yunchuan 	dev = adapter->rsi_dev;
95dad0d04fSFariya Fatima 	if (dev->write_fail)
96ed833be6SKarun Eagalapati 		return -ENETDOWN;
97dad0d04fSFariya Fatima 
98ed833be6SKarun Eagalapati 	return rsi_usb_card_write(adapter, data, count, endpoint);
99dad0d04fSFariya Fatima }
100dad0d04fSFariya Fatima 
101dad0d04fSFariya Fatima /**
102dad0d04fSFariya Fatima  * rsi_find_bulk_in_and_out_endpoints() - This function initializes the bulk
103dad0d04fSFariya Fatima  *					  endpoints to the device.
104dad0d04fSFariya Fatima  * @interface: Pointer to the USB interface structure.
105dad0d04fSFariya Fatima  * @adapter: Pointer to the adapter structure.
106dad0d04fSFariya Fatima  *
107dad0d04fSFariya Fatima  * Return: ret_val: 0 on success, -ENOMEM on failure.
108dad0d04fSFariya Fatima  */
rsi_find_bulk_in_and_out_endpoints(struct usb_interface * interface,struct rsi_hw * adapter)109dad0d04fSFariya Fatima static int rsi_find_bulk_in_and_out_endpoints(struct usb_interface *interface,
110dad0d04fSFariya Fatima 					      struct rsi_hw *adapter)
111dad0d04fSFariya Fatima {
112*f543235cSWu Yunchuan 	struct rsi_91x_usbdev *dev = adapter->rsi_dev;
113dad0d04fSFariya Fatima 	struct usb_host_interface *iface_desc;
114dad0d04fSFariya Fatima 	struct usb_endpoint_descriptor *endpoint;
115dad0d04fSFariya Fatima 	__le16 buffer_size;
116a4302bffSSiva Rebbagondla 	int ii, bin_found = 0, bout_found = 0;
117dad0d04fSFariya Fatima 
1183139b180SJohan Hovold 	iface_desc = interface->cur_altsetting;
119dad0d04fSFariya Fatima 
120dad0d04fSFariya Fatima 	for (ii = 0; ii < iface_desc->desc.bNumEndpoints; ++ii) {
121dad0d04fSFariya Fatima 		endpoint = &(iface_desc->endpoint[ii].desc);
122dad0d04fSFariya Fatima 
123a4302bffSSiva Rebbagondla 		if (!dev->bulkin_endpoint_addr[bin_found] &&
124dad0d04fSFariya Fatima 		    (endpoint->bEndpointAddress & USB_DIR_IN) &&
125a4302bffSSiva Rebbagondla 		    ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
126dad0d04fSFariya Fatima 		    USB_ENDPOINT_XFER_BULK)) {
127dad0d04fSFariya Fatima 			buffer_size = endpoint->wMaxPacketSize;
128a4302bffSSiva Rebbagondla 			dev->bulkin_size[bin_found] = buffer_size;
129a4302bffSSiva Rebbagondla 			dev->bulkin_endpoint_addr[bin_found] =
130dad0d04fSFariya Fatima 				endpoint->bEndpointAddress;
131a4302bffSSiva Rebbagondla 			bin_found++;
132dad0d04fSFariya Fatima 		}
133dad0d04fSFariya Fatima 
134a4302bffSSiva Rebbagondla 		if (!dev->bulkout_endpoint_addr[bout_found] &&
135dad0d04fSFariya Fatima 		    !(endpoint->bEndpointAddress & USB_DIR_IN) &&
136dad0d04fSFariya Fatima 		    ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
137dad0d04fSFariya Fatima 		    USB_ENDPOINT_XFER_BULK)) {
138a4302bffSSiva Rebbagondla 			buffer_size = endpoint->wMaxPacketSize;
139a4302bffSSiva Rebbagondla 			dev->bulkout_endpoint_addr[bout_found] =
140dad0d04fSFariya Fatima 				endpoint->bEndpointAddress;
141a4302bffSSiva Rebbagondla 			dev->bulkout_size[bout_found] = buffer_size;
142a4302bffSSiva Rebbagondla 			bout_found++;
143dad0d04fSFariya Fatima 		}
144dad0d04fSFariya Fatima 
145a4302bffSSiva Rebbagondla 		if (bin_found >= MAX_BULK_EP || bout_found >= MAX_BULK_EP)
146dad0d04fSFariya Fatima 			break;
147dad0d04fSFariya Fatima 	}
148dad0d04fSFariya Fatima 
149960da557SJohan Hovold 	if (!(dev->bulkin_endpoint_addr[0] && dev->bulkout_endpoint_addr[0])) {
150960da557SJohan Hovold 		dev_err(&interface->dev, "missing wlan bulk endpoints\n");
151dad0d04fSFariya Fatima 		return -EINVAL;
152960da557SJohan Hovold 	}
153960da557SJohan Hovold 
154960da557SJohan Hovold 	if (adapter->priv->coex_mode > 1) {
155960da557SJohan Hovold 		if (!dev->bulkin_endpoint_addr[1]) {
156960da557SJohan Hovold 			dev_err(&interface->dev, "missing bt bulk-in endpoint\n");
157960da557SJohan Hovold 			return -EINVAL;
158960da557SJohan Hovold 		}
159960da557SJohan Hovold 	}
160dad0d04fSFariya Fatima 
161dad0d04fSFariya Fatima 	return 0;
162dad0d04fSFariya Fatima }
163dad0d04fSFariya Fatima 
1644b1fc881SPrameela Rani Garnepudi #define RSI_USB_REQ_OUT	(USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE)
1654b1fc881SPrameela Rani Garnepudi #define RSI_USB_REQ_IN	(USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE)
1664b1fc881SPrameela Rani Garnepudi 
167dad0d04fSFariya Fatima /* rsi_usb_reg_read() - This function reads data from given register address.
168dad0d04fSFariya Fatima  * @usbdev: Pointer to the usb_device structure.
169dad0d04fSFariya Fatima  * @reg: Address of the register to be read.
170dad0d04fSFariya Fatima  * @value: Value to be read.
171dad0d04fSFariya Fatima  * @len: length of data to be read.
172dad0d04fSFariya Fatima  *
17350591c60SAlexey Khoroshilov  * Return: status: 0 on success, a negative error code on failure.
174dad0d04fSFariya Fatima  */
rsi_usb_reg_read(struct usb_device * usbdev,u32 reg,u16 * value,u16 len)175dad0d04fSFariya Fatima static int rsi_usb_reg_read(struct usb_device *usbdev,
176dad0d04fSFariya Fatima 			    u32 reg,
177dad0d04fSFariya Fatima 			    u16 *value,
178dad0d04fSFariya Fatima 			    u16 len)
179dad0d04fSFariya Fatima {
180d453ba81SFariya Fatima 	u8 *buf;
181d453ba81SFariya Fatima 	int status = -ENOMEM;
182d453ba81SFariya Fatima 
183d35ef8f8SColin Ian King 	if (len > RSI_USB_CTRL_BUF_SIZE)
184d35ef8f8SColin Ian King 		return -EINVAL;
185d35ef8f8SColin Ian King 
186523b724aSAmitkumar Karwar 	buf  = kmalloc(RSI_USB_CTRL_BUF_SIZE, GFP_KERNEL);
187d453ba81SFariya Fatima 	if (!buf)
188d453ba81SFariya Fatima 		return status;
189dad0d04fSFariya Fatima 
190dad0d04fSFariya Fatima 	status = usb_control_msg(usbdev,
191dad0d04fSFariya Fatima 				 usb_rcvctrlpipe(usbdev, 0),
192dad0d04fSFariya Fatima 				 USB_VENDOR_REGISTER_READ,
1934b1fc881SPrameela Rani Garnepudi 				 RSI_USB_REQ_IN,
194dad0d04fSFariya Fatima 				 ((reg & 0xffff0000) >> 16), (reg & 0xffff),
195d453ba81SFariya Fatima 				 (void *)buf,
196dad0d04fSFariya Fatima 				 len,
1974b1fc881SPrameela Rani Garnepudi 				 USB_CTRL_GET_TIMEOUT);
198dad0d04fSFariya Fatima 
199d453ba81SFariya Fatima 	*value = (buf[0] | (buf[1] << 8));
200dad0d04fSFariya Fatima 	if (status < 0) {
201dad0d04fSFariya Fatima 		rsi_dbg(ERR_ZONE,
202dad0d04fSFariya Fatima 			"%s: Reg read failed with error code :%d\n",
203dad0d04fSFariya Fatima 			__func__, status);
204dad0d04fSFariya Fatima 	}
205d453ba81SFariya Fatima 	kfree(buf);
206d453ba81SFariya Fatima 
207dad0d04fSFariya Fatima 	return status;
208dad0d04fSFariya Fatima }
209dad0d04fSFariya Fatima 
210dad0d04fSFariya Fatima /**
211dad0d04fSFariya Fatima  * rsi_usb_reg_write() - This function writes the given data into the given
212dad0d04fSFariya Fatima  *			 register address.
213dad0d04fSFariya Fatima  * @usbdev: Pointer to the usb_device structure.
214dad0d04fSFariya Fatima  * @reg: Address of the register.
215dad0d04fSFariya Fatima  * @value: Value to write.
216dad0d04fSFariya Fatima  * @len: Length of data to be written.
217dad0d04fSFariya Fatima  *
21850591c60SAlexey Khoroshilov  * Return: status: 0 on success, a negative error code on failure.
219dad0d04fSFariya Fatima  */
rsi_usb_reg_write(struct usb_device * usbdev,u32 reg,u32 value,u16 len)220dad0d04fSFariya Fatima static int rsi_usb_reg_write(struct usb_device *usbdev,
221dad0d04fSFariya Fatima 			     u32 reg,
2220a60014bSSiva Rebbagondla 			     u32 value,
223dad0d04fSFariya Fatima 			     u16 len)
224dad0d04fSFariya Fatima {
225d453ba81SFariya Fatima 	u8 *usb_reg_buf;
226d453ba81SFariya Fatima 	int status = -ENOMEM;
227d453ba81SFariya Fatima 
228d35ef8f8SColin Ian King 	if (len > RSI_USB_CTRL_BUF_SIZE)
229d35ef8f8SColin Ian King 		return -EINVAL;
230d35ef8f8SColin Ian King 
231523b724aSAmitkumar Karwar 	usb_reg_buf  = kmalloc(RSI_USB_CTRL_BUF_SIZE, GFP_KERNEL);
232d453ba81SFariya Fatima 	if (!usb_reg_buf)
233d453ba81SFariya Fatima 		return status;
234dad0d04fSFariya Fatima 
2350a60014bSSiva Rebbagondla 	usb_reg_buf[0] = (cpu_to_le32(value) & 0x00ff);
2360a60014bSSiva Rebbagondla 	usb_reg_buf[1] = (cpu_to_le32(value) & 0xff00) >> 8;
2370a60014bSSiva Rebbagondla 	usb_reg_buf[2] = (cpu_to_le32(value) & 0x00ff0000) >> 16;
2380a60014bSSiva Rebbagondla 	usb_reg_buf[3] = (cpu_to_le32(value) & 0xff000000) >> 24;
239dad0d04fSFariya Fatima 
240dad0d04fSFariya Fatima 	status = usb_control_msg(usbdev,
241dad0d04fSFariya Fatima 				 usb_sndctrlpipe(usbdev, 0),
242dad0d04fSFariya Fatima 				 USB_VENDOR_REGISTER_WRITE,
2434b1fc881SPrameela Rani Garnepudi 				 RSI_USB_REQ_OUT,
2440a60014bSSiva Rebbagondla 				 ((cpu_to_le32(reg) & 0xffff0000) >> 16),
2450a60014bSSiva Rebbagondla 				 (cpu_to_le32(reg) & 0xffff),
246dad0d04fSFariya Fatima 				 (void *)usb_reg_buf,
247dad0d04fSFariya Fatima 				 len,
2484b1fc881SPrameela Rani Garnepudi 				 USB_CTRL_SET_TIMEOUT);
249dad0d04fSFariya Fatima 	if (status < 0) {
250dad0d04fSFariya Fatima 		rsi_dbg(ERR_ZONE,
251dad0d04fSFariya Fatima 			"%s: Reg write failed with error code :%d\n",
252dad0d04fSFariya Fatima 			__func__, status);
253dad0d04fSFariya Fatima 	}
254d453ba81SFariya Fatima 	kfree(usb_reg_buf);
255d453ba81SFariya Fatima 
256dad0d04fSFariya Fatima 	return status;
257dad0d04fSFariya Fatima }
258dad0d04fSFariya Fatima 
259dad0d04fSFariya Fatima /**
260dad0d04fSFariya Fatima  * rsi_rx_done_handler() - This function is called when a packet is received
261b9c767fdSSiva Rebbagondla  *			   from USB stack. This is callback to receive done.
262dad0d04fSFariya Fatima  * @urb: Received URB.
263dad0d04fSFariya Fatima  *
264dad0d04fSFariya Fatima  * Return: None.
265dad0d04fSFariya Fatima  */
rsi_rx_done_handler(struct urb * urb)266dad0d04fSFariya Fatima static void rsi_rx_done_handler(struct urb *urb)
267dad0d04fSFariya Fatima {
2681100f81bSPrameela Rani Garnepudi 	struct rx_usb_ctrl_block *rx_cb = urb->context;
2691100f81bSPrameela Rani Garnepudi 	struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)rx_cb->data;
270a1854faeSPrameela Rani Garnepudi 	int status = -EINVAL;
271dad0d04fSFariya Fatima 
272b07e3c6eSZekun Shen 	if (!rx_cb->rx_skb)
273b07e3c6eSZekun Shen 		return;
274b07e3c6eSZekun Shen 
2750a60014bSSiva Rebbagondla 	if (urb->status) {
2760a60014bSSiva Rebbagondla 		dev_kfree_skb(rx_cb->rx_skb);
277b07e3c6eSZekun Shen 		rx_cb->rx_skb = NULL;
2780a60014bSSiva Rebbagondla 		return;
2790a60014bSSiva Rebbagondla 	}
280dad0d04fSFariya Fatima 
281baa8caf4SSiva Rebbagondla 	if (urb->actual_length <= 0 ||
282baa8caf4SSiva Rebbagondla 	    urb->actual_length > rx_cb->rx_skb->len) {
283baa8caf4SSiva Rebbagondla 		rsi_dbg(INFO_ZONE, "%s: Invalid packet length = %d\n",
284baa8caf4SSiva Rebbagondla 			__func__, urb->actual_length);
285a1854faeSPrameela Rani Garnepudi 		goto out;
286a1854faeSPrameela Rani Garnepudi 	}
287a1854faeSPrameela Rani Garnepudi 	if (skb_queue_len(&dev->rx_q) >= RSI_MAX_RX_PKTS) {
288a1854faeSPrameela Rani Garnepudi 		rsi_dbg(INFO_ZONE, "Max RX packets reached\n");
289a1854faeSPrameela Rani Garnepudi 		goto out;
290a1854faeSPrameela Rani Garnepudi 	}
291baa8caf4SSiva Rebbagondla 	skb_trim(rx_cb->rx_skb, urb->actual_length);
292a1854faeSPrameela Rani Garnepudi 	skb_queue_tail(&dev->rx_q, rx_cb->rx_skb);
293a1854faeSPrameela Rani Garnepudi 
294dad0d04fSFariya Fatima 	rsi_set_event(&dev->rx_thread.event);
295a1854faeSPrameela Rani Garnepudi 	status = 0;
296a1854faeSPrameela Rani Garnepudi 
297a1854faeSPrameela Rani Garnepudi out:
298b9b9f9feSJohan Hovold 	if (rsi_rx_urb_submit(dev->priv, rx_cb->ep_num, GFP_ATOMIC))
299a1854faeSPrameela Rani Garnepudi 		rsi_dbg(ERR_ZONE, "%s: Failed in urb submission", __func__);
300a1854faeSPrameela Rani Garnepudi 
301b07e3c6eSZekun Shen 	if (status) {
302a1854faeSPrameela Rani Garnepudi 		dev_kfree_skb(rx_cb->rx_skb);
303b07e3c6eSZekun Shen 		rx_cb->rx_skb = NULL;
304b07e3c6eSZekun Shen 	}
305dad0d04fSFariya Fatima }
306dad0d04fSFariya Fatima 
rsi_rx_urb_kill(struct rsi_hw * adapter,u8 ep_num)307e93cd351SJohan Hovold static void rsi_rx_urb_kill(struct rsi_hw *adapter, u8 ep_num)
308e93cd351SJohan Hovold {
309*f543235cSWu Yunchuan 	struct rsi_91x_usbdev *dev = adapter->rsi_dev;
310e93cd351SJohan Hovold 	struct rx_usb_ctrl_block *rx_cb = &dev->rx_cb[ep_num - 1];
311e93cd351SJohan Hovold 	struct urb *urb = rx_cb->rx_urb;
312e93cd351SJohan Hovold 
313e93cd351SJohan Hovold 	usb_kill_urb(urb);
314e93cd351SJohan Hovold }
315e93cd351SJohan Hovold 
316dad0d04fSFariya Fatima /**
317dad0d04fSFariya Fatima  * rsi_rx_urb_submit() - This function submits the given URB to the USB stack.
318dad0d04fSFariya Fatima  * @adapter: Pointer to the adapter structure.
319f21e6c58SLee Jones  * @ep_num: Endpoint number.
320f21e6c58SLee Jones  * @mem_flags: The type of memory to allocate.
321dad0d04fSFariya Fatima  *
32250591c60SAlexey Khoroshilov  * Return: 0 on success, a negative error code on failure.
323dad0d04fSFariya Fatima  */
rsi_rx_urb_submit(struct rsi_hw * adapter,u8 ep_num,gfp_t mem_flags)324b9b9f9feSJohan Hovold static int rsi_rx_urb_submit(struct rsi_hw *adapter, u8 ep_num, gfp_t mem_flags)
325dad0d04fSFariya Fatima {
326*f543235cSWu Yunchuan 	struct rsi_91x_usbdev *dev = adapter->rsi_dev;
3271100f81bSPrameela Rani Garnepudi 	struct rx_usb_ctrl_block *rx_cb = &dev->rx_cb[ep_num - 1];
3281100f81bSPrameela Rani Garnepudi 	struct urb *urb = rx_cb->rx_urb;
329dad0d04fSFariya Fatima 	int status;
330a1854faeSPrameela Rani Garnepudi 	struct sk_buff *skb;
331a1854faeSPrameela Rani Garnepudi 	u8 dword_align_bytes = 0;
332a1854faeSPrameela Rani Garnepudi 
333a1854faeSPrameela Rani Garnepudi 	skb = dev_alloc_skb(RSI_MAX_RX_USB_PKT_SIZE);
334a1854faeSPrameela Rani Garnepudi 	if (!skb)
335a1854faeSPrameela Rani Garnepudi 		return -ENOMEM;
336a1854faeSPrameela Rani Garnepudi 	skb_reserve(skb, MAX_DWORD_ALIGN_BYTES);
337baa8caf4SSiva Rebbagondla 	skb_put(skb, RSI_MAX_RX_USB_PKT_SIZE - MAX_DWORD_ALIGN_BYTES);
338a1854faeSPrameela Rani Garnepudi 	dword_align_bytes = (unsigned long)skb->data & 0x3f;
339a1854faeSPrameela Rani Garnepudi 	if (dword_align_bytes > 0)
340a1854faeSPrameela Rani Garnepudi 		skb_push(skb, dword_align_bytes);
341a1854faeSPrameela Rani Garnepudi 	urb->transfer_buffer = skb->data;
342a1854faeSPrameela Rani Garnepudi 	rx_cb->rx_skb = skb;
343dad0d04fSFariya Fatima 
344dad0d04fSFariya Fatima 	usb_fill_bulk_urb(urb,
345dad0d04fSFariya Fatima 			  dev->usbdev,
346dad0d04fSFariya Fatima 			  usb_rcvbulkpipe(dev->usbdev,
347a4302bffSSiva Rebbagondla 			  dev->bulkin_endpoint_addr[ep_num - 1]),
348dad0d04fSFariya Fatima 			  urb->transfer_buffer,
349baa8caf4SSiva Rebbagondla 			  skb->len,
350dad0d04fSFariya Fatima 			  rsi_rx_done_handler,
3511100f81bSPrameela Rani Garnepudi 			  rx_cb);
352dad0d04fSFariya Fatima 
353b9b9f9feSJohan Hovold 	status = usb_submit_urb(urb, mem_flags);
35447768297SJohan Hovold 	if (status) {
355dad0d04fSFariya Fatima 		rsi_dbg(ERR_ZONE, "%s: Failed in urb submission\n", __func__);
35647768297SJohan Hovold 		dev_kfree_skb(skb);
35747768297SJohan Hovold 	}
358dad0d04fSFariya Fatima 
359dad0d04fSFariya Fatima 	return status;
360dad0d04fSFariya Fatima }
361dad0d04fSFariya Fatima 
rsi_usb_read_register_multiple(struct rsi_hw * adapter,u32 addr,u8 * data,u16 count)36288fa51e1SPrameela Rani Garnepudi static int rsi_usb_read_register_multiple(struct rsi_hw *adapter, u32 addr,
36388fa51e1SPrameela Rani Garnepudi 					  u8 *data, u16 count)
36488fa51e1SPrameela Rani Garnepudi {
365*f543235cSWu Yunchuan 	struct rsi_91x_usbdev *dev = adapter->rsi_dev;
36688fa51e1SPrameela Rani Garnepudi 	u8 *buf;
36788fa51e1SPrameela Rani Garnepudi 	u16 transfer;
36888fa51e1SPrameela Rani Garnepudi 	int status;
36988fa51e1SPrameela Rani Garnepudi 
37088fa51e1SPrameela Rani Garnepudi 	if (!addr)
37188fa51e1SPrameela Rani Garnepudi 		return -EINVAL;
37288fa51e1SPrameela Rani Garnepudi 
37388fa51e1SPrameela Rani Garnepudi 	buf = kzalloc(RSI_USB_BUF_SIZE, GFP_KERNEL);
37488fa51e1SPrameela Rani Garnepudi 	if (!buf)
37588fa51e1SPrameela Rani Garnepudi 		return -ENOMEM;
37688fa51e1SPrameela Rani Garnepudi 
37788fa51e1SPrameela Rani Garnepudi 	while (count) {
37888fa51e1SPrameela Rani Garnepudi 		transfer = min_t(u16, count, RSI_USB_BUF_SIZE);
37988fa51e1SPrameela Rani Garnepudi 		status = usb_control_msg(dev->usbdev,
38088fa51e1SPrameela Rani Garnepudi 					 usb_rcvctrlpipe(dev->usbdev, 0),
38188fa51e1SPrameela Rani Garnepudi 					 USB_VENDOR_REGISTER_READ,
38288fa51e1SPrameela Rani Garnepudi 					 RSI_USB_REQ_IN,
38388fa51e1SPrameela Rani Garnepudi 					 ((addr & 0xffff0000) >> 16),
38488fa51e1SPrameela Rani Garnepudi 					 (addr & 0xffff), (void *)buf,
38588fa51e1SPrameela Rani Garnepudi 					 transfer, USB_CTRL_GET_TIMEOUT);
38688fa51e1SPrameela Rani Garnepudi 		if (status < 0) {
38788fa51e1SPrameela Rani Garnepudi 			rsi_dbg(ERR_ZONE,
38888fa51e1SPrameela Rani Garnepudi 				"Reg read failed with error code :%d\n",
38988fa51e1SPrameela Rani Garnepudi 				 status);
39088fa51e1SPrameela Rani Garnepudi 			kfree(buf);
39188fa51e1SPrameela Rani Garnepudi 			return status;
39288fa51e1SPrameela Rani Garnepudi 		}
39388fa51e1SPrameela Rani Garnepudi 		memcpy(data, buf, transfer);
39488fa51e1SPrameela Rani Garnepudi 		count -= transfer;
39588fa51e1SPrameela Rani Garnepudi 		data += transfer;
39688fa51e1SPrameela Rani Garnepudi 		addr += transfer;
39788fa51e1SPrameela Rani Garnepudi 	}
39888fa51e1SPrameela Rani Garnepudi 	kfree(buf);
39988fa51e1SPrameela Rani Garnepudi 	return 0;
40088fa51e1SPrameela Rani Garnepudi }
40188fa51e1SPrameela Rani Garnepudi 
402dad0d04fSFariya Fatima /**
403dad0d04fSFariya Fatima  * rsi_usb_write_register_multiple() - This function writes multiple bytes of
404dad0d04fSFariya Fatima  *				       information to multiple registers.
405dad0d04fSFariya Fatima  * @adapter: Pointer to the adapter structure.
406dad0d04fSFariya Fatima  * @addr: Address of the register.
407dad0d04fSFariya Fatima  * @data: Pointer to the data that has to be written.
408dad0d04fSFariya Fatima  * @count: Number of multiple bytes to be written on to the registers.
409dad0d04fSFariya Fatima  *
41050591c60SAlexey Khoroshilov  * Return: status: 0 on success, a negative error code on failure.
411dad0d04fSFariya Fatima  */
rsi_usb_write_register_multiple(struct rsi_hw * adapter,u32 addr,u8 * data,u16 count)4125578b1ffSPrameela Rani Garnepudi static int rsi_usb_write_register_multiple(struct rsi_hw *adapter, u32 addr,
4135578b1ffSPrameela Rani Garnepudi 					   u8 *data, u16 count)
414dad0d04fSFariya Fatima {
415*f543235cSWu Yunchuan 	struct rsi_91x_usbdev *dev = adapter->rsi_dev;
416dad0d04fSFariya Fatima 	u8 *buf;
4172fbbe517SPrameela Rani Garnepudi 	u16 transfer;
418dad0d04fSFariya Fatima 	int status = 0;
419dad0d04fSFariya Fatima 
4207bdead7aSamit karwar 	buf = kzalloc(RSI_USB_BUF_SIZE, GFP_KERNEL);
421dad0d04fSFariya Fatima 	if (!buf)
422dad0d04fSFariya Fatima 		return -ENOMEM;
423dad0d04fSFariya Fatima 
424dad0d04fSFariya Fatima 	while (count) {
4252fbbe517SPrameela Rani Garnepudi 		transfer = min_t(u16, count, RSI_USB_BUF_SIZE);
426dad0d04fSFariya Fatima 		memcpy(buf, data, transfer);
427dad0d04fSFariya Fatima 		status = usb_control_msg(dev->usbdev,
428dad0d04fSFariya Fatima 					 usb_sndctrlpipe(dev->usbdev, 0),
429dad0d04fSFariya Fatima 					 USB_VENDOR_REGISTER_WRITE,
4304b1fc881SPrameela Rani Garnepudi 					 RSI_USB_REQ_OUT,
431dad0d04fSFariya Fatima 					 ((addr & 0xffff0000) >> 16),
432dad0d04fSFariya Fatima 					 (addr & 0xffff),
433dad0d04fSFariya Fatima 					 (void *)buf,
434dad0d04fSFariya Fatima 					 transfer,
4354b1fc881SPrameela Rani Garnepudi 					 USB_CTRL_SET_TIMEOUT);
436dad0d04fSFariya Fatima 		if (status < 0) {
437dad0d04fSFariya Fatima 			rsi_dbg(ERR_ZONE,
438dad0d04fSFariya Fatima 				"Reg write failed with error code :%d\n",
439dad0d04fSFariya Fatima 				status);
440ea3336acSPrameela Rani Garnepudi 			kfree(buf);
441ea3336acSPrameela Rani Garnepudi 			return status;
442ea3336acSPrameela Rani Garnepudi 		}
443dad0d04fSFariya Fatima 		count -= transfer;
444dad0d04fSFariya Fatima 		data += transfer;
445dad0d04fSFariya Fatima 		addr += transfer;
446dad0d04fSFariya Fatima 	}
447dad0d04fSFariya Fatima 
448dad0d04fSFariya Fatima 	kfree(buf);
449dad0d04fSFariya Fatima 	return 0;
450dad0d04fSFariya Fatima }
451dad0d04fSFariya Fatima 
452dad0d04fSFariya Fatima /**
453dad0d04fSFariya Fatima  *rsi_usb_host_intf_write_pkt() - This function writes the packet to the
454dad0d04fSFariya Fatima  *				   USB card.
455dad0d04fSFariya Fatima  * @adapter: Pointer to the adapter structure.
456dad0d04fSFariya Fatima  * @pkt: Pointer to the data to be written on to the card.
457dad0d04fSFariya Fatima  * @len: Length of the data to be written on to the card.
458dad0d04fSFariya Fatima  *
45950591c60SAlexey Khoroshilov  * Return: 0 on success, a negative error code on failure.
460dad0d04fSFariya Fatima  */
rsi_usb_host_intf_write_pkt(struct rsi_hw * adapter,u8 * pkt,u32 len)461dad0d04fSFariya Fatima static int rsi_usb_host_intf_write_pkt(struct rsi_hw *adapter,
462dad0d04fSFariya Fatima 				       u8 *pkt,
463dad0d04fSFariya Fatima 				       u32 len)
464dad0d04fSFariya Fatima {
465ac6107caSAmitkumar Karwar 	u32 queueno = ((pkt[1] >> 4) & 0x7);
466dad0d04fSFariya Fatima 	u8 endpoint;
467dad0d04fSFariya Fatima 
468d1f69e41SKarun Eagalapati 	endpoint = ((queueno == RSI_WIFI_MGMT_Q || queueno == RSI_WIFI_DATA_Q ||
469d1f69e41SKarun Eagalapati 		     queueno == RSI_COEX_Q) ? WLAN_EP : BT_EP);
470dad0d04fSFariya Fatima 
471dad0d04fSFariya Fatima 	return rsi_write_multiple(adapter,
472dad0d04fSFariya Fatima 				  endpoint,
473dad0d04fSFariya Fatima 				  (u8 *)pkt,
474dad0d04fSFariya Fatima 				  len);
475dad0d04fSFariya Fatima }
476dad0d04fSFariya Fatima 
rsi_usb_master_reg_read(struct rsi_hw * adapter,u32 reg,u32 * value,u16 len)477b97e9b94SPrameela Rani Garnepudi static int rsi_usb_master_reg_read(struct rsi_hw *adapter, u32 reg,
478b97e9b94SPrameela Rani Garnepudi 				   u32 *value, u16 len)
479b97e9b94SPrameela Rani Garnepudi {
480b97e9b94SPrameela Rani Garnepudi 	struct usb_device *usbdev =
481b97e9b94SPrameela Rani Garnepudi 		((struct rsi_91x_usbdev *)adapter->rsi_dev)->usbdev;
482e6249e15SAmitkumar Karwar 	u16 temp;
483e6249e15SAmitkumar Karwar 	int ret;
484b97e9b94SPrameela Rani Garnepudi 
485e6249e15SAmitkumar Karwar 	ret = rsi_usb_reg_read(usbdev, reg, &temp, len);
486e6249e15SAmitkumar Karwar 	if (ret < 0)
487e6249e15SAmitkumar Karwar 		return ret;
488e6249e15SAmitkumar Karwar 	*value = temp;
489e6249e15SAmitkumar Karwar 
490e6249e15SAmitkumar Karwar 	return 0;
491b97e9b94SPrameela Rani Garnepudi }
492b97e9b94SPrameela Rani Garnepudi 
rsi_usb_master_reg_write(struct rsi_hw * adapter,unsigned long reg,unsigned long value,u16 len)493b97e9b94SPrameela Rani Garnepudi static int rsi_usb_master_reg_write(struct rsi_hw *adapter,
494b97e9b94SPrameela Rani Garnepudi 				    unsigned long reg,
495b97e9b94SPrameela Rani Garnepudi 				    unsigned long value, u16 len)
496b97e9b94SPrameela Rani Garnepudi {
497b97e9b94SPrameela Rani Garnepudi 	struct usb_device *usbdev =
498b97e9b94SPrameela Rani Garnepudi 		((struct rsi_91x_usbdev *)adapter->rsi_dev)->usbdev;
499b97e9b94SPrameela Rani Garnepudi 
500b97e9b94SPrameela Rani Garnepudi 	return rsi_usb_reg_write(usbdev, reg, value, len);
501b97e9b94SPrameela Rani Garnepudi }
502b97e9b94SPrameela Rani Garnepudi 
rsi_usb_load_data_master_write(struct rsi_hw * adapter,u32 base_address,u32 instructions_sz,u16 block_size,u8 * ta_firmware)503b97e9b94SPrameela Rani Garnepudi static int rsi_usb_load_data_master_write(struct rsi_hw *adapter,
504b97e9b94SPrameela Rani Garnepudi 					  u32 base_address,
505b97e9b94SPrameela Rani Garnepudi 					  u32 instructions_sz, u16 block_size,
506b97e9b94SPrameela Rani Garnepudi 					  u8 *ta_firmware)
507b97e9b94SPrameela Rani Garnepudi {
508b97e9b94SPrameela Rani Garnepudi 	u16 num_blocks;
509b97e9b94SPrameela Rani Garnepudi 	u32 cur_indx, i;
510b97e9b94SPrameela Rani Garnepudi 	u8 temp_buf[256];
511b97e9b94SPrameela Rani Garnepudi 	int status;
512b97e9b94SPrameela Rani Garnepudi 
513b97e9b94SPrameela Rani Garnepudi 	num_blocks = instructions_sz / block_size;
514b97e9b94SPrameela Rani Garnepudi 	rsi_dbg(INFO_ZONE, "num_blocks: %d\n", num_blocks);
515b97e9b94SPrameela Rani Garnepudi 
516b97e9b94SPrameela Rani Garnepudi 	for (cur_indx = 0, i = 0; i < num_blocks; i++, cur_indx += block_size) {
517b97e9b94SPrameela Rani Garnepudi 		memcpy(temp_buf, ta_firmware + cur_indx, block_size);
518b97e9b94SPrameela Rani Garnepudi 		status = rsi_usb_write_register_multiple(adapter, base_address,
519b97e9b94SPrameela Rani Garnepudi 							 (u8 *)(temp_buf),
520b97e9b94SPrameela Rani Garnepudi 							 block_size);
521b97e9b94SPrameela Rani Garnepudi 		if (status < 0)
522b97e9b94SPrameela Rani Garnepudi 			return status;
523b97e9b94SPrameela Rani Garnepudi 
524b97e9b94SPrameela Rani Garnepudi 		rsi_dbg(INFO_ZONE, "%s: loading block: %d\n", __func__, i);
525b97e9b94SPrameela Rani Garnepudi 		base_address += block_size;
526b97e9b94SPrameela Rani Garnepudi 	}
527b97e9b94SPrameela Rani Garnepudi 
528b97e9b94SPrameela Rani Garnepudi 	if (instructions_sz % block_size) {
529b97e9b94SPrameela Rani Garnepudi 		memset(temp_buf, 0, block_size);
530b97e9b94SPrameela Rani Garnepudi 		memcpy(temp_buf, ta_firmware + cur_indx,
531b97e9b94SPrameela Rani Garnepudi 		       instructions_sz % block_size);
532b97e9b94SPrameela Rani Garnepudi 		status = rsi_usb_write_register_multiple
533b97e9b94SPrameela Rani Garnepudi 						(adapter, base_address,
534b97e9b94SPrameela Rani Garnepudi 						 (u8 *)temp_buf,
535b97e9b94SPrameela Rani Garnepudi 						 instructions_sz % block_size);
536b97e9b94SPrameela Rani Garnepudi 		if (status < 0)
537b97e9b94SPrameela Rani Garnepudi 			return status;
538b97e9b94SPrameela Rani Garnepudi 		rsi_dbg(INFO_ZONE,
539b97e9b94SPrameela Rani Garnepudi 			"Written Last Block in Address 0x%x Successfully\n",
540b97e9b94SPrameela Rani Garnepudi 			cur_indx);
541b97e9b94SPrameela Rani Garnepudi 	}
542b97e9b94SPrameela Rani Garnepudi 	return 0;
543b97e9b94SPrameela Rani Garnepudi }
544b97e9b94SPrameela Rani Garnepudi 
545a2ce952cSPrameela Rani Garnepudi static struct rsi_host_intf_ops usb_host_intf_ops = {
546a2ce952cSPrameela Rani Garnepudi 	.write_pkt		= rsi_usb_host_intf_write_pkt,
547a2ce952cSPrameela Rani Garnepudi 	.read_reg_multiple	= rsi_usb_read_register_multiple,
548a2ce952cSPrameela Rani Garnepudi 	.write_reg_multiple	= rsi_usb_write_register_multiple,
549b97e9b94SPrameela Rani Garnepudi 	.master_reg_read	= rsi_usb_master_reg_read,
550b97e9b94SPrameela Rani Garnepudi 	.master_reg_write	= rsi_usb_master_reg_write,
551b97e9b94SPrameela Rani Garnepudi 	.load_data_master_write	= rsi_usb_load_data_master_write,
552a2ce952cSPrameela Rani Garnepudi };
553a2ce952cSPrameela Rani Garnepudi 
554dad0d04fSFariya Fatima /**
555dad0d04fSFariya Fatima  * rsi_deinit_usb_interface() - This function deinitializes the usb interface.
556dad0d04fSFariya Fatima  * @adapter: Pointer to the adapter structure.
557dad0d04fSFariya Fatima  *
558dad0d04fSFariya Fatima  * Return: None.
559dad0d04fSFariya Fatima  */
rsi_deinit_usb_interface(struct rsi_hw * adapter)560dad0d04fSFariya Fatima static void rsi_deinit_usb_interface(struct rsi_hw *adapter)
561dad0d04fSFariya Fatima {
562*f543235cSWu Yunchuan 	struct rsi_91x_usbdev *dev = adapter->rsi_dev;
563dad0d04fSFariya Fatima 
564dad0d04fSFariya Fatima 	rsi_kill_thread(&dev->rx_thread);
5651100f81bSPrameela Rani Garnepudi 
5668809f08cSPrameela Rani Garnepudi 	usb_free_urb(dev->rx_cb[0].rx_urb);
567a1854faeSPrameela Rani Garnepudi 	if (adapter->priv->coex_mode > 1)
5688809f08cSPrameela Rani Garnepudi 		usb_free_urb(dev->rx_cb[1].rx_urb);
5691100f81bSPrameela Rani Garnepudi 
570dad0d04fSFariya Fatima 	kfree(dev->tx_buffer);
571dad0d04fSFariya Fatima }
572dad0d04fSFariya Fatima 
rsi_usb_init_rx(struct rsi_hw * adapter)5731100f81bSPrameela Rani Garnepudi static int rsi_usb_init_rx(struct rsi_hw *adapter)
5741100f81bSPrameela Rani Garnepudi {
575*f543235cSWu Yunchuan 	struct rsi_91x_usbdev *dev = adapter->rsi_dev;
5761100f81bSPrameela Rani Garnepudi 	struct rx_usb_ctrl_block *rx_cb;
5778809f08cSPrameela Rani Garnepudi 	u8 idx, num_rx_cb;
5781100f81bSPrameela Rani Garnepudi 
5798809f08cSPrameela Rani Garnepudi 	num_rx_cb = (adapter->priv->coex_mode > 1 ? 2 : 1);
5808809f08cSPrameela Rani Garnepudi 
5818809f08cSPrameela Rani Garnepudi 	for (idx = 0; idx < num_rx_cb; idx++) {
5821100f81bSPrameela Rani Garnepudi 		rx_cb = &dev->rx_cb[idx];
5831100f81bSPrameela Rani Garnepudi 
5841100f81bSPrameela Rani Garnepudi 		rx_cb->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
5851100f81bSPrameela Rani Garnepudi 		if (!rx_cb->rx_urb) {
5861100f81bSPrameela Rani Garnepudi 			rsi_dbg(ERR_ZONE, "Failed alloc rx urb[%d]\n", idx);
5871100f81bSPrameela Rani Garnepudi 			goto err;
5881100f81bSPrameela Rani Garnepudi 		}
5891100f81bSPrameela Rani Garnepudi 		rx_cb->ep_num = idx + 1;
5901100f81bSPrameela Rani Garnepudi 		rx_cb->data = (void *)dev;
5911100f81bSPrameela Rani Garnepudi 	}
592a1854faeSPrameela Rani Garnepudi 	skb_queue_head_init(&dev->rx_q);
593a1854faeSPrameela Rani Garnepudi 	rsi_init_event(&dev->rx_thread.event);
594a1854faeSPrameela Rani Garnepudi 	if (rsi_create_kthread(adapter->priv, &dev->rx_thread,
595a1854faeSPrameela Rani Garnepudi 			       rsi_usb_rx_thread, "RX-Thread")) {
596a1854faeSPrameela Rani Garnepudi 		rsi_dbg(ERR_ZONE, "%s: Unable to init rx thrd\n", __func__);
597a1854faeSPrameela Rani Garnepudi 		goto err;
598a1854faeSPrameela Rani Garnepudi 	}
599a1854faeSPrameela Rani Garnepudi 
6001100f81bSPrameela Rani Garnepudi 	return 0;
6011100f81bSPrameela Rani Garnepudi 
6021100f81bSPrameela Rani Garnepudi err:
6038809f08cSPrameela Rani Garnepudi 	usb_free_urb(dev->rx_cb[0].rx_urb);
604a1854faeSPrameela Rani Garnepudi 	if (adapter->priv->coex_mode > 1)
6058809f08cSPrameela Rani Garnepudi 		usb_free_urb(dev->rx_cb[1].rx_urb);
606a1854faeSPrameela Rani Garnepudi 
6071100f81bSPrameela Rani Garnepudi 	return -1;
6081100f81bSPrameela Rani Garnepudi }
6091100f81bSPrameela Rani Garnepudi 
610dad0d04fSFariya Fatima /**
611dad0d04fSFariya Fatima  * rsi_init_usb_interface() - This function initializes the usb interface.
612dad0d04fSFariya Fatima  * @adapter: Pointer to the adapter structure.
613dad0d04fSFariya Fatima  * @pfunction: Pointer to USB interface structure.
614dad0d04fSFariya Fatima  *
61550591c60SAlexey Khoroshilov  * Return: 0 on success, a negative error code on failure.
616dad0d04fSFariya Fatima  */
rsi_init_usb_interface(struct rsi_hw * adapter,struct usb_interface * pfunction)617dad0d04fSFariya Fatima static int rsi_init_usb_interface(struct rsi_hw *adapter,
618dad0d04fSFariya Fatima 				  struct usb_interface *pfunction)
619dad0d04fSFariya Fatima {
620dad0d04fSFariya Fatima 	struct rsi_91x_usbdev *rsi_dev;
6218809f08cSPrameela Rani Garnepudi 	int status;
622dad0d04fSFariya Fatima 
623dad0d04fSFariya Fatima 	rsi_dev = kzalloc(sizeof(*rsi_dev), GFP_KERNEL);
624dad0d04fSFariya Fatima 	if (!rsi_dev)
625dad0d04fSFariya Fatima 		return -ENOMEM;
626dad0d04fSFariya Fatima 
627dad0d04fSFariya Fatima 	adapter->rsi_dev = rsi_dev;
628dad0d04fSFariya Fatima 	rsi_dev->usbdev = interface_to_usbdev(pfunction);
629a1854faeSPrameela Rani Garnepudi 	rsi_dev->priv = (void *)adapter;
630dad0d04fSFariya Fatima 
631a1854faeSPrameela Rani Garnepudi 	if (rsi_find_bulk_in_and_out_endpoints(pfunction, adapter)) {
632a1854faeSPrameela Rani Garnepudi 		status = -EINVAL;
633a1854faeSPrameela Rani Garnepudi 		goto fail_eps;
634a1854faeSPrameela Rani Garnepudi 	}
635dad0d04fSFariya Fatima 
636dad0d04fSFariya Fatima 	adapter->device = &pfunction->dev;
637dad0d04fSFariya Fatima 	usb_set_intfdata(pfunction, adapter);
638dad0d04fSFariya Fatima 
6395bc5ca85SAlexey Khoroshilov 	rsi_dev->tx_buffer = kmalloc(2048, GFP_KERNEL);
64050591c60SAlexey Khoroshilov 	if (!rsi_dev->tx_buffer) {
64150591c60SAlexey Khoroshilov 		status = -ENOMEM;
642a1854faeSPrameela Rani Garnepudi 		goto fail_eps;
64350591c60SAlexey Khoroshilov 	}
6441100f81bSPrameela Rani Garnepudi 
6451100f81bSPrameela Rani Garnepudi 	if (rsi_usb_init_rx(adapter)) {
6461100f81bSPrameela Rani Garnepudi 		rsi_dbg(ERR_ZONE, "Failed to init RX handle\n");
647a1854faeSPrameela Rani Garnepudi 		status = -ENOMEM;
648a1854faeSPrameela Rani Garnepudi 		goto fail_rx;
64950591c60SAlexey Khoroshilov 	}
6501100f81bSPrameela Rani Garnepudi 
651dad0d04fSFariya Fatima 	rsi_dev->tx_blk_size = 252;
652b78e91bcSPrameela Rani Garnepudi 	adapter->block_size = rsi_dev->tx_blk_size;
653dad0d04fSFariya Fatima 
654dad0d04fSFariya Fatima 	/* Initializing function callbacks */
655dad0d04fSFariya Fatima 	adapter->check_hw_queue_status = rsi_usb_check_queue_status;
656dad0d04fSFariya Fatima 	adapter->determine_event_timeout = rsi_usb_event_timeout;
657a2ce952cSPrameela Rani Garnepudi 	adapter->rsi_host_intf = RSI_HOST_INTF_USB;
658a2ce952cSPrameela Rani Garnepudi 	adapter->host_intf_ops = &usb_host_intf_ops;
659dad0d04fSFariya Fatima 
660dad0d04fSFariya Fatima #ifdef CONFIG_RSI_DEBUGFS
661dad0d04fSFariya Fatima 	/* In USB, one less than the MAX_DEBUGFS_ENTRIES entries is required */
662dad0d04fSFariya Fatima 	adapter->num_debugfs_entries = (MAX_DEBUGFS_ENTRIES - 1);
663dad0d04fSFariya Fatima #endif
664dad0d04fSFariya Fatima 
665dad0d04fSFariya Fatima 	rsi_dbg(INIT_ZONE, "%s: Enabled the interface\n", __func__);
666dad0d04fSFariya Fatima 	return 0;
667dad0d04fSFariya Fatima 
668a1854faeSPrameela Rani Garnepudi fail_rx:
669a1854faeSPrameela Rani Garnepudi 	kfree(rsi_dev->tx_buffer);
670a1854faeSPrameela Rani Garnepudi 
671a1854faeSPrameela Rani Garnepudi fail_eps:
672a1854faeSPrameela Rani Garnepudi 
673dad0d04fSFariya Fatima 	return status;
674dad0d04fSFariya Fatima }
675dad0d04fSFariya Fatima 
usb_ulp_read_write(struct rsi_hw * adapter,u16 addr,u32 data,u16 len_in_bits)676ea0676c4SKarun Eagalapati static int usb_ulp_read_write(struct rsi_hw *adapter, u16 addr, u32 data,
677ea0676c4SKarun Eagalapati 			      u16 len_in_bits)
678ea0676c4SKarun Eagalapati {
679ea0676c4SKarun Eagalapati 	int ret;
680ea0676c4SKarun Eagalapati 
681ea0676c4SKarun Eagalapati 	ret = rsi_usb_master_reg_write
682ea0676c4SKarun Eagalapati 			(adapter, RSI_GSPI_DATA_REG1,
683ea0676c4SKarun Eagalapati 			 ((addr << 6) | ((data >> 16) & 0xffff)), 2);
684ea0676c4SKarun Eagalapati 	if (ret < 0)
685ea0676c4SKarun Eagalapati 		return ret;
686ea0676c4SKarun Eagalapati 
687ea0676c4SKarun Eagalapati 	ret = rsi_usb_master_reg_write(adapter, RSI_GSPI_DATA_REG0,
688ea0676c4SKarun Eagalapati 				       (data & 0xffff), 2);
689ea0676c4SKarun Eagalapati 	if (ret < 0)
690ea0676c4SKarun Eagalapati 		return ret;
691ea0676c4SKarun Eagalapati 
692ea0676c4SKarun Eagalapati 	/* Initializing GSPI for ULP read/writes */
693ea0676c4SKarun Eagalapati 	rsi_usb_master_reg_write(adapter, RSI_GSPI_CTRL_REG0,
694ea0676c4SKarun Eagalapati 				 RSI_GSPI_CTRL_REG0_VALUE, 2);
695ea0676c4SKarun Eagalapati 
696ea0676c4SKarun Eagalapati 	ret = rsi_usb_master_reg_write(adapter, RSI_GSPI_CTRL_REG1,
697ea0676c4SKarun Eagalapati 				       ((len_in_bits - 1) | RSI_GSPI_TRIG), 2);
698ea0676c4SKarun Eagalapati 	if (ret < 0)
699ea0676c4SKarun Eagalapati 		return ret;
700ea0676c4SKarun Eagalapati 
701ea0676c4SKarun Eagalapati 	msleep(20);
702ea0676c4SKarun Eagalapati 
703ea0676c4SKarun Eagalapati 	return 0;
704ea0676c4SKarun Eagalapati }
705ea0676c4SKarun Eagalapati 
rsi_reset_card(struct rsi_hw * adapter)706ea0676c4SKarun Eagalapati static int rsi_reset_card(struct rsi_hw *adapter)
707ea0676c4SKarun Eagalapati {
708ea0676c4SKarun Eagalapati 	int ret;
709ea0676c4SKarun Eagalapati 
710ea0676c4SKarun Eagalapati 	rsi_dbg(INFO_ZONE, "Resetting Card...\n");
711ea0676c4SKarun Eagalapati 	rsi_usb_master_reg_write(adapter, RSI_TA_HOLD_REG, 0xE, 4);
712ea0676c4SKarun Eagalapati 
713ea0676c4SKarun Eagalapati 	/* This msleep will ensure Thread-Arch processor to go to hold
714ea0676c4SKarun Eagalapati 	 * and any pending dma transfers to rf in device to finish.
715ea0676c4SKarun Eagalapati 	 */
716ea0676c4SKarun Eagalapati 	msleep(100);
717ea0676c4SKarun Eagalapati 
71848c6b5c9SGustavo A. R. Silva 	ret = rsi_usb_master_reg_write(adapter, SWBL_REGOUT,
71916d3bb7bSAmitkumar Karwar 				       RSI_FW_WDT_DISABLE_REQ,
72048c6b5c9SGustavo A. R. Silva 				       RSI_COMMON_REG_SIZE);
72148c6b5c9SGustavo A. R. Silva 	if (ret < 0) {
72216d3bb7bSAmitkumar Karwar 		rsi_dbg(ERR_ZONE, "Disabling firmware watchdog timer failed\n");
72316d3bb7bSAmitkumar Karwar 		goto fail;
72416d3bb7bSAmitkumar Karwar 	}
72516d3bb7bSAmitkumar Karwar 
72617ff2c79SSiva Rebbagondla 	if (adapter->device_model != RSI_DEV_9116) {
727ea0676c4SKarun Eagalapati 		ret = usb_ulp_read_write(adapter, RSI_WATCH_DOG_TIMER_1,
728ea0676c4SKarun Eagalapati 					 RSI_ULP_WRITE_2, 32);
729ea0676c4SKarun Eagalapati 		if (ret < 0)
730ea0676c4SKarun Eagalapati 			goto fail;
731ea0676c4SKarun Eagalapati 		ret = usb_ulp_read_write(adapter, RSI_WATCH_DOG_TIMER_2,
732ea0676c4SKarun Eagalapati 					 RSI_ULP_WRITE_0, 32);
733ea0676c4SKarun Eagalapati 		if (ret < 0)
734ea0676c4SKarun Eagalapati 			goto fail;
735ea0676c4SKarun Eagalapati 		ret = usb_ulp_read_write(adapter, RSI_WATCH_DOG_DELAY_TIMER_1,
736ea0676c4SKarun Eagalapati 					 RSI_ULP_WRITE_50, 32);
737ea0676c4SKarun Eagalapati 		if (ret < 0)
738ea0676c4SKarun Eagalapati 			goto fail;
739ea0676c4SKarun Eagalapati 		ret = usb_ulp_read_write(adapter, RSI_WATCH_DOG_DELAY_TIMER_2,
740ea0676c4SKarun Eagalapati 					 RSI_ULP_WRITE_0, 32);
741ea0676c4SKarun Eagalapati 		if (ret < 0)
742ea0676c4SKarun Eagalapati 			goto fail;
743ea0676c4SKarun Eagalapati 		ret = usb_ulp_read_write(adapter, RSI_WATCH_DOG_TIMER_ENABLE,
744ea0676c4SKarun Eagalapati 					 RSI_ULP_TIMER_ENABLE, 32);
745ea0676c4SKarun Eagalapati 		if (ret < 0)
746ea0676c4SKarun Eagalapati 			goto fail;
74717ff2c79SSiva Rebbagondla 	} else {
748fb21d146SZhang Changzhong 		ret = rsi_usb_master_reg_write(adapter,
74917ff2c79SSiva Rebbagondla 					       NWP_WWD_INTERRUPT_TIMER,
75017ff2c79SSiva Rebbagondla 					       NWP_WWD_INT_TIMER_CLKS,
751fb21d146SZhang Changzhong 					       RSI_9116_REG_SIZE);
752fb21d146SZhang Changzhong 		if (ret < 0)
75317ff2c79SSiva Rebbagondla 			goto fail;
754fb21d146SZhang Changzhong 		ret = rsi_usb_master_reg_write(adapter,
75517ff2c79SSiva Rebbagondla 					       NWP_WWD_SYSTEM_RESET_TIMER,
75617ff2c79SSiva Rebbagondla 					       NWP_WWD_SYS_RESET_TIMER_CLKS,
757fb21d146SZhang Changzhong 					       RSI_9116_REG_SIZE);
758fb21d146SZhang Changzhong 		if (ret < 0)
75917ff2c79SSiva Rebbagondla 			goto fail;
760fb21d146SZhang Changzhong 		ret = rsi_usb_master_reg_write(adapter,
76117ff2c79SSiva Rebbagondla 					       NWP_WWD_MODE_AND_RSTART,
76217ff2c79SSiva Rebbagondla 					       NWP_WWD_TIMER_DISABLE,
763fb21d146SZhang Changzhong 					       RSI_9116_REG_SIZE);
764fb21d146SZhang Changzhong 		if (ret < 0)
76517ff2c79SSiva Rebbagondla 			goto fail;
76617ff2c79SSiva Rebbagondla 	}
767ea0676c4SKarun Eagalapati 
768ea0676c4SKarun Eagalapati 	rsi_dbg(INFO_ZONE, "Reset card done\n");
769ea0676c4SKarun Eagalapati 	return ret;
770ea0676c4SKarun Eagalapati 
771ea0676c4SKarun Eagalapati fail:
772ea0676c4SKarun Eagalapati 	rsi_dbg(ERR_ZONE, "Reset card failed\n");
773ea0676c4SKarun Eagalapati 	return ret;
774ea0676c4SKarun Eagalapati }
775ea0676c4SKarun Eagalapati 
776dad0d04fSFariya Fatima /**
777dad0d04fSFariya Fatima  * rsi_probe() - This function is called by kernel when the driver provided
778dad0d04fSFariya Fatima  *		 Vendor and device IDs are matched. All the initialization
779dad0d04fSFariya Fatima  *		 work is done here.
780dad0d04fSFariya Fatima  * @pfunction: Pointer to the USB interface structure.
781dad0d04fSFariya Fatima  * @id: Pointer to the usb_device_id structure.
782dad0d04fSFariya Fatima  *
78350591c60SAlexey Khoroshilov  * Return: 0 on success, a negative error code on failure.
784dad0d04fSFariya Fatima  */
rsi_probe(struct usb_interface * pfunction,const struct usb_device_id * id)785dad0d04fSFariya Fatima static int rsi_probe(struct usb_interface *pfunction,
786dad0d04fSFariya Fatima 		     const struct usb_device_id *id)
787dad0d04fSFariya Fatima {
788dad0d04fSFariya Fatima 	struct rsi_hw *adapter;
789dad0d04fSFariya Fatima 	struct rsi_91x_usbdev *dev;
790dad0d04fSFariya Fatima 	u16 fw_status;
79150591c60SAlexey Khoroshilov 	int status;
792dad0d04fSFariya Fatima 
793dad0d04fSFariya Fatima 	rsi_dbg(INIT_ZONE, "%s: Init function called\n", __func__);
794dad0d04fSFariya Fatima 
795898b2553SPrameela Rani Garnepudi 	adapter = rsi_91x_init(dev_oper_mode);
796dad0d04fSFariya Fatima 	if (!adapter) {
797dad0d04fSFariya Fatima 		rsi_dbg(ERR_ZONE, "%s: Failed to init os intf ops\n",
798dad0d04fSFariya Fatima 			__func__);
79950591c60SAlexey Khoroshilov 		return -ENOMEM;
800dad0d04fSFariya Fatima 	}
801b78e91bcSPrameela Rani Garnepudi 	adapter->rsi_host_intf = RSI_HOST_INTF_USB;
802dad0d04fSFariya Fatima 
80350591c60SAlexey Khoroshilov 	status = rsi_init_usb_interface(adapter, pfunction);
80450591c60SAlexey Khoroshilov 	if (status) {
805dad0d04fSFariya Fatima 		rsi_dbg(ERR_ZONE, "%s: Failed to init usb interface\n",
806dad0d04fSFariya Fatima 			__func__);
807dad0d04fSFariya Fatima 		goto err;
808dad0d04fSFariya Fatima 	}
809dad0d04fSFariya Fatima 
810dad0d04fSFariya Fatima 	rsi_dbg(ERR_ZONE, "%s: Initialized os intf ops\n", __func__);
811dad0d04fSFariya Fatima 
81239e50f5cSJohan Hovold 	if (id->idProduct == RSI_USB_PID_9113) {
81354fdb318SSiva Rebbagondla 		rsi_dbg(INIT_ZONE, "%s: 9113 module detected\n", __func__);
81454fdb318SSiva Rebbagondla 		adapter->device_model = RSI_DEV_9113;
81539e50f5cSJohan Hovold 	} else if (id->idProduct == RSI_USB_PID_9116) {
81654fdb318SSiva Rebbagondla 		rsi_dbg(INIT_ZONE, "%s: 9116 module detected\n", __func__);
81754fdb318SSiva Rebbagondla 		adapter->device_model = RSI_DEV_9116;
81854fdb318SSiva Rebbagondla 	} else {
81954fdb318SSiva Rebbagondla 		rsi_dbg(ERR_ZONE, "%s: Unsupported RSI device id 0x%x\n",
820c5dcf8f0SJohan Hovold 			__func__, id->idProduct);
8219adcdf67SDan Carpenter 		status = -ENODEV;
82254fdb318SSiva Rebbagondla 		goto err1;
82354fdb318SSiva Rebbagondla 	}
82454fdb318SSiva Rebbagondla 
825*f543235cSWu Yunchuan 	dev = adapter->rsi_dev;
826dad0d04fSFariya Fatima 
82750591c60SAlexey Khoroshilov 	status = rsi_usb_reg_read(dev->usbdev, FW_STATUS_REG, &fw_status, 2);
828b78e91bcSPrameela Rani Garnepudi 	if (status < 0)
829dad0d04fSFariya Fatima 		goto err1;
830dad0d04fSFariya Fatima 	else
831dad0d04fSFariya Fatima 		fw_status &= 1;
832dad0d04fSFariya Fatima 
833dad0d04fSFariya Fatima 	if (!fw_status) {
834b78e91bcSPrameela Rani Garnepudi 		rsi_dbg(INIT_ZONE, "Loading firmware...\n");
835b78e91bcSPrameela Rani Garnepudi 		status = rsi_hal_device_init(adapter);
83650591c60SAlexey Khoroshilov 		if (status) {
837dad0d04fSFariya Fatima 			rsi_dbg(ERR_ZONE, "%s: Failed in device init\n",
838dad0d04fSFariya Fatima 				__func__);
839dad0d04fSFariya Fatima 			goto err1;
840dad0d04fSFariya Fatima 		}
841b78e91bcSPrameela Rani Garnepudi 		rsi_dbg(INIT_ZONE, "%s: Device Init Done\n", __func__);
842dad0d04fSFariya Fatima 	}
843dad0d04fSFariya Fatima 
844b9b9f9feSJohan Hovold 	status = rsi_rx_urb_submit(adapter, WLAN_EP, GFP_KERNEL);
84550591c60SAlexey Khoroshilov 	if (status)
846dad0d04fSFariya Fatima 		goto err1;
847dad0d04fSFariya Fatima 
848a4302bffSSiva Rebbagondla 	if (adapter->priv->coex_mode > 1) {
849b9b9f9feSJohan Hovold 		status = rsi_rx_urb_submit(adapter, BT_EP, GFP_KERNEL);
850a4302bffSSiva Rebbagondla 		if (status)
851e93cd351SJohan Hovold 			goto err_kill_wlan_urb;
852a4302bffSSiva Rebbagondla 	}
853a4302bffSSiva Rebbagondla 
854dad0d04fSFariya Fatima 	return 0;
855e93cd351SJohan Hovold 
856e93cd351SJohan Hovold err_kill_wlan_urb:
857e93cd351SJohan Hovold 	rsi_rx_urb_kill(adapter, WLAN_EP);
858dad0d04fSFariya Fatima err1:
859dad0d04fSFariya Fatima 	rsi_deinit_usb_interface(adapter);
860dad0d04fSFariya Fatima err:
861dad0d04fSFariya Fatima 	rsi_91x_deinit(adapter);
862dad0d04fSFariya Fatima 	rsi_dbg(ERR_ZONE, "%s: Failed in probe...Exiting\n", __func__);
86350591c60SAlexey Khoroshilov 	return status;
864dad0d04fSFariya Fatima }
865dad0d04fSFariya Fatima 
866dad0d04fSFariya Fatima /**
867dad0d04fSFariya Fatima  * rsi_disconnect() - This function performs the reverse of the probe function,
8681cce2000SMasahiro Yamada  *		      it deinitialize the driver structure.
869dad0d04fSFariya Fatima  * @pfunction: Pointer to the USB interface structure.
870dad0d04fSFariya Fatima  *
871dad0d04fSFariya Fatima  * Return: None.
872dad0d04fSFariya Fatima  */
rsi_disconnect(struct usb_interface * pfunction)873dad0d04fSFariya Fatima static void rsi_disconnect(struct usb_interface *pfunction)
874dad0d04fSFariya Fatima {
875dad0d04fSFariya Fatima 	struct rsi_hw *adapter = usb_get_intfdata(pfunction);
876dad0d04fSFariya Fatima 
877dad0d04fSFariya Fatima 	if (!adapter)
878dad0d04fSFariya Fatima 		return;
879dad0d04fSFariya Fatima 
880dad0d04fSFariya Fatima 	rsi_mac80211_detach(adapter);
881cbde979bSSiva Rebbagondla 
882cbde979bSSiva Rebbagondla 	if (IS_ENABLED(CONFIG_RSI_COEX) && adapter->priv->coex_mode > 1 &&
883cbde979bSSiva Rebbagondla 	    adapter->priv->bt_adapter) {
884cbde979bSSiva Rebbagondla 		rsi_bt_ops.detach(adapter->priv->bt_adapter);
885cbde979bSSiva Rebbagondla 		adapter->priv->bt_adapter = NULL;
886cbde979bSSiva Rebbagondla 	}
887cbde979bSSiva Rebbagondla 
888e93cd351SJohan Hovold 	if (adapter->priv->coex_mode > 1)
889e93cd351SJohan Hovold 		rsi_rx_urb_kill(adapter, BT_EP);
890e93cd351SJohan Hovold 	rsi_rx_urb_kill(adapter, WLAN_EP);
891e93cd351SJohan Hovold 
892ea0676c4SKarun Eagalapati 	rsi_reset_card(adapter);
893dad0d04fSFariya Fatima 	rsi_deinit_usb_interface(adapter);
894dad0d04fSFariya Fatima 	rsi_91x_deinit(adapter);
895dad0d04fSFariya Fatima 
896dad0d04fSFariya Fatima 	rsi_dbg(INFO_ZONE, "%s: Deinitialization completed\n", __func__);
897dad0d04fSFariya Fatima }
898dad0d04fSFariya Fatima 
899dad0d04fSFariya Fatima #ifdef CONFIG_PM
rsi_suspend(struct usb_interface * intf,pm_message_t message)900dad0d04fSFariya Fatima static int rsi_suspend(struct usb_interface *intf, pm_message_t message)
901dad0d04fSFariya Fatima {
902dad0d04fSFariya Fatima 	/* Not yet implemented */
903dad0d04fSFariya Fatima 	return -ENOSYS;
904dad0d04fSFariya Fatima }
905dad0d04fSFariya Fatima 
rsi_resume(struct usb_interface * intf)906dad0d04fSFariya Fatima static int rsi_resume(struct usb_interface *intf)
907dad0d04fSFariya Fatima {
908dad0d04fSFariya Fatima 	/* Not yet implemented */
909dad0d04fSFariya Fatima 	return -ENOSYS;
910dad0d04fSFariya Fatima }
911dad0d04fSFariya Fatima #endif
912dad0d04fSFariya Fatima 
913dad0d04fSFariya Fatima static const struct usb_device_id rsi_dev_table[] = {
91454fdb318SSiva Rebbagondla 	{ USB_DEVICE(RSI_USB_VENDOR_ID, RSI_USB_PID_9113) },
91554fdb318SSiva Rebbagondla 	{ USB_DEVICE(RSI_USB_VENDOR_ID, RSI_USB_PID_9116) },
916dad0d04fSFariya Fatima 	{ /* Blank */},
917dad0d04fSFariya Fatima };
918dad0d04fSFariya Fatima 
919dad0d04fSFariya Fatima static struct usb_driver rsi_driver = {
920dad0d04fSFariya Fatima 	.name       = "RSI-USB WLAN",
921dad0d04fSFariya Fatima 	.probe      = rsi_probe,
922dad0d04fSFariya Fatima 	.disconnect = rsi_disconnect,
923dad0d04fSFariya Fatima 	.id_table   = rsi_dev_table,
924dad0d04fSFariya Fatima #ifdef CONFIG_PM
925dad0d04fSFariya Fatima 	.suspend    = rsi_suspend,
926dad0d04fSFariya Fatima 	.resume     = rsi_resume,
927dad0d04fSFariya Fatima #endif
928dad0d04fSFariya Fatima };
929dad0d04fSFariya Fatima 
930b101426aSAlexey Khoroshilov module_usb_driver(rsi_driver);
931dad0d04fSFariya Fatima 
932dad0d04fSFariya Fatima MODULE_AUTHOR("Redpine Signals Inc");
933dad0d04fSFariya Fatima MODULE_DESCRIPTION("Common USB layer for RSI drivers");
934dad0d04fSFariya Fatima MODULE_DEVICE_TABLE(usb, rsi_dev_table);
935dad0d04fSFariya Fatima MODULE_FIRMWARE(FIRMWARE_RSI9113);
936dad0d04fSFariya Fatima MODULE_VERSION("0.1");
937dad0d04fSFariya Fatima MODULE_LICENSE("Dual BSD/GPL");
938