root/drivers/usb/misc/usb-ljca.c
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Intel La Jolla Cove Adapter USB driver
 *
 * Copyright (c) 2023, Intel Corporation.
 */

#include <linux/acpi.h>
#include <linux/auxiliary_bus.h>
#include <linux/dev_printk.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#include <linux/usb.h>
#include <linux/usb/ljca.h>

#include <linux/unaligned.h>

/* command flags */
#define LJCA_ACK_FLAG                   BIT(0)
#define LJCA_RESP_FLAG                  BIT(1)
#define LJCA_CMPL_FLAG                  BIT(2)

#define LJCA_MAX_PACKET_SIZE            64u
#define LJCA_MAX_PAYLOAD_SIZE           \
                (LJCA_MAX_PACKET_SIZE - sizeof(struct ljca_msg))

#define LJCA_WRITE_TIMEOUT_MS           200
#define LJCA_WRITE_ACK_TIMEOUT_MS       500
#define LJCA_ENUM_CLIENT_TIMEOUT_MS     20

/* ljca client type */
enum ljca_client_type {
        LJCA_CLIENT_MNG = 1,
        LJCA_CLIENT_GPIO = 3,
        LJCA_CLIENT_I2C = 4,
        LJCA_CLIENT_SPI = 5,
};

/* MNG client commands */
enum ljca_mng_cmd {
        LJCA_MNG_RESET = 2,
        LJCA_MNG_ENUM_GPIO = 4,
        LJCA_MNG_ENUM_I2C = 5,
        LJCA_MNG_ENUM_SPI = 8,
};

/* ljca client acpi _ADR */
enum ljca_client_acpi_adr {
        LJCA_GPIO_ACPI_ADR,
        LJCA_I2C1_ACPI_ADR,
        LJCA_I2C2_ACPI_ADR,
        LJCA_SPI1_ACPI_ADR,
        LJCA_SPI2_ACPI_ADR,
        LJCA_CLIENT_ACPI_ADR_MAX,
};

/* ljca cmd message structure */
struct ljca_msg {
        u8 type;
        u8 cmd;
        u8 flags;
        u8 len;
        u8 data[] __counted_by(len);
} __packed;

struct ljca_i2c_ctr_info {
        u8 id;
        u8 capacity;
        u8 intr_pin;
} __packed;

struct ljca_i2c_descriptor {
        u8 num;
        struct ljca_i2c_ctr_info info[] __counted_by(num);
} __packed;

struct ljca_spi_ctr_info {
        u8 id;
        u8 capacity;
        u8 intr_pin;
} __packed;

struct ljca_spi_descriptor {
        u8 num;
        struct ljca_spi_ctr_info info[] __counted_by(num);
} __packed;

struct ljca_bank_descriptor {
        u8 bank_id;
        u8 pin_num;

        /* 1 bit for each gpio, 1 means valid */
        __le32 valid_pins;
} __packed;

struct ljca_gpio_descriptor {
        u8 pins_per_bank;
        u8 bank_num;
        struct ljca_bank_descriptor bank_desc[] __counted_by(bank_num);
} __packed;

/**
 * struct ljca_adapter - represent a ljca adapter
 *
 * @intf: the usb interface for this ljca adapter
 * @usb_dev: the usb device for this ljca adapter
 * @dev: the specific device info of the usb interface
 * @rx_pipe: bulk in pipe for receive data from firmware
 * @tx_pipe: bulk out pipe for send data to firmware
 * @rx_urb: urb used for the bulk in pipe
 * @rx_buf: buffer used to receive command response and event
 * @rx_len: length of rx buffer
 * @ex_buf: external buffer to save command response
 * @ex_buf_len: length of external buffer
 * @actual_length: actual length of data copied to external buffer
 * @tx_buf: buffer used to download command to firmware
 * @tx_buf_len: length of tx buffer
 * @lock: spinlock to protect tx_buf and ex_buf
 * @cmd_completion: completion object as the command receives ack
 * @mutex: mutex to avoid command download concurrently
 * @client_list: client device list
 * @disconnect: usb disconnect ongoing or not
 * @reset_id: used to reset firmware
 */
