usb: gadget: Add snapshot of QC USB function drivers

Snapshot of downstream USB function drivers used on QC platforms.  The
drivers are based on msm-5.15.  Removed USB BAM support from QDSS, and
removed f_diag driver.

commit 653f27dbeac4 ("usb: gadget: qdss: Redo the dequeuing requests
during channel close").

Change-Id: I31b8878464452047c849134908dd92f55c748cac
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
This commit is contained in:
Wesley Cheng 2022-05-10 11:21:49 -07:00
parent 16da7c2d8d
commit b38119d08c
13 changed files with 11070 additions and 0 deletions

View File

@ -222,6 +222,30 @@ config USB_F_ACC
config USB_F_AUDIO_SRC
tristate
config USB_F_CDEV
tristate
config USB_F_CCID
tristate
config USB_F_QDSS
tristate
config USB_F_GSI
tristate
config USB_F_FS_IPC_LOGGING
tristate "Enable IPC logging for FunctionFS via f_fs_ipc_log"
depends on IPC_LOGGING
depends on ARM64
help
Enables additional debug messages for FunctionFS driver with the help
of f_fs_ipc_log module and output via IPC Logging mechanism. This can
be useful when troubleshooting transfer stalls or other general
failures and determine if the issue is in the kernel gadget or the
userspace client. Separate IPC log contexts are created for each
function instance at mount time.
# this first set of drivers all depend on bulk-capable hardware.
config USB_CONFIGFS
@ -517,6 +541,50 @@ config USB_CONFIGFS_F_TCM
Both protocols can work on USB2.0 and USB3.0.
UAS utilizes the USB 3.0 feature called streams support.
config USB_CONFIGFS_F_CDEV
tristate "USB Serial Character function"
select USB_F_CDEV
depends on USB_CONFIGFS
help
The serial character function is a generic function driver that
exposes a pair of bulk IN and OUT endpoints which are backed by
a character device and mapped to its read/write routines. The
function also supports a single interrupt IN endpoint for
asynchronous notification to the host. This driver is typically
used to support DUN/NMEA functions.
config USB_CONFIGFS_F_CCID
tristate "USB CCID function"
select USB_F_CCID
depends on USB_CONFIGFS
help
The Chip Card Interface Device (CCID) function implements a USB
interface that exposes a standard CSCID class that consists of a
pair of bulk IN and OUT endpoints and a single interrupt IN
endpoint. This driver provides a character device interface
allowing a userspace component to be able to provide the
implementation necessary to interface with the smartcard.
config USB_CONFIGFS_F_QDSS
tristate "USB QDSS function"
select USB_F_QDSS
depends on USB_CONFIGFS
help
USB QDSS function driver to get hwtracing related data over
USB. USB QDSS function driver which allows communication
between USB BAM and QDSS BAM for QDSS debug functionality
over USB.
config USB_CONFIGFS_F_GSI
tristate "USB GSI function"
select USB_F_GSI
depends on USB_CONFIGFS
help
Generic function driver to support h/w acceleration to IPA
over GSI. This driver provides USB RMNET/RNDIS/ECM/MBIM/DPL
related functionalities using GSI hardware accelerated data
path and control path.
source "drivers/usb/gadget/legacy/Kconfig"
endif # USB_GADGET

View File

@ -54,3 +54,15 @@ usb_f_accessory-y := f_accessory.o
obj-$(CONFIG_USB_F_ACC) += usb_f_accessory.o
usb_f_audio_source-y := f_audio_source.o
obj-$(CONFIG_USB_F_AUDIO_SRC) += usb_f_audio_source.o
obj-$(CONFIG_USB_F_FS_IPC_LOGGING) += f_fs_ipc_log.o
usb_f_cdev-y := f_cdev.o
obj-$(CONFIG_USB_F_CDEV) += usb_f_cdev.o
usb_f_ccid-y := f_ccid.o
obj-$(CONFIG_USB_F_CCID) += usb_f_ccid.o
usb_f_qdss-y := f_qdss.o u_qdss.o
obj-$(CONFIG_USB_F_QDSS) += usb_f_qdss.o
usb_f_gsi-y := f_gsi.o
ifeq ($(CONFIG_USB_F_RNDIS),)
usb_f_gsi-y += rndis.o
endif
obj-$(CONFIG_USB_F_GSI) += usb_f_gsi.o

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,76 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2011, 2017 The Linux Foundation. All rights reserved.
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef __F_CCID_H
#define __F_CCID_H
#define PROTOCOL_TO 0x01
#define PROTOCOL_T1 0x02
#define ABDATA_SIZE 512
/* define for dwFeatures for Smart Card Device Class Descriptors */
/* No special characteristics */
#define CCID_FEATURES_NADA 0x00000000
/* Automatic parameter configuration based on ATR data */
#define CCID_FEATURES_AUTO_PCONF 0x00000002
/* Automatic activation of ICC on inserting */
#define CCID_FEATURES_AUTO_ACTIV 0x00000004
/* Automatic ICC voltage selection */
#define CCID_FEATURES_AUTO_VOLT 0x00000008
/* Automatic ICC clock frequency change */
#define CCID_FEATURES_AUTO_CLOCK 0x00000010
/* Automatic baud rate change */
#define CCID_FEATURES_AUTO_BAUD 0x00000020
/*Automatic parameters negotiation made by the CCID */
#define CCID_FEATURES_AUTO_PNEGO 0x00000040
/* Automatic PPS made by the CCID according to the active parameters */
#define CCID_FEATURES_AUTO_PPS 0x00000080
/* CCID can set ICC in clock stop mode */
#define CCID_FEATURES_ICCSTOP 0x00000100
/* NAD value other than 00 accepted (T=1 protocol in use) */
#define CCID_FEATURES_NAD 0x00000200
/* Automatic IFSD exchange as first exchange (T=1 protocol in use) */
#define CCID_FEATURES_AUTO_IFSD 0x00000400
/* TPDU level exchanges with CCID */
#define CCID_FEATURES_EXC_TPDU 0x00010000
/* Short APDU level exchange with CCID */
#define CCID_FEATURES_EXC_SAPDU 0x00020000
/* Short and Extended APDU level exchange with CCID */
#define CCID_FEATURES_EXC_APDU 0x00040000
/* USB Wake up signaling supported on card insertion and removal */
#define CCID_FEATURES_WAKEUP 0x00100000
#define CCID_NOTIFY_CARD _IOW('C', 1, struct usb_ccid_notification)
#define CCID_NOTIFY_HWERROR _IOW('C', 2, struct usb_ccid_notification)
#define CCID_READ_DTR _IOR('C', 3, int)
struct usb_ccid_notification {
__u8 buf[4];
} __packed;
struct ccid_bulk_in_header {
__u8 bMessageType;
__u32 wLength;
__u8 bSlot;
__u8 bSeq;
__u8 bStatus;
__u8 bError;
__u8 bSpecific;
__u8 abData[ABDATA_SIZE];
__u8 bSizeToSend;
} __packed;
struct ccid_bulk_out_header {
__u8 bMessageType;
__u32 wLength;
__u8 bSlot;
__u8 bSeq;
__u8 bSpecific_0;
__u8 bSpecific_1;
__u8 bSpecific_2;
__u8 APDU[ABDATA_SIZE];
} __packed;
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,904 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/kprobes.h>
#include <linux/fs_parser.h>
#include <linux/ipc_logging.h>
#include <linux/usb/functionfs.h>
#include "u_fs.h"
/* Copied from f_fs.c */
struct ffs_io_data {
bool aio;
bool read;
struct kiocb *kiocb;
struct iov_iter data;
const void *to_free;
char *buf;
struct mm_struct *mm;
struct work_struct work;
struct usb_ep *ep;
struct usb_request *req;
struct sg_table sgt;
bool use_sg;
struct ffs_data *ffs;
};
/* Copied from f_fs.c */
struct ffs_epfile {
struct mutex mutex;
struct ffs_data *ffs;
struct ffs_ep *ep; /* P: ffs->eps_lock */
struct dentry *dentry;
struct ffs_buffer *read_buffer;
#define READ_BUFFER_DROP ((struct ffs_buffer *)ERR_PTR(-ESHUTDOWN))
char name[5];
unsigned char in; /* P: ffs->eps_lock */
unsigned char isoc; /* P: ffs->eps_lock */
unsigned char _pad;
};
/* Copied from f_fs.c */
struct ffs_ep {
struct usb_ep *ep; /* P: ffs->eps_lock */
struct usb_request *req; /* P: epfile->mutex */
/* [0]: full speed, [1]: high speed, [2]: super speed */
struct usb_endpoint_descriptor *descs[3];
u8 num;
int status; /* P: epfile->mutex */
};
/* Copied from f_fs.c */
struct ffs_sb_fill_data {
struct ffs_file_perms perms;
umode_t root_mode;
const char *dev_name;
bool no_disconnect;
struct ffs_data *ffs_data;
};
/* Copied from f_fs.c */
struct ffs_function {
struct usb_configuration *conf;
struct usb_gadget *gadget;
struct ffs_data *ffs;
struct ffs_ep *eps;
u8 eps_revmap[16];
short *interfaces_nums;
struct usb_function function;
};
/* Copied from f_fs.c */
enum ffs_os_desc_type {
FFS_OS_DESC, FFS_OS_DESC_EXT_COMPAT, FFS_OS_DESC_EXT_PROP
};
#define kprobe_log(context, fmt, ...) \
ipc_log_string(context, "%s: " fmt, \
get_kretprobe(ri)->kp.symbol_name, ##__VA_ARGS__)
#define MAX_IPC_INSTANCES 9
/* per-probe private data */
struct kprobe_data {
void *x0;
void *x1;
void *x2;
};
struct ipc_log_work {
struct ffs_data *ffs;
const char *dev_name;
struct work_struct ctxt_work;
};
/* per-device IPC log data */
struct ipc_log {
void *context;
struct ffs_data *ffs;
};
static struct workqueue_struct *ipc_wq;
static struct ipc_log ipc_log_s[MAX_IPC_INSTANCES];
/* Number of devices for f_fs driver */
static int num_devices;
static int ipc_inst_exists(struct ffs_data *ffs)
{
int i = 0;
for (i = 0; i < num_devices; i++)
if (ipc_log_s[i].ffs == ffs)
return i;
return -ENODEV;
}
static void create_ipc_context_work(struct work_struct *w)
{
struct ipc_log_work *ipc_w = container_of(w, struct ipc_log_work,
ctxt_work);
char ipcname[24] = "usb_ffs_";
void *ctx;
if (num_devices >= MAX_IPC_INSTANCES) {
pr_err("Can't create any more FFS log contexts\n");
goto exit;
}
if (ipc_inst_exists(ipc_w->ffs) >= 0)
goto exit;
strlcat(ipcname, ipc_w->dev_name, sizeof(ipcname));
ctx = ipc_log_context_create(10, ipcname, 0);
if (IS_ERR_OR_NULL(ctx)) {
pr_err("%s: Could not create IPC log context for device %s\n",
__func__, ipc_w->dev_name);
goto exit;
}
ipc_log_s[num_devices].context = ctx;
ipc_log_s[num_devices].ffs = ipc_w->ffs;
num_devices++;
exit:
kfree(ipc_w);
}
static void create_ipc_context(const char *dev_name, struct ffs_data *ffs)
{
struct ipc_log_work *ipc_w;
ipc_w = kzalloc(sizeof(*ipc_w), GFP_ATOMIC);
if (!ipc_w)
return;
INIT_WORK(&ipc_w->ctxt_work, create_ipc_context_work);
ipc_w->dev_name = ffs->dev_name;
ipc_w->ffs = ffs;
queue_work(ipc_wq, &ipc_w->ctxt_work);
}
static void *get_ipc_context(struct ffs_data *ffs)
{
int idx = 0;
idx = ipc_inst_exists(ffs);
if (idx >= 0)
return ipc_log_s[idx].context;
/* not found, create a new one now */
create_ipc_context(ffs->dev_name, ffs);
return NULL;
}
static int entry_ffs_user_copy_worker(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
struct work_struct *work = (struct work_struct *)regs->regs[0];
struct ffs_io_data *io_data = container_of(work, struct ffs_io_data, work);
int ret = io_data->req->status ? io_data->req->status :
io_data->req->actual;
struct ffs_data *ffs = io_data->ffs;
void *context = get_ipc_context(ffs);
data->x0 = work;
kprobe_log(context, "enter: ret %d for %s",
ret, io_data->read ? "read" : "write");
return 0;
}
static int entry_ffs_epfile_io(struct kretprobe_instance *ri, struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
struct file *file = (struct file *)regs->regs[0];
struct ffs_io_data *io_data = (struct ffs_io_data *)regs->regs[1];
struct ffs_epfile *epfile = file->private_data;
void *context = get_ipc_context(epfile->ffs);
data->x0 = file;
data->x1 = io_data;
kprobe_log(context, "enter: %s about to queue %zd bytes",
epfile->name, iov_iter_count(&io_data->data));
return 0;
}
static int exit_ffs_epfile_io(struct kretprobe_instance *ri, struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
struct file *file = data->x0;
struct ffs_epfile *epfile = file->private_data;
unsigned long ret = regs_return_value(regs);
void *context = get_ipc_context(epfile->ffs);
kprobe_log(context, "exit: %s ret %zd", epfile->name, ret);
return 0;
}
static int entry_ffs_epfile_async_io_complete(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct usb_request *req = (struct usb_request *)regs->regs[1];
struct ffs_io_data *io_data = req->context;
void *context = get_ipc_context(io_data->ffs);
kprobe_log(context, "enter");
return 0;
}
static int entry_ffs_epfile_write_iter(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
struct kiocb *kiocb = (struct kiocb *)regs->regs[0];
struct ffs_epfile *epfile = kiocb->ki_filp->private_data;
void *context = get_ipc_context(epfile->ffs);
data->x0 = kiocb;
kprobe_log(context, "enter");
return 0;
}
static int exit_ffs_epfile_write_iter(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
struct kiocb *kiocb = data->x0;
struct ffs_epfile *epfile = kiocb->ki_filp->private_data;
void *context = get_ipc_context(epfile->ffs);
unsigned long ret = regs_return_value(regs);
if (ret != -ENOMEM && ret != -EIOCBQUEUED)
kprobe_log(context, "exit: ret %zd", ret);
return 0;
}
static int entry_ffs_epfile_read_iter(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
struct kiocb *kiocb = (struct kiocb *)regs->regs[0];
struct ffs_epfile *epfile = kiocb->ki_filp->private_data;
void *context = get_ipc_context(epfile->ffs);
data->x0 = kiocb;
kprobe_log(context, "enter");
return 0;
}
static int exit_ffs_epfile_read_iter(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
struct kiocb *kiocb = data->x0;
struct ffs_epfile *epfile = kiocb->ki_filp->private_data;
void *context = get_ipc_context(epfile->ffs);
unsigned long ret = regs_return_value(regs);
kprobe_log(context, "exit: ret %zd", ret);
return 0;
}
static int entry_ffs_data_put(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct ffs_data *ffs = (struct ffs_data *)regs->regs[0];
void *context = get_ipc_context(ffs);
unsigned int refcount = refcount_read(&ffs->ref);
kprobe_log(context, "ref %u", refcount);
if (refcount == 1) {
ipc_log_context_destroy(context);
context = NULL;
}
return 0;
}
static int entry_ffs_sb_fill(struct kretprobe_instance *ri, struct pt_regs *regs)
{
struct fs_context *fc = (struct fs_context *)regs->regs[1];
struct ffs_sb_fill_data *data = fc->fs_private;
create_ipc_context(fc->source, data->ffs_data);
return 0;
}
static int entry___ffs_ep0_queue_wait(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
struct ffs_data *ffs = (struct ffs_data *)regs->regs[0];
void *context = get_ipc_context(ffs);
data->x0 = ffs;
kprobe_log(context, "enter: state %d setup_state %d flags %lu",
ffs->state, ffs->setup_state, ffs->flags);
return 0;
}
static int exit___ffs_ep0_queue_wait(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
struct ffs_data *ffs = data->x0;
void *context = get_ipc_context(ffs);
kprobe_log(context, "exit: state %d setup_state %d flags %lu",
ffs->state, ffs->setup_state, ffs->flags);
return 0;
}
static int entry_ffs_ep0_write(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
struct file *file = (struct file *)regs->regs[0];
struct ffs_data *ffs = file->private_data;
unsigned int len = (unsigned int)regs->regs[2];
void *context = get_ipc_context(ffs);
data->x0 = file;
kprobe_log(context, "enter:len %zu state %d setup_state %d flags %lu",
len, ffs->state, ffs->setup_state, ffs->flags);
return 0;
}
static int exit_ffs_ep0_write(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
struct file *file = data->x0;
struct ffs_data *ffs = file->private_data;
void *context = get_ipc_context(ffs);
unsigned long ret = regs_return_value(regs);
if (ret != -EIDRM && ret != -EINVAL)
kprobe_log(context,
"exit:ret %zd state %d setup_state %d flags %lu",
ret, ffs->state, ffs->setup_state, ffs->flags);
return 0;
}
static int entry_ffs_ep0_read(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
struct file *file = (struct file *)regs->regs[0];
size_t len = (size_t)regs->regs[2];
struct ffs_data *ffs = file->private_data;
void *context = get_ipc_context(ffs);
size_t n;
data->x0 = file;
n = min((len / sizeof(struct usb_functionfs_event)), (size_t)ffs->ev.count);
kprobe_log(context, "enter:len %zu state %d setup_state %d flags %lu n %zu",
len, ffs->state, ffs->setup_state, ffs->flags, n);
return 0;
}
static int exit_ffs_ep0_read(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
struct file *file = data->x0;
struct ffs_data *ffs = file->private_data;
void *context = get_ipc_context(ffs);
unsigned long ret = regs_return_value(regs);
kprobe_log(context, "exit:ret %d state %d setup_state %d flags %lu",
ret, ffs->state, ffs->setup_state, ffs->flags);
return 0;
}
static int entry_ffs_ep0_open(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct inode *inode = (struct inode *)regs->regs[0];
struct ffs_data *ffs = inode->i_private;
void *context = get_ipc_context(ffs);
kprobe_log(context, "state %d setup_state %d flags %lu opened %d",
ffs->state, ffs->setup_state, ffs->flags,
atomic_read(&ffs->opened));
return 0;
}
static int entry_ffs_ep0_release(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct file *file = (struct file *)regs->regs[1];
struct ffs_data *ffs = file->private_data;
void *context = get_ipc_context(ffs);
kprobe_log(context, "state %d setup_state %d flags %lu opened %d",
ffs->state, ffs->setup_state, ffs->flags,
atomic_read(&ffs->opened));
return 0;
}
static int entry_ffs_ep0_ioctl(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct file *file = (struct file *)regs->regs[0];
struct ffs_data *ffs = file->private_data;
void *context = get_ipc_context(ffs);
kprobe_log(context, "state %d setup_state %d flags %lu opened %d",
ffs->state, ffs->setup_state, ffs->flags,
atomic_read(&ffs->opened));
return 0;
}
static int entry_ffs_ep0_poll(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
struct file *file = (struct file *)regs->regs[0];
struct ffs_data *ffs = file->private_data;
void *context = get_ipc_context(ffs);
data->x0 = file;
kprobe_log(context, "state %d setup_state %d flags %lu opened %d",
ffs->state, ffs->setup_state, ffs->flags,
atomic_read(&ffs->opened));
return 0;
}
static int exit_ffs_ep0_poll(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
unsigned long ret = regs_return_value(regs);
struct file *file = data->x0;
struct ffs_data *ffs = file->private_data;
void *context = get_ipc_context(ffs);
kprobe_log(context, "exit: mask %u", ret);
return 0;
}
static int entry_ffs_epfile_open(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct inode *inode = (struct inode *)regs->regs[0];
struct ffs_epfile *epfile = inode->i_private;
void *context = get_ipc_context(epfile->ffs);
kprobe_log(context, "%s: state %d setup_state %d flag %lu opened %u",
epfile->name, epfile->ffs->state, epfile->ffs->setup_state,
epfile->ffs->flags, atomic_read(&epfile->ffs->opened));
return 0;
}
static int entry_ffs_aio_cancel(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
struct kiocb *kiocb = (struct kiocb *)regs->regs[0];
struct ffs_epfile *epfile = kiocb->ki_filp->private_data;
void *context = get_ipc_context(epfile->ffs);
data->x0 = kiocb;
kprobe_log(context, "enter:state %d setup_state %d flag %lu",
epfile->ffs->state, epfile->ffs->setup_state,
epfile->ffs->flags);
return 0;
}
static int exit_ffs_aio_cancel(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
unsigned long ret = regs_return_value(regs);
struct kiocb *kiocb = data->x0;
struct ffs_epfile *epfile = kiocb->ki_filp->private_data;
void *context = get_ipc_context(epfile->ffs);
kprobe_log(context, "exit: mask %u", ret);
return 0;
}
static int entry_ffs_epfile_release(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct inode *inode = (struct inode *)regs->regs[0];
struct ffs_epfile *epfile = inode->i_private;
void *context = get_ipc_context(epfile->ffs);
kprobe_log(context, "%s: state %d setup_state %d flag %lu opened %u",
epfile->name, epfile->ffs->state, epfile->ffs->setup_state,
epfile->ffs->flags, atomic_read(&epfile->ffs->opened));
return 0;
}
static int entry_ffs_epfile_ioctl(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
struct file *file = (struct file *)regs->regs[0];
unsigned int code = (unsigned int)regs->regs[1];
unsigned long value = (unsigned long)regs->regs[2];
struct ffs_epfile *epfile = file->private_data;
void *context = get_ipc_context(epfile->ffs);
data->x0 = file;
kprobe_log(context,
"%s: code 0x%08x value %#lx state %d setup_state %d flag %lu",
epfile->name, code, value, epfile->ffs->state,
epfile->ffs->setup_state, epfile->ffs->flags);
return 0;
}
static int exit_ffs_epfile_ioctl(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
unsigned long ret = regs_return_value(regs);
struct file *file = data->x0;
struct ffs_epfile *epfile = file->private_data;
void *context = get_ipc_context(epfile->ffs);
kprobe_log(context, "exit: %s: ret %d\n", epfile->name, ret);
return 0;
}
static int entry_ffs_data_opened(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct ffs_data *ffs = (struct ffs_data *)regs->regs[0];
void *context = get_ipc_context(ffs);
kprobe_log(context,
"enter: state %d setup_state %d flag %lu opened %d ref %d",
ffs->state, ffs->setup_state, ffs->flags,
atomic_read(&ffs->opened), refcount_read(&ffs->ref));
return 0;
}
static int entry_ffs_data_closed(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct ffs_data *ffs = (struct ffs_data *)regs->regs[0];
void *context = get_ipc_context(ffs);
kprobe_log(context, "state %d setup_state %d flag %lu opened %d",
ffs->state, ffs->setup_state, ffs->flags,
atomic_read(&ffs->opened));
return 0;
}
static int entry_ffs_data_clear(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct ffs_data *ffs = (struct ffs_data *)regs->regs[0];
void *context = get_ipc_context(ffs);
kprobe_log(context, "enter: state %d setup_state %d flag %lu",
ffs->state, ffs->setup_state, ffs->flags);
return 0;
}
static int entry_functionfs_bind(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
struct ffs_data *ffs = (struct ffs_data *)regs->regs[0];
void *context = get_ipc_context(ffs);
data->x0 = ffs;
kprobe_log(context, "enter: state %d setup_state %d flag %lu",
ffs->state, ffs->setup_state, ffs->flags);
return 0;
}
static int exit_functionfs_bind(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
struct ffs_data *ffs = data->x0;
void *context = get_ipc_context(ffs);
unsigned long ret = regs_return_value(regs);
kprobe_log(context, "functionfs_bind returned %d", ret);
return 0;
}
static int entry_ffs_func_eps_disable(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct ffs_function *func = (struct ffs_function *)regs->regs[0];
void *context = get_ipc_context(func->ffs);
kprobe_log(context, "enter: state %d setup_state %d flag %lu",
func->ffs->state, func->ffs->setup_state, func->ffs->flags);
return 0;
}
static int entry_ffs_func_bind(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
struct usb_function *f = (struct usb_function *)regs->regs[1];
struct f_fs_opts *ffs_opts =
container_of(f->fi, struct f_fs_opts, func_inst);
/* func->ffs not set yet; get ffs_data via ffs_opts instead */
struct ffs_data *ffs = ffs_opts->dev->ffs_data;
void *context;
if (!ffs)
return 0;
context = get_ipc_context(ffs);
data->x0 = ffs;
kprobe_log(context, "enter");
kprobe_log(context, "_ffs_func_bind",
"enter: state %d setup_state %d flag %lu", ffs->state,
ffs->setup_state, ffs->flags);
return 0;
}
static int exit_ffs_func_bind(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
int ret = (int)regs_return_value(regs);
struct ffs_data *ffs = data->x0;
void *context;
if (!ffs)
return 0;
context = get_ipc_context(ffs);
if (ret < 0)
kprobe_log(context, "exit: ret %d", ret);
return 0;
}
static int entry_ffs_reset_work(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct work_struct *work = (struct work_struct *)regs->regs[0];
struct ffs_data *ffs = container_of(work, struct ffs_data, reset_work);
void *context = get_ipc_context(ffs);
kprobe_log(context, "enter");
return 0;
}
static int entry_ffs_func_set_alt(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
struct usb_function *f = (struct usb_function *)regs->regs[0];
struct ffs_function *func = container_of(f, struct ffs_function, function);
unsigned int alt = (unsigned int)regs->regs[2];
void *context = get_ipc_context(func->ffs);
data->x0 = func;
kprobe_log(context, "enter: alt %d", (int)alt);
return 0;
}
static int entry_ffs_func_disable(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct usb_function *f = (struct usb_function *)regs->regs[0];
struct ffs_function *func = container_of(f, struct ffs_function, function);
void *context = get_ipc_context(func->ffs);
kprobe_log(context, "enter");
return 0;
}
static int entry_ffs_func_setup(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct usb_function *f = (struct usb_function *)regs->regs[0];
struct ffs_function *func = container_of(f, struct ffs_function, function);
struct usb_ctrlrequest *creq = (struct usb_ctrlrequest *)regs->regs[1];
struct ffs_data *ffs = func->ffs;
void *context = get_ipc_context(func->ffs);
kprobe_log(context,
"enter: state %d reqtype=%02x req=%02x wv=%04x wi=%04x wl=%04x",
ffs->state, creq->bRequestType, creq->bRequest,
le16_to_cpu(creq->wValue), le16_to_cpu(creq->wIndex),
le16_to_cpu(creq->wLength));
return 0;
}
static int entry_ffs_func_suspend(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct usb_function *f = (struct usb_function *)regs->regs[0];
struct ffs_function *func = container_of(f, struct ffs_function, function);
void *context = get_ipc_context(func->ffs);
kprobe_log(context, "enter");
return 0;
}
static int entry_ffs_func_resume(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct usb_function *f = (struct usb_function *)regs->regs[0];
struct ffs_function *func = container_of(f, struct ffs_function, function);
void *context = get_ipc_context(func->ffs);
kprobe_log(context, "enter");
return 0;
}
static int entry_ffs_func_unbind(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
struct usb_function *f = (struct usb_function *)regs->regs[1];
struct ffs_function *func = container_of(f, struct ffs_function, function);
struct ffs_data *ffs = func->ffs;
struct f_fs_opts *opts =
container_of(f->fi, struct f_fs_opts, func_inst);
void *context = get_ipc_context(ffs);
data->x1 = ffs;
kprobe_log(context, "enter: state %d setup_state %d flag %lu refcnt %u",
ffs->state, ffs->setup_state, ffs->flags, opts->refcnt);
return 0;
}
static int exit_ffs_func_unbind(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
struct ffs_data *ffs = data->x1;
void *context = get_ipc_context(ffs);
kprobe_log(context, "exit: state %d setup_state %d flag %lu",
ffs->state, ffs->setup_state, ffs->flags);
return 0;
}
static int entry_ffs_closed(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
struct ffs_data *ffs = (struct ffs_data *)regs->regs[0];
void *context = get_ipc_context(ffs);
data->x0 = ffs;
kprobe_log(context, "enter");
return 0;
}
static int exit_ffs_closed(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
struct ffs_data *ffs = data->x0;
struct ffs_dev *ffs_obj = ffs->private_data;
void *context = get_ipc_context(ffs);
if (test_bit(FFS_FL_BOUND, &ffs->flags))
kprobe_log(context, "unreg gadget done");
else if (!ffs_obj || !ffs_obj->opts || ffs_obj->opts->no_configfs ||
!ffs_obj->opts->func_inst.group.cg_item.ci_parent
|| !kref_read(&ffs_obj->opts->func_inst.group.cg_item.ci_kref))
kprobe_log(context, "exit error");
return 0;
}
#define ENTRY_EXIT(name) {\
.handler = exit_##name,\
.entry_handler = entry_##name,\
.data_size = sizeof(struct kprobe_data),\
.maxactive = MAX_IPC_INSTANCES,\
.kp.symbol_name = #name,\
}
#define ENTRY(name) {\
.entry_handler = entry_##name,\
.data_size = sizeof(struct kprobe_data),\
.maxactive = MAX_IPC_INSTANCES,\
.kp.symbol_name = #name,\
}
static struct kretprobe ffsprobes[] = {
ENTRY(ffs_user_copy_worker),
ENTRY_EXIT(ffs_epfile_io),
ENTRY(ffs_epfile_async_io_complete),
ENTRY_EXIT(ffs_epfile_write_iter),
ENTRY_EXIT(ffs_epfile_read_iter),
ENTRY(ffs_data_put),
ENTRY(ffs_sb_fill),
ENTRY_EXIT(__ffs_ep0_queue_wait),
ENTRY_EXIT(ffs_ep0_write),
ENTRY_EXIT(ffs_ep0_read),
ENTRY(ffs_ep0_open),
ENTRY(ffs_ep0_release),
ENTRY(ffs_ep0_ioctl),
ENTRY_EXIT(ffs_ep0_poll),
ENTRY(ffs_epfile_open),
ENTRY_EXIT(ffs_aio_cancel),
ENTRY(ffs_epfile_release),
ENTRY_EXIT(ffs_epfile_ioctl),
ENTRY(ffs_data_opened),
ENTRY(ffs_data_closed),
ENTRY(ffs_data_clear),
ENTRY_EXIT(functionfs_bind),
ENTRY(ffs_func_eps_disable),
ENTRY_EXIT(ffs_func_bind),
ENTRY(ffs_reset_work),
ENTRY(ffs_func_set_alt),
ENTRY(ffs_func_disable),
ENTRY(ffs_func_setup),
ENTRY(ffs_func_suspend),
ENTRY(ffs_func_resume),
ENTRY_EXIT(ffs_func_unbind),
ENTRY_EXIT(ffs_closed)
};
static int __init kretprobe_init(void)
{
int ret;
int i;
ipc_wq = alloc_ordered_workqueue("ipc_wq", 0);
if (!ipc_wq) {
pr_err("%s: Unable to create workqueue ipc_wq\n", __func__);
return -ENOMEM;
}
for (i = 0; i < ARRAY_SIZE(ffsprobes); i++) {
ret = register_kretprobe(&ffsprobes[i]);
if (ret < 0) {
pr_err("register_kretprobe failed at %s, returned %d\n",
ffsprobes[i].kp.symbol_name, ret);
}
}
return 0;
}
static void __exit kretprobe_exit(void)
{
int i;
destroy_workqueue(ipc_wq);
for (i = 0; i < num_devices; i++) {
if (ipc_log_s[i].ffs) {
ipc_log_context_destroy(ipc_log_s[i].context);
ipc_log_s[i].context = NULL;
}
}
for (i = 0; i < ARRAY_SIZE(ffsprobes); i++) {
unregister_kretprobe(&ffsprobes[i]);
/* nmissed > 0 suggests that maxactive was set too low. */
if (ffsprobes[i].nmissed > 0)
pr_info("Missed probing %d instances of %s\n",
ffsprobes[i].nmissed,
ffsprobes[i].kp.symbol_name);
}
}
module_init(kretprobe_init)
module_exit(kretprobe_exit)
MODULE_LICENSE("GPL");

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,88 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef _F_QDSS_H
#define _F_QDSS_H
#include <linux/completion.h>
#include <linux/kernel.h>
#include <linux/ipc_logging.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/usb/composite.h>
#include <linux/usb/usb_qdss.h>
enum qti_port_type {
QTI_PORT_RMNET,
QTI_PORT_DPL,
QTI_NUM_PORTS
};
struct usb_qdss_ch {
const char *name;
struct list_head list;
void (*notify)(void *priv, unsigned int event,
struct qdss_request *d_req, struct usb_qdss_ch *ch);
void *priv;
int ch_type;
};
struct gqdss {
struct usb_function function;
struct usb_ep *ctrl_out;
struct usb_ep *ctrl_in;
struct usb_ep *data;
int (*send_encap_cmd)(enum qti_port_type qport, void *buf, size_t len);
void (*notify_modem)(void *g, enum qti_port_type qport, int cbits);
};
/* struct f_qdss - USB qdss function driver private structure */
struct f_qdss {
struct gqdss port;
struct usb_gadget *gadget;
short int port_num;
u8 ctrl_iface_id;
u8 data_iface_id;
int usb_connected;
bool debug_inface_enabled;
struct usb_request *endless_req;
struct usb_qdss_ch ch;
/* for mdm channel SW path */
struct list_head data_write_pool;
struct list_head queued_data_pool;
struct list_head dequeued_data_pool;
struct work_struct connect_w;
struct work_struct disconnect_w;
spinlock_t lock;
unsigned int data_enabled:1;
unsigned int ctrl_in_enabled:1;
unsigned int ctrl_out_enabled:1;
struct workqueue_struct *wq;
struct mutex mutex;
bool opened; /* protected by 'mutex' */
struct completion dequeue_done;
};
struct usb_qdss_opts {
struct usb_function_instance func_inst;
struct f_qdss *usb_qdss;
char *channel_name;
};
struct qdss_req {
struct usb_request *usb_req;
struct qdss_request *qdss_req;
struct list_head list;
};
int set_qdss_data_connection(struct f_qdss *qdss, int enable);
int alloc_hw_req(struct usb_ep *data_ep);
#endif

