linux/drivers/char/xillybus/xillyusb.c

// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright 2020 Xillybus Ltd, http://xillybus.com
 *
 * Driver for the XillyUSB FPGA/host framework.
 *
 * This driver interfaces with a special IP core in an FPGA, setting up
 * a pipe between a hardware FIFO in the programmable logic and a device
 * file in the host. The number of such pipes and their attributes are
 * set up on the logic. This driver detects these automatically and
 * creates the device files accordingly.
 */

#include <linux/types.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/device.h>
#include <linux/module.h>
#include <asm/byteorder.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/spinlock.h>
#include <linux/mutex.h>
#include <linux/workqueue.h>
#include <linux/crc32.h>
#include <linux/poll.h>
#include <linux/delay.h>
#include <linux/usb.h>

#include "xillybus_class.h"

MODULE_DESCRIPTION();
MODULE_AUTHOR();
MODULE_ALIAS();
MODULE_LICENSE();

#define XILLY_RX_TIMEOUT
#define XILLY_RESPONSE_TIMEOUT

#define BUF_SIZE_ORDER
#define BUFNUM
#define LOG2_IDT_FIFO_SIZE
#define LOG2_INITIAL_FIFO_BUF_SIZE

#define MSG_EP_NUM
#define IN_EP_NUM

static const char xillyname[] =;

static unsigned int fifo_buf_order;
static struct workqueue_struct *wakeup_wq;

#define USB_VENDOR_ID_XILINX
#define USB_VENDOR_ID_ALTERA

#define USB_PRODUCT_ID_XILLYUSB

static const struct usb_device_id xillyusb_table[] =;

MODULE_DEVICE_TABLE(usb, xillyusb_table);

struct xillyusb_dev;

struct xillyfifo {};

struct xillyusb_channel;

struct xillyusb_endpoint {};

struct xillyusb_channel {};

struct xillybuffer {};

struct xillyusb_dev {};

/*
 * kref_mutex is used in xillyusb_open() to prevent the xillyusb_dev
 * struct from being freed during the gap between being found by
 * xillybus_find_inode() and having its reference count incremented.
 */

static DEFINE_MUTEX(kref_mutex);

/* FPGA to host opcodes */
enum {};

/* Host to FPGA opcodes */
enum {};

/*
 * fifo_write() and fifo_read() are NOT reentrant (i.e. concurrent multiple
 * calls to each on the same FIFO is not allowed) however it's OK to have
 * threads calling each of the two functions once on the same FIFO, and
 * at the same time.
 */

static int fifo_write(struct xillyfifo *fifo,
		      const void *data, unsigned int len,
		      int (*copier)(void *, const void *, int))
{}

static int fifo_read(struct xillyfifo *fifo,
		     void *data, unsigned int len,
		     int (*copier)(void *, const void *, int))
{}

/*
 * These three wrapper functions are used as the @copier argument to
 * fifo_write() and fifo_read(), so that they can work directly with
 * user memory as well.
 */

static int xilly_copy_from_user(void *dst, const void *src, int n)
{}

static int xilly_copy_to_user(void *dst, const void *src, int n)
{}

static int xilly_memcpy(void *dst, const void *src, int n)
{}

static int fifo_init(struct xillyfifo *fifo,
		     unsigned int log2_size)
{}

static void fifo_mem_release(struct xillyfifo *fifo)
{}

/*
 * When endpoint_quiesce() returns, the endpoint has no URBs submitted,
 * won't accept any new URB submissions, and its related work item doesn't
 * and won't run anymore.
 */

static void endpoint_quiesce(struct xillyusb_endpoint *ep)
{}

/*
 * Note that endpoint_dealloc() also frees fifo memory (if allocated), even
 * though endpoint_alloc doesn't allocate that memory.
 */

static void endpoint_dealloc(struct xillyusb_endpoint *ep)
{}

static struct xillyusb_endpoint
*endpoint_alloc(struct xillyusb_dev *xdev,
		u8 ep_num,
		void (*work)(struct work_struct *),
		unsigned int order,
		int bufnum)
{}