struct ljca_adapter {
        struct usb_interface *intf;
        struct usb_device *usb_dev;
        struct device *dev;

        unsigned int rx_pipe;
        unsigned int tx_pipe;

        struct urb *rx_urb;
        void *rx_buf;
        unsigned int rx_len;

        u8 *ex_buf;
        u8 ex_buf_len;
        u8 actual_length;

        void *tx_buf;
        u8 tx_buf_len;

        spinlock_t lock;

        struct completion cmd_completion;
        struct mutex mutex;

        struct list_head client_list;

        bool disconnect;

        u32 reset_id;
};

struct ljca_match_ids_walk_data {
        const struct acpi_device_id *ids;
        const char *uid;
        struct acpi_device *adev;
};

/*
 * ACPI hardware IDs for LJCA client devices.
 *
 * [1] Some BIOS implementations use these IDs for denoting LJCA client devices
 *     even though the IDs have been allocated for USBIO. This isn't a problem
 *     as the usb-ljca driver is probed based on the USB device's vendor and
 *     product IDs and its client drivers are probed based on auxiliary device
 *     names, not these ACPI _HIDs. List of such systems:
 *
 *     Dell Precision 5490
 */
static const struct acpi_device_id ljca_gpio_hids[] = {
        { "INTC100B" }, /* RPL LJCA GPIO */
        { "INTC1074" }, /* CVF LJCA GPIO */
        { "INTC1096" }, /* ADL LJCA GPIO */
        { "INTC10B5" }, /* LNL LJCA GPIO */
        { "INTC10D1" }, /* MTL (CVF VSC) USBIO GPIO [1] */
        {},
};

static const struct acpi_device_id ljca_i2c_hids[] = {
        { "INTC100C" }, /* RPL LJCA I2C */
        { "INTC1075" }, /* CVF LJCA I2C */
        { "INTC1097" }, /* ADL LJCA I2C */
        { "INTC10D2" }, /* MTL (CVF VSC) USBIO I2C [1] */
        {},
};

static const struct acpi_device_id ljca_spi_hids[] = {
        { "INTC100D" }, /* RPL LJCA SPI */
        { "INTC1091" }, /* TGL/ADL LJCA SPI */
        { "INTC1098" }, /* ADL LJCA SPI */
        { "INTC10D3" }, /* MTL (CVF VSC) USBIO SPI [1] */
        {},
};

static void ljca_handle_event(struct ljca_adapter *adap,
                              struct ljca_msg *header)
{
        struct ljca_client *client;

        list_for_each_entry(client, &adap->client_list, link) {
                /*
                 * Currently only GPIO register event callback, but
                 * firmware message structure should include id when
                 * multiple same type clients register event callback.
                 */
                if (client->type == header->type) {
                        unsigned long flags;

                        spin_lock_irqsave(&client->event_cb_lock, flags);
                        client->event_cb(client->context, header->cmd,
                                         header->data, header->len);
                        spin_unlock_irqrestore(&client->event_cb_lock, flags);

                        break;
                }
        }
}

/* process command ack and received data if available */
static void ljca_handle_cmd_ack(struct ljca_adapter *adap, struct ljca_msg *header)
{
        struct ljca_msg *tx_header = adap->tx_buf;
        u8 ibuf_len, actual_len = 0;
        unsigned long flags;
        u8 *ibuf;

        spin_lock_irqsave(&adap->lock, flags);

        if (tx_header->type != header->type || tx_header->cmd != header->cmd) {
                spin_unlock_irqrestore(&adap->lock, flags);
                dev_err(adap->dev, "cmd ack mismatch error\n");
                return;
        }

        ibuf_len = adap->ex_buf_len;
        ibuf = adap->ex_buf;

        if (ibuf && ibuf_len) {
                actual_len = min(header->len, ibuf_len);

                /* copy received data to external buffer */
                memcpy(ibuf, header->data, actual_len);
        }
        /* update copied data length */
        adap->actual_length = actual_len;

        spin_unlock_irqrestore(&adap->lock, flags);

        complete(&adap->cmd_completion);
}