View File

@ -0,0 +1,70 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/usb/dwc3-msm.h>
#include "f_qdss.h"
#define NUM_EBC_IN_BUF 2
int alloc_hw_req(struct usb_ep *data_ep)
{
struct usb_request *req = NULL;
struct f_qdss *qdss = data_ep->driver_data;
pr_debug("allocating EBC request\n");
req = usb_ep_alloc_request(data_ep, GFP_ATOMIC);
if (!req) {
pr_err("usb_ep_alloc_request failed\n");
return -ENOMEM;
}
req->length = NUM_EBC_IN_BUF * EBC_TRB_SIZE;
qdss->endless_req = req;
return 0;
}
static int enable_qdss_ebc_data_connection(struct f_qdss *qdss)
{
int ret;
ret = msm_ep_config(qdss->port.data, qdss->endless_req, 1);
if (ret)
pr_err("msm_ep_config failed\n");
return ret;
}
int set_qdss_data_connection(struct f_qdss *qdss, int enable)
{
struct usb_gadget *gadget;
struct device *dev;
int ret = 0;
if (!qdss) {
pr_err("%s: qdss ptr is NULL\n", __func__);
return -EINVAL;
}
gadget = qdss->gadget;
dev = gadget->dev.parent;
pr_debug("%s ch_type:%d\n", __func__, qdss->ch.ch_type);
if (enable) {
ret = enable_qdss_ebc_data_connection(qdss);
} else {
ret = msm_ep_unconfig(qdss->port.data);
if (ret)
pr_err("msm_ep_unconfig failed\n");
}
return ret;
}

