// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/usb.h>
#include <linux/sched.h>
#include <linux/kthread.h>
#include <linux/usb/cdc.h>
#include <linux/wait.h>
#include <linux/if_ether.h>
#include <linux/pm_runtime.h>
#include "gdm_usb.h"
#include "gdm_lte.h"
#include "hci.h"
#include "hci_packet.h"
#include "gdm_endian.h"
#define USB_DEVICE_CDC_DATA(vid, pid) \
.match_flags = USB_DEVICE_ID_MATCH_DEVICE | \
USB_DEVICE_ID_MATCH_INT_CLASS | \
USB_DEVICE_ID_MATCH_INT_SUBCLASS,\
.idVendor = vid,\
.idProduct = pid,\
.bInterfaceClass = USB_CLASS_COMM,\
.bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET
#define USB_DEVICE_MASS_DATA(vid, pid) \
.match_flags = USB_DEVICE_ID_MATCH_DEVICE | \
USB_DEVICE_ID_MATCH_INT_INFO,\
.idVendor = vid,\
.idProduct = pid,\
.bInterfaceSubClass = USB_SC_SCSI, \
.bInterfaceClass = USB_CLASS_MASS_STORAGE,\
.bInterfaceProtocol = USB_PR_BULK
static const struct usb_device_id id_table[] = {
{ USB_DEVICE_CDC_DATA(VID_GCT, PID_GDM7240) }, /* GCT GDM7240 */
{ USB_DEVICE_CDC_DATA(VID_GCT, PID_GDM7243) }, /* GCT GDM7243 */
{ }
};
MODULE_DEVICE_TABLE(usb, id_table);
static void do_tx(struct work_struct *work);
static void do_rx(struct work_struct *work);
static int gdm_usb_recv(void *priv_dev,
int (*cb)(void *cb_data,
void *data, int len, int context),
void *cb_data,
int context);
static int request_mac_address(struct lte_udev *udev)
{
u8 buf[16] = {0,};
struct hci_packet *hci = (struct hci_packet *)buf;
struct usb_device *usbdev = udev->usbdev;
int actual;
int ret = -1;
hci->cmd_evt = gdm_cpu_to_dev16(udev->gdm_ed, LTE_GET_INFORMATION);
hci->len = gdm_cpu_to_dev16(udev->gdm_ed, 1);
hci->data[0] = MAC_ADDRESS;
ret = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, 2), buf, 5,
&actual, 1000);
udev->request_mac_addr = 1;
return ret;
}
static struct usb_tx *alloc_tx_struct(int len)
{
struct usb_tx *t = NULL;
int ret = 0;
t = kzalloc(sizeof(*t), GFP_ATOMIC);
if (!t) {
ret = -ENOMEM;
goto out;
}
t->urb = usb_alloc_urb(0, GFP_ATOMIC);
if (!(len % 512))
len++;
t->buf = kmalloc(len, GFP_ATOMIC);
if (!t->urb || !t->buf) {
ret = -ENOMEM;
goto out;
}
out:
if (ret < 0) {
if (t) {
usb_free_urb(t->urb);
kfree(t->buf);
kfree(t);
}
return NULL;
}
return t;
}
static struct usb_tx_sdu *alloc_tx_sdu_struct(void)
{
struct usb_tx_sdu *t_sdu;
t_sdu = kzalloc(sizeof(*t_sdu), GFP_KERNEL);
if (!t_sdu)
return NULL;
t_sdu->buf = kmalloc(SDU_BUF_SIZE, GFP_KERNEL);
if (!t_sdu->buf) {
kfree(t_sdu);
return NULL;
}
return t_sdu;
}
static void free_tx_struct(struct usb_tx *t)
{
if (t) {
usb_free_urb(t->urb);
kfree(t->buf);
kfree(t);
}
}
static void free_tx_sdu_struct(struct usb_tx_sdu *t_sdu)
{
if (t_sdu) {
kfree(t_sdu->buf);
kfree(t_sdu);
}
}
static struct usb_tx_sdu *get_tx_sdu_struct(struct tx_cxt *tx, int *no_spc)
{
struct usb_tx_sdu *t_sdu;
if (list_empty(&tx->free_list))