static void ljca_recv(struct urb *urb)
{
        struct ljca_msg *header = urb->transfer_buffer;
        struct ljca_adapter *adap = urb->context;
        int ret;

        switch (urb->status) {
        case 0:
                /* success */
                break;
        case -ENOENT:
                /*
                 * directly complete the possible ongoing transfer
                 * during disconnect
                 */
                if (adap->disconnect)
                        complete(&adap->cmd_completion);
                return;
        case -ECONNRESET:
        case -ESHUTDOWN:
        case -EPIPE:
                /* rx urb is terminated */
                dev_dbg(adap->dev, "rx urb terminated with status: %d\n",
                        urb->status);
                return;
        default:
                dev_dbg(adap->dev, "rx urb error: %d\n", urb->status);
                goto resubmit;
        }

        if (header->len + sizeof(*header) != urb->actual_length)
                goto resubmit;

        if (header->flags & LJCA_ACK_FLAG)
                ljca_handle_cmd_ack(adap, header);
        else
                ljca_handle_event(adap, header);

resubmit:
        ret = usb_submit_urb(urb, GFP_ATOMIC);
        if (ret && ret != -EPERM)
                dev_err(adap->dev, "resubmit rx urb error %d\n", ret);
}

static int ljca_send(struct ljca_adapter *adap, u8 type, u8 cmd,
                     const u8 *obuf, u8 obuf_len, u8 *ibuf, u8 ibuf_len,
                     bool ack, unsigned long timeout)
{
        unsigned int msg_len = sizeof(struct ljca_msg) + obuf_len;
        struct ljca_msg *header = adap->tx_buf;
        unsigned int transferred;
        unsigned long flags;
        int ret;

        if (adap->disconnect)
                return -ENODEV;

        if (msg_len > adap->tx_buf_len)
                return -EINVAL;

        mutex_lock(&adap->mutex);

        spin_lock_irqsave(&adap->lock, flags);

        header->type = type;
        header->cmd = cmd;
        header->len = obuf_len;
        if (obuf)
                memcpy(header->data, obuf, obuf_len);

        header->flags = LJCA_CMPL_FLAG | (ack ? LJCA_ACK_FLAG : 0);

        adap->ex_buf = ibuf;
        adap->ex_buf_len = ibuf_len;
        adap->actual_length = 0;

        spin_unlock_irqrestore(&adap->lock, flags);

        reinit_completion(&adap->cmd_completion);

        ret = usb_autopm_get_interface(adap->intf);
        if (ret < 0)
                goto out;

        ret = usb_bulk_msg(adap->usb_dev, adap->tx_pipe, header,
                           msg_len, &transferred, LJCA_WRITE_TIMEOUT_MS);
        if (ret < 0)
                goto out_put;
        if (transferred != msg_len) {
                ret = -EIO;
                goto out_put;
        }

        if (ack) {
                ret = wait_for_completion_timeout(&adap->cmd_completion,
                                                  timeout);
                if (!ret) {
                        ret = -ETIMEDOUT;
                        goto out_put;
                }
        }
        ret = adap->actual_length;

out_put:
        usb_autopm_put_interface(adap->intf);

out:
        spin_lock_irqsave(&adap->lock, flags);
        adap->ex_buf = NULL;
        adap->ex_buf_len = 0;

        memset(header, 0, sizeof(*header));
        spin_unlock_irqrestore(&adap->lock, flags);

        mutex_unlock(&adap->mutex);

        return ret;
}

int ljca_transfer(struct ljca_client *client, u8 cmd, const u8 *obuf,
                  u8 obuf_len, u8 *ibuf, u8 ibuf_len)
{
        return ljca_send(client->adapter, client->type, cmd,
                         obuf, obuf_len, ibuf, ibuf_len, true,
                         LJCA_WRITE_ACK_TIMEOUT_MS);
}
EXPORT_SYMBOL_NS_GPL(ljca_transfer, "LJCA");

int ljca_transfer_noack(struct ljca_client *client, u8 cmd, const u8 *obuf,
                        u8 obuf_len)
{
        return ljca_send(client->adapter, client->type, cmd, obuf,
                         obuf_len, NULL, 0, false, LJCA_WRITE_ACK_TIMEOUT_MS);
}
EXPORT_SYMBOL_NS_GPL(ljca_transfer_noack, "LJCA");