static void cleanup_dev(struct kref *kref)
{}

/*
 * @process_in_mutex is taken to ensure that bulk_in_work() won't call
 * process_bulk_in() after wakeup_all()'s execution: The latter zeroes all
 * @read_data_ok entries, which will make process_bulk_in() report false
 * errors if executed. The mechanism relies on that xdev->error is assigned
 * a non-zero value by report_io_error() prior to queueing wakeup_all(),
 * which prevents bulk_in_work() from calling process_bulk_in().
 */

static void wakeup_all(struct work_struct *work)
{}

static void report_io_error(struct xillyusb_dev *xdev,
			    int errcode)
{}

/*
 * safely_assign_in_fifo() changes the value of chan->in_fifo and ensures
 * the previous pointer is never used after its return.
 */

static void safely_assign_in_fifo(struct xillyusb_channel *chan,
				  struct xillyfifo *fifo)
{}

static void bulk_in_completer(struct urb *urb)
{}

static void bulk_out_completer(struct urb *urb)
{}

static void try_queue_bulk_in(struct xillyusb_endpoint *ep)
{}

static void try_queue_bulk_out(struct xillyusb_endpoint *ep)
{}

static void bulk_out_work(struct work_struct *work)
{}

static int process_in_opcode(struct xillyusb_dev *xdev,
			     int opcode,
			     int chan_num)
{}

static int process_bulk_in(struct xillybuffer *xb)
{}

static void bulk_in_work(struct work_struct *work)
{}

static int xillyusb_send_opcode(struct xillyusb_dev *xdev,
				int chan_num, char opcode, u32 data)
{}

/*
 * Note that flush_downstream() merely waits for the data to arrive to
 * the application logic at the FPGA -- unlike PCIe Xillybus' counterpart,
 * it does nothing to make it happen (and neither is it necessary).
 *
 * This function is not reentrant for the same @chan, but this is covered
 * by the fact that for any given @chan, it's called either by the open,
 * write, llseek and flush fops methods, which can't run in parallel (and the
 * write + flush and llseek method handlers are protected with out_mutex).
 *
 * chan->flushed is there to avoid multiple flushes at the same position,
 * in particular as a result of programs that close the file descriptor
 * e.g. after a dup2() for redirection.
 */

static int flush_downstream(struct xillyusb_channel *chan,
			    long timeout,
			    bool interruptible)
{}

/* request_read_anything(): Ask the FPGA for any little amount of data */
static int request_read_anything(struct xillyusb_channel *chan,
				 char opcode)
{}

static int xillyusb_open(struct inode *inode, struct file *filp)
{}

static ssize_t xillyusb_read(struct file *filp, char __user *userbuf,
			     size_t count, loff_t *f_pos)
{}

static int xillyusb_flush(struct file *filp, fl_owner_t id)
{}

static ssize_t xillyusb_write(struct file *filp, const char __user *userbuf,
			      size_t count, loff_t *f_pos)
{}

static int xillyusb_release(struct inode *inode, struct file *filp)
{}

/*
 * Xillybus' API allows device nodes to be seekable, giving the user
 * application access to a RAM array on the FPGA (or logic emulating it).
 */

static loff_t xillyusb_llseek(struct file *filp, loff_t offset, int whence)
{}

static __poll_t xillyusb_poll(struct file *filp, poll_table *wait)
{}

static const struct file_operations xillyusb_fops =;

static int xillyusb_setup_base_eps(struct xillyusb_dev *xdev)
{}

static int setup_channels(struct xillyusb_dev *xdev,
			  __le16 *chandesc,
			  int num_channels)
{}

static int xillyusb_discovery(struct usb_interface *interface)
{}

static int xillyusb_probe(struct usb_interface *interface,
			  const struct usb_device_id *id)
{}

static void xillyusb_disconnect(struct usb_interface *interface)
{}

static struct usb_driver xillyusb_driver =;

static int __init xillyusb_init(void)
{}

static void __exit xillyusb_exit(void)
{}

module_init();
module_exit(xillyusb_exit);