View File

@ -0,0 +1,107 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2011, 2017 The Linux Foundation. All rights reserved.
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef __LINUX_USB_CCID_DESC_H
#define __LINUX_USB_CCID_DESC_H
#include <linux/types.h>
/*CCID specification version 1.10*/
#define CCID1_10 0x0110
#define SMART_CARD_DEVICE_CLASS 0x0B
/* Smart Card Device Class Descriptor Type */
#define CCID_DECRIPTOR_TYPE 0x21
/* Table 5.3-1 Summary of CCID Class Specific Request */
#define CCIDGENERICREQ_ABORT 0x01
#define CCIDGENERICREQ_GET_CLOCK_FREQUENCIES 0x02
#define CCIDGENERICREQ_GET_DATA_RATES 0x03
/* 6.1 Command Pipe, Bulk-OUT Messages */
#define PC_TO_RDR_ICCPOWERON 0x62
#define PC_TO_RDR_ICCPOWEROFF 0x63
#define PC_TO_RDR_GETSLOTSTATUS 0x65
#define PC_TO_RDR_XFRBLOCK 0x6F
#define PC_TO_RDR_GETPARAMETERS 0x6C
#define PC_TO_RDR_RESETPARAMETERS 0x6D
#define PC_TO_RDR_SETPARAMETERS 0x61
#define PC_TO_RDR_ESCAPE 0x6B
#define PC_TO_RDR_ICCCLOCK 0x6E
#define PC_TO_RDR_T0APDU 0x6A
#define PC_TO_RDR_SECURE 0x69
#define PC_TO_RDR_MECHANICAL 0x71
#define PC_TO_RDR_ABORT 0x72
#define PC_TO_RDR_SETDATARATEANDCLOCKFREQUENCY 0x73
/* 6.2 Response Pipe, Bulk-IN Messages */
#define RDR_TO_PC_DATABLOCK 0x80
#define RDR_TO_PC_SLOTSTATUS 0x81
#define RDR_TO_PC_PARAMETERS 0x82
#define RDR_TO_PC_ESCAPE 0x83
#define RDR_TO_PC_DATARATEANDCLOCKFREQUENCY 0x84
/* 6.3 Interrupt-IN Messages */
#define RDR_TO_PC_NOTIFYSLOTCHANGE 0x50
#define RDR_TO_PC_HARDWAREERROR 0x51
/* Table 6.2-2 Slot error register when bmCommandStatus = 1 */
#define CMD_ABORTED 0xFF
#define ICC_MUTE 0xFE
#define XFR_PARITY_ERROR 0xFD
#define XFR_OVERRUN 0xFC
#define HW_ERROR 0xFB
#define BAD_ATR_TS 0xF8
#define BAD_ATR_TCK 0xF7
#define ICC_PROTOCOL_NOT_SUPPORTED 0xF6
#define ICC_CLASS_NOT_SUPPORTED 0xF5
#define PROCEDURE_BYTE_CONFLICT 0xF4
#define DEACTIVATED_PROTOCOL 0xF3
#define BUSY_WITH_AUTO_SEQUENCE 0xF2
#define PIN_TIMEOUT 0xF0
#define PIN_CANCELLED 0xEF
#define CMD_SLOT_BUSY 0xE0
/* CCID rev 1.1, p.27 */
#define VOLTS_AUTO 0x00
#define VOLTS_5_0 0x01
#define VOLTS_3_0 0x02
#define VOLTS_1_8 0x03
/* 6.3.1 RDR_to_PC_NotifySlotChange */
#define ICC_NOT_PRESENT 0x00
#define ICC_PRESENT 0x01
#define ICC_CHANGE 0x02
#define ICC_INSERTED_EVENT (ICC_PRESENT+ICC_CHANGE)
/* Identifies the length of type of subordinate descriptors of a CCID device
* Table 5.1-1 Smart Card Device Class descriptors
*/
struct usb_ccid_class_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__u16 bcdCCID;
__u8 bMaxSlotIndex;
__u8 bVoltageSupport;
__u32 dwProtocols;
__u32 dwDefaultClock;
__u32 dwMaximumClock;
__u8 bNumClockSupported;
__u32 dwDataRate;
__u32 dwMaxDataRate;
__u8 bNumDataRatesSupported;
__u32 dwMaxIFSD;
__u32 dwSynchProtocols;
__u32 dwMechanical;
__u32 dwFeatures;
__u32 dwMaxCCIDMessageLength;
__u8 bClassGetResponse;
__u8 bClassEnvelope;
__u16 wLcdLayout;
__u8 bPINSupport;
__u8 bMaxCCIDBusySlots;
} __packed;
#endif