int ljca_register_event_cb(struct ljca_client *client, ljca_event_cb_t event_cb,
                           void *context)
{
        unsigned long flags;

        if (!event_cb)
                return -EINVAL;

        spin_lock_irqsave(&client->event_cb_lock, flags);

        if (client->event_cb) {
                spin_unlock_irqrestore(&client->event_cb_lock, flags);
                return -EALREADY;
        }

        client->event_cb = event_cb;
        client->context = context;

        spin_unlock_irqrestore(&client->event_cb_lock, flags);

        return 0;
}
EXPORT_SYMBOL_NS_GPL(ljca_register_event_cb, "LJCA");

void ljca_unregister_event_cb(struct ljca_client *client)
{
        unsigned long flags;

        spin_lock_irqsave(&client->event_cb_lock, flags);

        client->event_cb = NULL;
        client->context = NULL;

        spin_unlock_irqrestore(&client->event_cb_lock, flags);
}
EXPORT_SYMBOL_NS_GPL(ljca_unregister_event_cb, "LJCA");

static int ljca_match_device_ids(struct acpi_device *adev, void *data)
{
        struct ljca_match_ids_walk_data *wd = data;
        const char *uid = acpi_device_uid(adev);

        if (acpi_match_device_ids(adev, wd->ids))
                return 0;

        if (!wd->uid)
                goto match;

        if (!uid)
                /*
                 * Some DSDTs have only one ACPI companion for the two I2C
                 * controllers and they don't set a UID at all (e.g. Dell
                 * Latitude 9420). On these platforms only the first I2C
                 * controller is used, so if a HID match has no UID we use
                 * "0" as the UID and assign ACPI companion to the first
                 * I2C controller.
                 */
                uid = "0";
        else
                uid = strchr(uid, wd->uid[0]);

        if (!uid || strcmp(uid, wd->uid))
                return 0;

match:
        wd->adev = adev;

        return 1;
}

/* bind auxiliary device to acpi device */
static void ljca_auxdev_acpi_bind(struct ljca_adapter *adap,
                                  struct auxiliary_device *auxdev,
                                  u64 adr, u8 id)
{
        struct ljca_match_ids_walk_data wd = { 0 };
        struct device *dev = adap->dev;
        struct acpi_device *parent;
        char uid[4];

        parent = ACPI_COMPANION(dev);
        if (!parent)
                return;

        /*
         * Currently LJCA hw doesn't use _ADR instead the shipped
         * platforms use _HID to distinguish children devices.
         */
        switch (adr) {
        case LJCA_GPIO_ACPI_ADR:
                wd.ids = ljca_gpio_hids;
                break;
        case LJCA_I2C1_ACPI_ADR:
        case LJCA_I2C2_ACPI_ADR:
                snprintf(uid, sizeof(uid), "%d", id);
                wd.uid = uid;
                wd.ids = ljca_i2c_hids;
                break;
        case LJCA_SPI1_ACPI_ADR:
        case LJCA_SPI2_ACPI_ADR:
                wd.ids = ljca_spi_hids;
                break;
        default:
                dev_warn(dev, "unsupported _ADR\n");
                return;
        }

        acpi_dev_for_each_child(parent, ljca_match_device_ids, &wd);
        if (wd.adev) {
                ACPI_COMPANION_SET(&auxdev->dev, wd.adev);
                return;
        }

        parent = ACPI_COMPANION(dev->parent->parent);
        if (!parent)
                return;

        acpi_dev_for_each_child(parent, ljca_match_device_ids, &wd);
        if (wd.adev)
                ACPI_COMPANION_SET(&auxdev->dev, wd.adev);
}

static void ljca_auxdev_release(struct device *dev)
{
        struct auxiliary_device *auxdev = to_auxiliary_dev(dev);

        kfree(auxdev->dev.platform_data);
}

