// SPDX-License-Identifier: GPL-2.0 /* * usb.c - Hardware dependent module for USB * * Copyright (C) 2013-2015 Microchip Technology Germany II GmbH & Co. KG */ #include <linux/module.h> #include <linux/fs.h> #include <linux/usb.h> #include <linux/slab.h> #include <linux/init.h> #include <linux/cdev.h> #include <linux/device.h> #include <linux/list.h> #include <linux/completion.h> #include <linux/mutex.h> #include <linux/spinlock.h> #include <linux/interrupt.h> #include <linux/workqueue.h> #include <linux/sysfs.h> #include <linux/dma-mapping.h> #include <linux/etherdevice.h> #include <linux/uaccess.h> #include <linux/most.h> #define USB_MTU … #define NO_ISOCHRONOUS_URB … #define AV_PACKETS_PER_XACT … #define BUF_CHAIN_SIZE … #define MAX_NUM_ENDPOINTS … #define MAX_SUFFIX_LEN … #define MAX_STRING_LEN … #define MAX_BUF_SIZE … #define USB_VENDOR_ID_SMSC … #define USB_DEV_ID_BRDG … #define USB_DEV_ID_OS81118 … #define USB_DEV_ID_OS81119 … #define USB_DEV_ID_OS81210 … /* DRCI Addresses */ #define DRCI_REG_NI_STATE … #define DRCI_REG_PACKET_BW … #define DRCI_REG_NODE_ADDR … #define DRCI_REG_NODE_POS … #define DRCI_REG_MEP_FILTER … #define DRCI_REG_HASH_TBL0 … #define DRCI_REG_HASH_TBL1 … #define DRCI_REG_HASH_TBL2 … #define DRCI_REG_HASH_TBL3 … #define DRCI_REG_HW_ADDR_HI … #define DRCI_REG_HW_ADDR_MI … #define DRCI_REG_HW_ADDR_LO … #define DRCI_REG_BASE … #define DRCI_COMMAND … #define DRCI_READ_REQ … #define DRCI_WRITE_REQ … /** * struct most_dci_obj - Direct Communication Interface * @kobj:position in sysfs * @usb_device: pointer to the usb device * @reg_addr: register address for arbitrary DCI access */ struct most_dci_obj { … }; #define to_dci_obj(p) … struct most_dev; struct clear_hold_work { … }; #define to_clear_hold_work(w) … /** * struct most_dev - holds all usb interface specific stuff * @usb_device: pointer to usb device * @iface: hardware interface * @cap: channel capabilities * @conf: channel configuration * @dci: direct communication interface of hardware * @ep_address: endpoint address table * @description: device description * @suffix: suffix for channel name * @channel_lock: synchronize channel access * @padding_active: indicates channel uses padding * @is_channel_healthy: health status table of each channel * @busy_urbs: list of anchored items * @io_mutex: synchronize I/O with disconnect * @link_stat_timer: timer for link status reports * @poll_work_obj: work for polling link status */ struct most_dev { … }; #define to_mdev(d) … #define to_mdev_from_dev(d) … #define to_mdev_from_work(w) … static void wq_clear_halt(struct work_struct *wq_obj); static void wq_netinfo(struct work_struct *wq_obj); /** * drci_rd_reg - read a DCI register * @dev: usb device * @reg: register address * @buf: buffer to store data * * This is reads data from INIC's direct register communication interface */ static inline int drci_rd_reg(struct usb_device *dev, u16 reg, u16 *buf) { … } /** * drci_wr_reg - write a DCI register * @dev: usb device * @reg: register address * @data: data to write * * This is writes data to INIC's direct register communication interface */ static inline int drci_wr_reg(struct usb_device *dev, u16 reg, u16 data) { … } static inline int start_sync_ep(struct usb_device *usb_dev, u16 ep) { … } /** * get_stream_frame_size - calculate frame size of current configuration * @dev: device structure * @cfg: channel configuration */ static unsigned int get_stream_frame_size(struct device *dev, struct most_channel_config *cfg) { … } /** * hdm_poison_channel - mark buffers of this channel as invalid * @iface: pointer to the interface * @channel: channel ID * * This unlinks all URBs submitted to the HCD, * calls the associated completion function of the core and removes * them from the list. * * Returns 0 on success or error code otherwise. */ static int hdm_poison_channel(struct most_interface *iface, int channel) { … } /** * hdm_add_padding - add padding bytes * @mdev: most device * @channel: channel ID * @mbo: buffer object * * This inserts the INIC hardware specific padding bytes into a streaming * channel's buffer */ static int hdm_add_padding(struct most_dev *mdev, int channel, struct mbo *mbo) { … } /** * hdm_remove_padding - remove padding bytes * @mdev: most device * @channel: channel ID * @mbo: buffer object * * This takes the INIC hardware specific padding bytes off a streaming * channel's buffer. */ static int hdm_remove_padding(struct most_dev *mdev, int channel, struct mbo *mbo) { … } /** * hdm_write_completion - completion function for submitted Tx URBs * @urb: the URB that has been completed * * This checks the status of the completed URB. In case the URB has been * unlinked before, it is immediately freed. On any other error the MBO * transfer flag is set. On success it frees allocated resources and calls * the completion function. * * Context: interrupt! */ static void hdm_write_completion(struct urb *urb) { … } /** * hdm_read_completion - completion function for submitted Rx URBs * @urb: the URB that has been completed * * This checks the status of the completed URB. In case the URB has been * unlinked before it is immediately freed. On any other error the MBO transfer * flag is set. On success it frees allocated resources, removes * padding bytes -if necessary- and calls the completion function. * * Context: interrupt! */ static void hdm_read_completion(struct urb *urb) { … } /** * hdm_enqueue - receive a buffer to be used for data transfer * @iface: interface to enqueue to * @channel: ID of the channel * @mbo: pointer to the buffer object * * This allocates a new URB and fills it according to the channel * that is being used for transmission of data. Before the URB is * submitted it is stored in the private anchor list. * * Returns 0 on success. On any error the URB is freed and a error code * is returned. * * Context: Could in _some_ cases be interrupt! */ static int hdm_enqueue(struct most_interface *iface, int channel, struct mbo *mbo) { … } static void *hdm_dma_alloc(struct mbo *mbo, u32 size) { … } static void hdm_dma_free(struct mbo *mbo, u32 size) { … } /** * hdm_configure_channel - receive channel configuration from core * @iface: interface * @channel: channel ID * @conf: structure that holds the configuration information * * The attached network interface controller (NIC) supports a padding mode * to avoid short packets on USB, hence increasing the performance due to a * lower interrupt load. This mode is default for synchronous data and can * be switched on for isochronous data. In case padding is active the * driver needs to know the frame size of the payload in order to calculate * the number of bytes it needs to pad when transmitting or to cut off when * receiving data. * */ static int hdm_configure_channel(struct most_interface *iface, int channel, struct most_channel_config *conf) { … } /** * hdm_request_netinfo - request network information * @iface: pointer to interface * @channel: channel ID * * This is used as trigger to set up the link status timer that * polls for the NI state of the INIC every 2 seconds. * */ static void hdm_request_netinfo(struct most_interface *iface, int channel, void (*on_netinfo)(struct most_interface *, unsigned char, unsigned char *)) { … } /** * link_stat_timer_handler - schedule work obtaining mac address and link status * @t: pointer to timer_list which holds a pointer to the USB device instance * * The handler runs in interrupt context. That's why we need to defer the * tasks to a work queue. */ static void link_stat_timer_handler(struct timer_list *t) { … } /** * wq_netinfo - work queue function to deliver latest networking information * @wq_obj: object that holds data for our deferred work to do * * This retrieves the network interface status of the USB INIC */ static void wq_netinfo(struct work_struct *wq_obj) { … } /** * wq_clear_halt - work queue function * @wq_obj: work_struct object to execute * * This sends a clear_halt to the given USB pipe. */ static void wq_clear_halt(struct work_struct *wq_obj) { … } /* * hdm_usb_fops - file operation table for USB driver */ static const struct file_operations hdm_usb_fops = …; /* * usb_device_id - ID table for HCD device probing */ static const struct usb_device_id usbid[] = …; struct regs { … }; static const struct regs ro_regs[] = …; static const struct regs rw_regs[] = …; static int get_stat_reg_addr(const struct regs *regs, int size, const char *name, u16 *reg_addr) { … } #define get_static_reg_addr(regs, name, reg_addr) … static ssize_t value_show(struct device *dev, struct device_attribute *attr, char *buf) { … } static ssize_t value_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { … } static DEVICE_ATTR(ni_state, 0444, value_show, NULL); static DEVICE_ATTR(packet_bandwidth, 0444, value_show, NULL); static DEVICE_ATTR(node_address, 0444, value_show, NULL); static DEVICE_ATTR(node_position, 0444, value_show, NULL); static DEVICE_ATTR(sync_ep, 0200, NULL, value_store); static DEVICE_ATTR(mep_filter, 0644, value_show, value_store); static DEVICE_ATTR(mep_hash0, 0644, value_show, value_store); static DEVICE_ATTR(mep_hash1, 0644, value_show, value_store); static DEVICE_ATTR(mep_hash2, 0644, value_show, value_store); static DEVICE_ATTR(mep_hash3, 0644, value_show, value_store); static DEVICE_ATTR(mep_eui48_hi, 0644, value_show, value_store); static DEVICE_ATTR(mep_eui48_mi, 0644, value_show, value_store); static DEVICE_ATTR(mep_eui48_lo, 0644, value_show, value_store); static DEVICE_ATTR(arb_address, 0644, value_show, value_store); static DEVICE_ATTR(arb_value, 0644, value_show, value_store); static struct attribute *dci_attrs[] = …; ATTRIBUTE_GROUPS(…); static void release_dci(struct device *dev) { … } static void release_mdev(struct device *dev) { … } /** * hdm_probe - probe function of USB device driver * @interface: Interface of the attached USB device * @id: Pointer to the USB ID table. * * This allocates and initializes the device instance, adds the new * entry to the internal list, scans the USB descriptors and registers * the interface with the core. * Additionally, the DCI objects are created and the hardware is sync'd. * * Return 0 on success. In case of an error a negative number is returned. */ static int hdm_probe(struct usb_interface *interface, const struct usb_device_id *id) { … } /** * hdm_disconnect - disconnect function of USB device driver * @interface: Interface of the attached USB device * * This deregisters the interface with the core, removes the kernel timer * and frees resources. * * Context: hub kernel thread */ static void hdm_disconnect(struct usb_interface *interface) { … } static int hdm_suspend(struct usb_interface *interface, pm_message_t message) { … } static int hdm_resume(struct usb_interface *interface) { … } static struct usb_driver hdm_usb = …; module_usb_driver(…) …; MODULE_LICENSE(…) …; MODULE_AUTHOR(…) …; MODULE_DESCRIPTION(…) …;