View File

@ -0,0 +1,69 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2012-2013, 2017-2020 The Linux Foundation. All rights reserved.
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef __LINUX_USB_QDSS_H
#define __LINUX_USB_QDSS_H
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/scatterlist.h>
#define USB_QDSS_CH_EBC "qdss_ebc"
#define USB_QDSS_CH_MDM "qdss_mdm"
#define USB_QDSS_CH_SW "qdss_sw"
struct qdss_request {
char *buf;
int length;
int actual;
int status;
void *context;
struct scatterlist *sg;
unsigned int num_sgs;
};
struct usb_qdss_ch;
enum qdss_state {
USB_QDSS_CONNECT,
USB_QDSS_DISCONNECT,
USB_QDSS_CTRL_READ_DONE,
USB_QDSS_DATA_WRITE_DONE,
};
#if IS_ENABLED(CONFIG_USB_F_QDSS)
struct usb_qdss_ch *usb_qdss_open(const char *name, void *priv,
void (*notify)(void *priv, unsigned int event,
struct qdss_request *d_req, struct usb_qdss_ch *ch));
void usb_qdss_close(struct usb_qdss_ch *ch);
int usb_qdss_alloc_req(struct usb_qdss_ch *ch, int n_write);
void usb_qdss_free_req(struct usb_qdss_ch *ch);
int usb_qdss_write(struct usb_qdss_ch *ch, struct qdss_request *d_req);
#else
static inline struct usb_qdss_ch *usb_qdss_open(const char *name, void *priv,
void (*n)(void *, unsigned int event,
struct qdss_request *d, struct usb_qdss_ch *c))
{
return ERR_PTR(-ENODEV);
}
static inline int usb_qdss_write(struct usb_qdss_ch *c, struct qdss_request *d)
{
return -ENODEV;
}
static inline int usb_qdss_alloc_req(struct usb_qdss_ch *c, int n_wr, int n_rd)
{
return -ENODEV;
}
static inline void usb_qdss_close(struct usb_qdss_ch *ch) { }
static inline void usb_qdss_free_req(struct usb_qdss_ch *ch) { }
#endif /* CONFIG_USB_F_QDSS */
#endif