static int ljca_new_client_device(struct ljca_adapter *adap, u8 type, u8 id,
                                  char *name, void *data, u64 adr)
{
        struct auxiliary_device *auxdev;
        struct ljca_client *client;
        int ret;

        client = kzalloc_obj(*client);
        if (!client) {
                kfree(data);
                return -ENOMEM;
        }

        client->type = type;
        client->id = id;
        client->adapter = adap;
        spin_lock_init(&client->event_cb_lock);

        auxdev = &client->auxdev;
        auxdev->name = name;
        auxdev->id = id;

        auxdev->dev.parent = adap->dev;
        auxdev->dev.platform_data = data;
        auxdev->dev.release = ljca_auxdev_release;

        ret = auxiliary_device_init(auxdev);
        if (ret) {
                kfree(data);
                goto err_free;
        }

        ljca_auxdev_acpi_bind(adap, auxdev, adr, id);

        ret = auxiliary_device_add(auxdev);
        if (ret)
                goto err_uninit;

        list_add_tail(&client->link, &adap->client_list);

        return 0;

err_uninit:
        auxiliary_device_uninit(auxdev);

err_free:
        kfree(client);

        return ret;
}

static int ljca_enumerate_gpio(struct ljca_adapter *adap)
{
        u32 valid_pin[LJCA_MAX_GPIO_NUM / BITS_PER_TYPE(u32)];
        struct ljca_gpio_descriptor *desc;
        struct ljca_gpio_info *gpio_info;
        u8 buf[LJCA_MAX_PAYLOAD_SIZE];
        int ret, gpio_num;
        unsigned int i;

        ret = ljca_send(adap, LJCA_CLIENT_MNG, LJCA_MNG_ENUM_GPIO, NULL, 0, buf,
                        sizeof(buf), true, LJCA_ENUM_CLIENT_TIMEOUT_MS);
        if (ret < 0)
                return ret;

        /* check firmware response */
        desc = (struct ljca_gpio_descriptor *)buf;
        if (ret != struct_size(desc, bank_desc, desc->bank_num))
                return -EINVAL;

        gpio_num = desc->pins_per_bank * desc->bank_num;
        if (gpio_num > LJCA_MAX_GPIO_NUM)
                return -EINVAL;

        /* construct platform data */
        gpio_info = kzalloc_obj(*gpio_info);
        if (!gpio_info)
                return -ENOMEM;
        gpio_info->num = gpio_num;

        for (i = 0; i < desc->bank_num; i++)
                valid_pin[i] = get_unaligned_le32(&desc->bank_desc[i].valid_pins);
        bitmap_from_arr32(gpio_info->valid_pin_map, valid_pin, gpio_num);

        return ljca_new_client_device(adap, LJCA_CLIENT_GPIO, 0, "ljca-gpio",
                                     gpio_info, LJCA_GPIO_ACPI_ADR);
}

static int ljca_enumerate_i2c(struct ljca_adapter *adap)
{
        struct ljca_i2c_descriptor *desc;
        struct ljca_i2c_info *i2c_info;
        u8 buf[LJCA_MAX_PAYLOAD_SIZE];
        unsigned int i;
        int ret;

        ret = ljca_send(adap, LJCA_CLIENT_MNG, LJCA_MNG_ENUM_I2C, NULL, 0, buf,
                        sizeof(buf), true, LJCA_ENUM_CLIENT_TIMEOUT_MS);
        if (ret < 0)
                return ret;

        /* check firmware response */
        desc = (struct ljca_i2c_descriptor *)buf;
        if (ret != struct_size(desc, info, desc->num))
                return -EINVAL;

        for (i = 0; i < desc->num; i++) {
                /* construct platform data */
                i2c_info = kzalloc_obj(*i2c_info);
                if (!i2c_info)
                        return -ENOMEM;

                i2c_info->id = desc->info[i].id;
                i2c_info->capacity = desc->info[i].capacity;
                i2c_info->intr_pin = desc->info[i].intr_pin;

                ret = ljca_new_client_device(adap, LJCA_CLIENT_I2C, i,
                                             "ljca-i2c", i2c_info,
                                             LJCA_I2C1_ACPI_ADR + i);
                if (ret)
                        return ret;
        }

        return 0;
}

static int ljca_enumerate_spi(struct ljca_adapter *adap)
{
        struct ljca_spi_descriptor *desc;
        struct ljca_spi_info *spi_info;
        u8 buf[LJCA_MAX_PAYLOAD_SIZE];
        unsigned int i;
        int ret;

        /* Not all LJCA chips implement SPI, a timeout reading the descriptors is normal */
        ret = ljca_send(adap, LJCA_CLIENT_MNG, LJCA_MNG_ENUM_SPI, NULL, 0, buf,
                        sizeof(buf), true, LJCA_ENUM_CLIENT_TIMEOUT_MS);
        if (ret < 0)
                return (ret == -ETIMEDOUT) ? 0 : ret;

        /* check firmware response */
        desc = (struct ljca_spi_descriptor *)buf;
        if (ret != struct_size(desc, info, desc->num))
                return -EINVAL;

        for (i = 0; i < desc->num; i++) {
                /* construct platform data */
                spi_info = kzalloc_obj(*spi_info);
                if (!spi_info)
                        return -ENOMEM;

                spi_info->id = desc->info[i].id;
                spi_info->capacity = desc->info[i].capacity;

                ret = ljca_new_client_device(adap, LJCA_CLIENT_SPI, i,
                                             "ljca-spi", spi_info,
                                             LJCA_SPI1_ACPI_ADR + i);
                if (ret)
                        return ret;
        }

        return 0;
}

static int ljca_reset_handshake(struct ljca_adapter *adap)
{
        __le32 reset_id = cpu_to_le32(adap->reset_id);
        __le32 reset_id_ret = 0;
        int ret;

        adap->reset_id++;

        ret = ljca_send(adap, LJCA_CLIENT_MNG, LJCA_MNG_RESET, (u8 *)&reset_id,
                        sizeof(__le32), (u8 *)&reset_id_ret, sizeof(__le32),
                        true, LJCA_WRITE_ACK_TIMEOUT_MS);
        if (ret < 0)
                return ret;

        if (reset_id_ret != reset_id)
                return -EINVAL;

        return 0;
}

static int ljca_enumerate_clients(struct ljca_adapter *adap)
{
        struct ljca_client *client, *next;
        int ret;

        ret = ljca_reset_handshake(adap);
        if (ret)
                goto err_kill;

        ret = ljca_enumerate_gpio(adap);
        if (ret) {
                dev_err(adap->dev, "enumerate GPIO error\n");
                goto err_kill;
        }

        ret = ljca_enumerate_i2c(adap);
        if (ret) {
                dev_err(adap->dev, "enumerate I2C error\n");
                goto err_kill;
        }

        ret = ljca_enumerate_spi(adap);
        if (ret) {
                dev_err(adap->dev, "enumerate SPI error\n");
                goto err_kill;
        }

        return 0;

err_kill:
        adap->disconnect = true;

        usb_kill_urb(adap->rx_urb);

        list_for_each_entry_safe_reverse(client, next, &adap->client_list, link) {
                auxiliary_device_delete(&client->auxdev);
                auxiliary_device_uninit(&client->auxdev);

                list_del_init(&client->link);
                kfree(client);
        }

        return ret;
}

static int ljca_probe(struct usb_interface *interface,
                      const struct usb_device_id *id)
{
        struct usb_device *usb_dev = interface_to_usbdev(interface);
        struct usb_host_interface *alt = interface->cur_altsetting;
        struct usb_endpoint_descriptor *ep_in, *ep_out;
        struct device *dev = &interface->dev;
        struct ljca_adapter *adap;
        int ret;

        adap = devm_kzalloc(dev, sizeof(*adap), GFP_KERNEL);
        if (!adap)
                return -ENOMEM;

        /* separate tx buffer allocation for alignment */
        adap->tx_buf = devm_kzalloc(dev, LJCA_MAX_PACKET_SIZE, GFP_KERNEL);
        if (!adap->tx_buf)
                return -ENOMEM;
        adap->tx_buf_len = LJCA_MAX_PACKET_SIZE;

        mutex_init(&adap->mutex);
        spin_lock_init(&adap->lock);
        init_completion(&adap->cmd_completion);
        INIT_LIST_HEAD(&adap->client_list);

        adap->intf = interface;
        adap->usb_dev = usb_dev;
        adap->dev = dev;

        /*
         * find the first bulk in and out endpoints.
         * ignore any others.
         */
        ret = usb_find_common_endpoints(alt, &ep_in, &ep_out, NULL, NULL);
        if (ret) {
                dev_err(dev, "bulk endpoints not found\n");
                goto err_destroy_mutex;
        }
        adap->rx_pipe = usb_rcvbulkpipe(usb_dev, usb_endpoint_num(ep_in));
        adap->tx_pipe = usb_sndbulkpipe(usb_dev, usb_endpoint_num(ep_out));

        /* setup rx buffer */
        adap->rx_len = usb_endpoint_maxp(ep_in);
        adap->rx_buf = devm_kzalloc(dev, adap->rx_len, GFP_KERNEL);
        if (!adap->rx_buf) {
                ret = -ENOMEM;
                goto err_destroy_mutex;
        }

        /* alloc rx urb */
        adap->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
        if (!adap->rx_urb) {
                ret = -ENOMEM;
                goto err_destroy_mutex;
        }
        usb_fill_bulk_urb(adap->rx_urb, usb_dev, adap->rx_pipe,
                          adap->rx_buf, adap->rx_len, ljca_recv, adap);

        usb_set_intfdata(interface, adap);

        /* submit rx urb before enumerate clients */
        ret = usb_submit_urb(adap->rx_urb, GFP_KERNEL);
        if (ret) {
                dev_err(dev, "submit rx urb failed: %d\n", ret);
                goto err_free;
        }

        ret = ljca_enumerate_clients(adap);
        if (ret)
                goto err_free;

        /*
         * This works around problems with ov2740 initialization on some
         * Lenovo platforms. The autosuspend delay, has to be smaller than
         * the delay after setting the reset_gpio line in ov2740_resume().
         * Otherwise the sensor randomly fails to initialize.
         */
        pm_runtime_set_autosuspend_delay(&usb_dev->dev, 10);

        usb_enable_autosuspend(usb_dev);

        return 0;

err_free:
        usb_free_urb(adap->rx_urb);
err_destroy_mutex:
        mutex_destroy(&adap->mutex);

        return ret;
}

static void ljca_disconnect(struct usb_interface *interface)
{
        struct ljca_adapter *adap = usb_get_intfdata(interface);
        struct ljca_client *client, *next;

        adap->disconnect = true;

        usb_kill_urb(adap->rx_urb);

        list_for_each_entry_safe_reverse(client, next, &adap->client_list, link) {
                auxiliary_device_delete(&client->auxdev);
                auxiliary_device_uninit(&client->auxdev);

                list_del_init(&client->link);
                kfree(client);
        }

        usb_free_urb(adap->rx_urb);

        mutex_destroy(&adap->mutex);
}

static int ljca_suspend(struct usb_interface *interface, pm_message_t message)
{
        struct ljca_adapter *adap = usb_get_intfdata(interface);

        usb_kill_urb(adap->rx_urb);

        return 0;
}

static int ljca_resume(struct usb_interface *interface)
{
        struct ljca_adapter *adap = usb_get_intfdata(interface);

        return usb_submit_urb(adap->rx_urb, GFP_KERNEL);
}

static const struct usb_device_id ljca_table[] = {
        { USB_DEVICE(0x8086, 0x0b63) },
        { /* sentinel */ }
};
MODULE_DEVICE_TABLE(usb, ljca_table);

static struct usb_driver ljca_driver = {
        .name = "ljca",
        .id_table = ljca_table,
        .probe = ljca_probe,
        .disconnect = ljca_disconnect,
        .suspend = ljca_suspend,
        .resume = ljca_resume,
        .supports_autosuspend = 1,
};
module_usb_driver(ljca_driver);

MODULE_AUTHOR("Wentong Wu");
MODULE_AUTHOR("Zhifeng Wang <zhifeng.wang@intel.com>");
MODULE_AUTHOR("Lixu Zhang <lixu.zhang@intel.com>");
MODULE_DESCRIPTION("Intel La Jolla Cove Adapter USB driver");
MODULE_LICENSE("GPL");