18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * usb.c - Hardware dependent module for USB
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2013-2015 Microchip Technology Germany II GmbH & Co. KG
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/module.h>
98c2ecf20Sopenharmony_ci#include <linux/fs.h>
108c2ecf20Sopenharmony_ci#include <linux/usb.h>
118c2ecf20Sopenharmony_ci#include <linux/slab.h>
128c2ecf20Sopenharmony_ci#include <linux/init.h>
138c2ecf20Sopenharmony_ci#include <linux/cdev.h>
148c2ecf20Sopenharmony_ci#include <linux/device.h>
158c2ecf20Sopenharmony_ci#include <linux/list.h>
168c2ecf20Sopenharmony_ci#include <linux/completion.h>
178c2ecf20Sopenharmony_ci#include <linux/mutex.h>
188c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
198c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
208c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
218c2ecf20Sopenharmony_ci#include <linux/sysfs.h>
228c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
238c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
248c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
258c2ecf20Sopenharmony_ci#include <linux/most.h>
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#define USB_MTU			512
288c2ecf20Sopenharmony_ci#define NO_ISOCHRONOUS_URB	0
298c2ecf20Sopenharmony_ci#define AV_PACKETS_PER_XACT	2
308c2ecf20Sopenharmony_ci#define BUF_CHAIN_SIZE		0xFFFF
318c2ecf20Sopenharmony_ci#define MAX_NUM_ENDPOINTS	30
328c2ecf20Sopenharmony_ci#define MAX_SUFFIX_LEN		10
338c2ecf20Sopenharmony_ci#define MAX_STRING_LEN		80
348c2ecf20Sopenharmony_ci#define MAX_BUF_SIZE		0xFFFF
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#define USB_VENDOR_ID_SMSC	0x0424  /* VID: SMSC */
378c2ecf20Sopenharmony_ci#define USB_DEV_ID_BRDG		0xC001  /* PID: USB Bridge */
388c2ecf20Sopenharmony_ci#define USB_DEV_ID_OS81118	0xCF18  /* PID: USB OS81118 */
398c2ecf20Sopenharmony_ci#define USB_DEV_ID_OS81119	0xCF19  /* PID: USB OS81119 */
408c2ecf20Sopenharmony_ci#define USB_DEV_ID_OS81210	0xCF30  /* PID: USB OS81210 */
418c2ecf20Sopenharmony_ci/* DRCI Addresses */
428c2ecf20Sopenharmony_ci#define DRCI_REG_NI_STATE	0x0100
438c2ecf20Sopenharmony_ci#define DRCI_REG_PACKET_BW	0x0101
448c2ecf20Sopenharmony_ci#define DRCI_REG_NODE_ADDR	0x0102
458c2ecf20Sopenharmony_ci#define DRCI_REG_NODE_POS	0x0103
468c2ecf20Sopenharmony_ci#define DRCI_REG_MEP_FILTER	0x0140
478c2ecf20Sopenharmony_ci#define DRCI_REG_HASH_TBL0	0x0141
488c2ecf20Sopenharmony_ci#define DRCI_REG_HASH_TBL1	0x0142
498c2ecf20Sopenharmony_ci#define DRCI_REG_HASH_TBL2	0x0143
508c2ecf20Sopenharmony_ci#define DRCI_REG_HASH_TBL3	0x0144
518c2ecf20Sopenharmony_ci#define DRCI_REG_HW_ADDR_HI	0x0145
528c2ecf20Sopenharmony_ci#define DRCI_REG_HW_ADDR_MI	0x0146
538c2ecf20Sopenharmony_ci#define DRCI_REG_HW_ADDR_LO	0x0147
548c2ecf20Sopenharmony_ci#define DRCI_REG_BASE		0x1100
558c2ecf20Sopenharmony_ci#define DRCI_COMMAND		0x02
568c2ecf20Sopenharmony_ci#define DRCI_READ_REQ		0xA0
578c2ecf20Sopenharmony_ci#define DRCI_WRITE_REQ		0xA1
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci/**
608c2ecf20Sopenharmony_ci * struct most_dci_obj - Direct Communication Interface
618c2ecf20Sopenharmony_ci * @kobj:position in sysfs
628c2ecf20Sopenharmony_ci * @usb_device: pointer to the usb device
638c2ecf20Sopenharmony_ci * @reg_addr: register address for arbitrary DCI access
648c2ecf20Sopenharmony_ci */
658c2ecf20Sopenharmony_cistruct most_dci_obj {
668c2ecf20Sopenharmony_ci	struct device dev;
678c2ecf20Sopenharmony_ci	struct usb_device *usb_device;
688c2ecf20Sopenharmony_ci	u16 reg_addr;
698c2ecf20Sopenharmony_ci};
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci#define to_dci_obj(p) container_of(p, struct most_dci_obj, dev)
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cistruct most_dev;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistruct clear_hold_work {
768c2ecf20Sopenharmony_ci	struct work_struct ws;
778c2ecf20Sopenharmony_ci	struct most_dev *mdev;
788c2ecf20Sopenharmony_ci	unsigned int channel;
798c2ecf20Sopenharmony_ci	int pipe;
808c2ecf20Sopenharmony_ci};
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci#define to_clear_hold_work(w) container_of(w, struct clear_hold_work, ws)
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci/**
858c2ecf20Sopenharmony_ci * struct most_dev - holds all usb interface specific stuff
868c2ecf20Sopenharmony_ci * @usb_device: pointer to usb device
878c2ecf20Sopenharmony_ci * @iface: hardware interface
888c2ecf20Sopenharmony_ci * @cap: channel capabilities
898c2ecf20Sopenharmony_ci * @conf: channel configuration
908c2ecf20Sopenharmony_ci * @dci: direct communication interface of hardware
918c2ecf20Sopenharmony_ci * @ep_address: endpoint address table
928c2ecf20Sopenharmony_ci * @description: device description
938c2ecf20Sopenharmony_ci * @suffix: suffix for channel name
948c2ecf20Sopenharmony_ci * @channel_lock: synchronize channel access
958c2ecf20Sopenharmony_ci * @padding_active: indicates channel uses padding
968c2ecf20Sopenharmony_ci * @is_channel_healthy: health status table of each channel
978c2ecf20Sopenharmony_ci * @busy_urbs: list of anchored items
988c2ecf20Sopenharmony_ci * @io_mutex: synchronize I/O with disconnect
998c2ecf20Sopenharmony_ci * @link_stat_timer: timer for link status reports
1008c2ecf20Sopenharmony_ci * @poll_work_obj: work for polling link status
1018c2ecf20Sopenharmony_ci */
1028c2ecf20Sopenharmony_cistruct most_dev {
1038c2ecf20Sopenharmony_ci	struct device dev;
1048c2ecf20Sopenharmony_ci	struct usb_device *usb_device;
1058c2ecf20Sopenharmony_ci	struct most_interface iface;
1068c2ecf20Sopenharmony_ci	struct most_channel_capability *cap;
1078c2ecf20Sopenharmony_ci	struct most_channel_config *conf;
1088c2ecf20Sopenharmony_ci	struct most_dci_obj *dci;
1098c2ecf20Sopenharmony_ci	u8 *ep_address;
1108c2ecf20Sopenharmony_ci	char description[MAX_STRING_LEN];
1118c2ecf20Sopenharmony_ci	char suffix[MAX_NUM_ENDPOINTS][MAX_SUFFIX_LEN];
1128c2ecf20Sopenharmony_ci	spinlock_t channel_lock[MAX_NUM_ENDPOINTS]; /* sync channel access */
1138c2ecf20Sopenharmony_ci	bool padding_active[MAX_NUM_ENDPOINTS];
1148c2ecf20Sopenharmony_ci	bool is_channel_healthy[MAX_NUM_ENDPOINTS];
1158c2ecf20Sopenharmony_ci	struct clear_hold_work clear_work[MAX_NUM_ENDPOINTS];
1168c2ecf20Sopenharmony_ci	struct usb_anchor *busy_urbs;
1178c2ecf20Sopenharmony_ci	struct mutex io_mutex;
1188c2ecf20Sopenharmony_ci	struct timer_list link_stat_timer;
1198c2ecf20Sopenharmony_ci	struct work_struct poll_work_obj;
1208c2ecf20Sopenharmony_ci	void (*on_netinfo)(struct most_interface *most_iface,
1218c2ecf20Sopenharmony_ci			   unsigned char link_state, unsigned char *addrs);
1228c2ecf20Sopenharmony_ci};
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci#define to_mdev(d) container_of(d, struct most_dev, iface)
1258c2ecf20Sopenharmony_ci#define to_mdev_from_dev(d) container_of(d, struct most_dev, dev)
1268c2ecf20Sopenharmony_ci#define to_mdev_from_work(w) container_of(w, struct most_dev, poll_work_obj)
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_cistatic void wq_clear_halt(struct work_struct *wq_obj);
1298c2ecf20Sopenharmony_cistatic void wq_netinfo(struct work_struct *wq_obj);
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci/**
1328c2ecf20Sopenharmony_ci * drci_rd_reg - read a DCI register
1338c2ecf20Sopenharmony_ci * @dev: usb device
1348c2ecf20Sopenharmony_ci * @reg: register address
1358c2ecf20Sopenharmony_ci * @buf: buffer to store data
1368c2ecf20Sopenharmony_ci *
1378c2ecf20Sopenharmony_ci * This is reads data from INIC's direct register communication interface
1388c2ecf20Sopenharmony_ci */
1398c2ecf20Sopenharmony_cistatic inline int drci_rd_reg(struct usb_device *dev, u16 reg, u16 *buf)
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci	int retval;
1428c2ecf20Sopenharmony_ci	__le16 *dma_buf;
1438c2ecf20Sopenharmony_ci	u8 req_type = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	dma_buf = kzalloc(sizeof(*dma_buf), GFP_KERNEL);
1468c2ecf20Sopenharmony_ci	if (!dma_buf)
1478c2ecf20Sopenharmony_ci		return -ENOMEM;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	retval = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
1508c2ecf20Sopenharmony_ci				 DRCI_READ_REQ, req_type,
1518c2ecf20Sopenharmony_ci				 0x0000,
1528c2ecf20Sopenharmony_ci				 reg, dma_buf, sizeof(*dma_buf),
1538c2ecf20Sopenharmony_ci				 USB_CTRL_GET_TIMEOUT);
1548c2ecf20Sopenharmony_ci	*buf = le16_to_cpu(*dma_buf);
1558c2ecf20Sopenharmony_ci	kfree(dma_buf);
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	if (retval < 0)
1588c2ecf20Sopenharmony_ci		return retval;
1598c2ecf20Sopenharmony_ci	return 0;
1608c2ecf20Sopenharmony_ci}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci/**
1638c2ecf20Sopenharmony_ci * drci_wr_reg - write a DCI register
1648c2ecf20Sopenharmony_ci * @dev: usb device
1658c2ecf20Sopenharmony_ci * @reg: register address
1668c2ecf20Sopenharmony_ci * @data: data to write
1678c2ecf20Sopenharmony_ci *
1688c2ecf20Sopenharmony_ci * This is writes data to INIC's direct register communication interface
1698c2ecf20Sopenharmony_ci */
1708c2ecf20Sopenharmony_cistatic inline int drci_wr_reg(struct usb_device *dev, u16 reg, u16 data)
1718c2ecf20Sopenharmony_ci{
1728c2ecf20Sopenharmony_ci	return usb_control_msg(dev,
1738c2ecf20Sopenharmony_ci			       usb_sndctrlpipe(dev, 0),
1748c2ecf20Sopenharmony_ci			       DRCI_WRITE_REQ,
1758c2ecf20Sopenharmony_ci			       USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
1768c2ecf20Sopenharmony_ci			       data,
1778c2ecf20Sopenharmony_ci			       reg,
1788c2ecf20Sopenharmony_ci			       NULL,
1798c2ecf20Sopenharmony_ci			       0,
1808c2ecf20Sopenharmony_ci			       USB_CTRL_SET_TIMEOUT);
1818c2ecf20Sopenharmony_ci}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cistatic inline int start_sync_ep(struct usb_device *usb_dev, u16 ep)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	return drci_wr_reg(usb_dev, DRCI_REG_BASE + DRCI_COMMAND + ep * 16, 1);
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci/**
1898c2ecf20Sopenharmony_ci * get_stream_frame_size - calculate frame size of current configuration
1908c2ecf20Sopenharmony_ci * @dev: device structure
1918c2ecf20Sopenharmony_ci * @cfg: channel configuration
1928c2ecf20Sopenharmony_ci */
1938c2ecf20Sopenharmony_cistatic unsigned int get_stream_frame_size(struct device *dev,
1948c2ecf20Sopenharmony_ci					  struct most_channel_config *cfg)
1958c2ecf20Sopenharmony_ci{
1968c2ecf20Sopenharmony_ci	unsigned int frame_size;
1978c2ecf20Sopenharmony_ci	unsigned int sub_size = cfg->subbuffer_size;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	if (!sub_size) {
2008c2ecf20Sopenharmony_ci		dev_warn(dev, "Misconfig: Subbuffer size zero.\n");
2018c2ecf20Sopenharmony_ci		return 0;
2028c2ecf20Sopenharmony_ci	}
2038c2ecf20Sopenharmony_ci	switch (cfg->data_type) {
2048c2ecf20Sopenharmony_ci	case MOST_CH_ISOC:
2058c2ecf20Sopenharmony_ci		frame_size = AV_PACKETS_PER_XACT * sub_size;
2068c2ecf20Sopenharmony_ci		break;
2078c2ecf20Sopenharmony_ci	case MOST_CH_SYNC:
2088c2ecf20Sopenharmony_ci		if (cfg->packets_per_xact == 0) {
2098c2ecf20Sopenharmony_ci			dev_warn(dev, "Misconfig: Packets per XACT zero\n");
2108c2ecf20Sopenharmony_ci			frame_size = 0;
2118c2ecf20Sopenharmony_ci		} else if (cfg->packets_per_xact == 0xFF) {
2128c2ecf20Sopenharmony_ci			frame_size = (USB_MTU / sub_size) * sub_size;
2138c2ecf20Sopenharmony_ci		} else {
2148c2ecf20Sopenharmony_ci			frame_size = cfg->packets_per_xact * sub_size;
2158c2ecf20Sopenharmony_ci		}
2168c2ecf20Sopenharmony_ci		break;
2178c2ecf20Sopenharmony_ci	default:
2188c2ecf20Sopenharmony_ci		dev_warn(dev, "Query frame size of non-streaming channel\n");
2198c2ecf20Sopenharmony_ci		frame_size = 0;
2208c2ecf20Sopenharmony_ci		break;
2218c2ecf20Sopenharmony_ci	}
2228c2ecf20Sopenharmony_ci	return frame_size;
2238c2ecf20Sopenharmony_ci}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci/**
2268c2ecf20Sopenharmony_ci * hdm_poison_channel - mark buffers of this channel as invalid
2278c2ecf20Sopenharmony_ci * @iface: pointer to the interface
2288c2ecf20Sopenharmony_ci * @channel: channel ID
2298c2ecf20Sopenharmony_ci *
2308c2ecf20Sopenharmony_ci * This unlinks all URBs submitted to the HCD,
2318c2ecf20Sopenharmony_ci * calls the associated completion function of the core and removes
2328c2ecf20Sopenharmony_ci * them from the list.
2338c2ecf20Sopenharmony_ci *
2348c2ecf20Sopenharmony_ci * Returns 0 on success or error code otherwise.
2358c2ecf20Sopenharmony_ci */
2368c2ecf20Sopenharmony_cistatic int hdm_poison_channel(struct most_interface *iface, int channel)
2378c2ecf20Sopenharmony_ci{
2388c2ecf20Sopenharmony_ci	struct most_dev *mdev = to_mdev(iface);
2398c2ecf20Sopenharmony_ci	unsigned long flags;
2408c2ecf20Sopenharmony_ci	spinlock_t *lock; /* temp. lock */
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	if (channel < 0 || channel >= iface->num_channels) {
2438c2ecf20Sopenharmony_ci		dev_warn(&mdev->usb_device->dev, "Channel ID out of range.\n");
2448c2ecf20Sopenharmony_ci		return -ECHRNG;
2458c2ecf20Sopenharmony_ci	}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	lock = mdev->channel_lock + channel;
2488c2ecf20Sopenharmony_ci	spin_lock_irqsave(lock, flags);
2498c2ecf20Sopenharmony_ci	mdev->is_channel_healthy[channel] = false;
2508c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(lock, flags);
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	cancel_work_sync(&mdev->clear_work[channel].ws);
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	mutex_lock(&mdev->io_mutex);
2558c2ecf20Sopenharmony_ci	usb_kill_anchored_urbs(&mdev->busy_urbs[channel]);
2568c2ecf20Sopenharmony_ci	if (mdev->padding_active[channel])
2578c2ecf20Sopenharmony_ci		mdev->padding_active[channel] = false;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	if (mdev->conf[channel].data_type == MOST_CH_ASYNC) {
2608c2ecf20Sopenharmony_ci		del_timer_sync(&mdev->link_stat_timer);
2618c2ecf20Sopenharmony_ci		cancel_work_sync(&mdev->poll_work_obj);
2628c2ecf20Sopenharmony_ci	}
2638c2ecf20Sopenharmony_ci	mutex_unlock(&mdev->io_mutex);
2648c2ecf20Sopenharmony_ci	return 0;
2658c2ecf20Sopenharmony_ci}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci/**
2688c2ecf20Sopenharmony_ci * hdm_add_padding - add padding bytes
2698c2ecf20Sopenharmony_ci * @mdev: most device
2708c2ecf20Sopenharmony_ci * @channel: channel ID
2718c2ecf20Sopenharmony_ci * @mbo: buffer object
2728c2ecf20Sopenharmony_ci *
2738c2ecf20Sopenharmony_ci * This inserts the INIC hardware specific padding bytes into a streaming
2748c2ecf20Sopenharmony_ci * channel's buffer
2758c2ecf20Sopenharmony_ci */
2768c2ecf20Sopenharmony_cistatic int hdm_add_padding(struct most_dev *mdev, int channel, struct mbo *mbo)
2778c2ecf20Sopenharmony_ci{
2788c2ecf20Sopenharmony_ci	struct most_channel_config *conf = &mdev->conf[channel];
2798c2ecf20Sopenharmony_ci	unsigned int frame_size = get_stream_frame_size(&mdev->dev, conf);
2808c2ecf20Sopenharmony_ci	unsigned int j, num_frames;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	if (!frame_size)
2838c2ecf20Sopenharmony_ci		return -EINVAL;
2848c2ecf20Sopenharmony_ci	num_frames = mbo->buffer_length / frame_size;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	if (num_frames < 1) {
2878c2ecf20Sopenharmony_ci		dev_err(&mdev->usb_device->dev,
2888c2ecf20Sopenharmony_ci			"Missed minimal transfer unit.\n");
2898c2ecf20Sopenharmony_ci		return -EINVAL;
2908c2ecf20Sopenharmony_ci	}
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	for (j = num_frames - 1; j > 0; j--)
2938c2ecf20Sopenharmony_ci		memmove(mbo->virt_address + j * USB_MTU,
2948c2ecf20Sopenharmony_ci			mbo->virt_address + j * frame_size,
2958c2ecf20Sopenharmony_ci			frame_size);
2968c2ecf20Sopenharmony_ci	mbo->buffer_length = num_frames * USB_MTU;
2978c2ecf20Sopenharmony_ci	return 0;
2988c2ecf20Sopenharmony_ci}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci/**
3018c2ecf20Sopenharmony_ci * hdm_remove_padding - remove padding bytes
3028c2ecf20Sopenharmony_ci * @mdev: most device
3038c2ecf20Sopenharmony_ci * @channel: channel ID
3048c2ecf20Sopenharmony_ci * @mbo: buffer object
3058c2ecf20Sopenharmony_ci *
3068c2ecf20Sopenharmony_ci * This takes the INIC hardware specific padding bytes off a streaming
3078c2ecf20Sopenharmony_ci * channel's buffer.
3088c2ecf20Sopenharmony_ci */
3098c2ecf20Sopenharmony_cistatic int hdm_remove_padding(struct most_dev *mdev, int channel,
3108c2ecf20Sopenharmony_ci			      struct mbo *mbo)
3118c2ecf20Sopenharmony_ci{
3128c2ecf20Sopenharmony_ci	struct most_channel_config *const conf = &mdev->conf[channel];
3138c2ecf20Sopenharmony_ci	unsigned int frame_size = get_stream_frame_size(&mdev->dev, conf);
3148c2ecf20Sopenharmony_ci	unsigned int j, num_frames;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	if (!frame_size)
3178c2ecf20Sopenharmony_ci		return -EINVAL;
3188c2ecf20Sopenharmony_ci	num_frames = mbo->processed_length / USB_MTU;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	for (j = 1; j < num_frames; j++)
3218c2ecf20Sopenharmony_ci		memmove(mbo->virt_address + frame_size * j,
3228c2ecf20Sopenharmony_ci			mbo->virt_address + USB_MTU * j,
3238c2ecf20Sopenharmony_ci			frame_size);
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	mbo->processed_length = frame_size * num_frames;
3268c2ecf20Sopenharmony_ci	return 0;
3278c2ecf20Sopenharmony_ci}
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci/**
3308c2ecf20Sopenharmony_ci * hdm_write_completion - completion function for submitted Tx URBs
3318c2ecf20Sopenharmony_ci * @urb: the URB that has been completed
3328c2ecf20Sopenharmony_ci *
3338c2ecf20Sopenharmony_ci * This checks the status of the completed URB. In case the URB has been
3348c2ecf20Sopenharmony_ci * unlinked before, it is immediately freed. On any other error the MBO
3358c2ecf20Sopenharmony_ci * transfer flag is set. On success it frees allocated resources and calls
3368c2ecf20Sopenharmony_ci * the completion function.
3378c2ecf20Sopenharmony_ci *
3388c2ecf20Sopenharmony_ci * Context: interrupt!
3398c2ecf20Sopenharmony_ci */
3408c2ecf20Sopenharmony_cistatic void hdm_write_completion(struct urb *urb)
3418c2ecf20Sopenharmony_ci{
3428c2ecf20Sopenharmony_ci	struct mbo *mbo = urb->context;
3438c2ecf20Sopenharmony_ci	struct most_dev *mdev = to_mdev(mbo->ifp);
3448c2ecf20Sopenharmony_ci	unsigned int channel = mbo->hdm_channel_id;
3458c2ecf20Sopenharmony_ci	spinlock_t *lock = mdev->channel_lock + channel;
3468c2ecf20Sopenharmony_ci	unsigned long flags;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	spin_lock_irqsave(lock, flags);
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	mbo->processed_length = 0;
3518c2ecf20Sopenharmony_ci	mbo->status = MBO_E_INVAL;
3528c2ecf20Sopenharmony_ci	if (likely(mdev->is_channel_healthy[channel])) {
3538c2ecf20Sopenharmony_ci		switch (urb->status) {
3548c2ecf20Sopenharmony_ci		case 0:
3558c2ecf20Sopenharmony_ci		case -ESHUTDOWN:
3568c2ecf20Sopenharmony_ci			mbo->processed_length = urb->actual_length;
3578c2ecf20Sopenharmony_ci			mbo->status = MBO_SUCCESS;
3588c2ecf20Sopenharmony_ci			break;
3598c2ecf20Sopenharmony_ci		case -EPIPE:
3608c2ecf20Sopenharmony_ci			dev_warn(&mdev->usb_device->dev,
3618c2ecf20Sopenharmony_ci				 "Broken pipe on ep%02x\n",
3628c2ecf20Sopenharmony_ci				 mdev->ep_address[channel]);
3638c2ecf20Sopenharmony_ci			mdev->is_channel_healthy[channel] = false;
3648c2ecf20Sopenharmony_ci			mdev->clear_work[channel].pipe = urb->pipe;
3658c2ecf20Sopenharmony_ci			schedule_work(&mdev->clear_work[channel].ws);
3668c2ecf20Sopenharmony_ci			break;
3678c2ecf20Sopenharmony_ci		case -ENODEV:
3688c2ecf20Sopenharmony_ci		case -EPROTO:
3698c2ecf20Sopenharmony_ci			mbo->status = MBO_E_CLOSE;
3708c2ecf20Sopenharmony_ci			break;
3718c2ecf20Sopenharmony_ci		}
3728c2ecf20Sopenharmony_ci	}
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(lock, flags);
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	if (likely(mbo->complete))
3778c2ecf20Sopenharmony_ci		mbo->complete(mbo);
3788c2ecf20Sopenharmony_ci	usb_free_urb(urb);
3798c2ecf20Sopenharmony_ci}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci/**
3828c2ecf20Sopenharmony_ci * hdm_read_completion - completion function for submitted Rx URBs
3838c2ecf20Sopenharmony_ci * @urb: the URB that has been completed
3848c2ecf20Sopenharmony_ci *
3858c2ecf20Sopenharmony_ci * This checks the status of the completed URB. In case the URB has been
3868c2ecf20Sopenharmony_ci * unlinked before it is immediately freed. On any other error the MBO transfer
3878c2ecf20Sopenharmony_ci * flag is set. On success it frees allocated resources, removes
3888c2ecf20Sopenharmony_ci * padding bytes -if necessary- and calls the completion function.
3898c2ecf20Sopenharmony_ci *
3908c2ecf20Sopenharmony_ci * Context: interrupt!
3918c2ecf20Sopenharmony_ci */
3928c2ecf20Sopenharmony_cistatic void hdm_read_completion(struct urb *urb)
3938c2ecf20Sopenharmony_ci{
3948c2ecf20Sopenharmony_ci	struct mbo *mbo = urb->context;
3958c2ecf20Sopenharmony_ci	struct most_dev *mdev = to_mdev(mbo->ifp);
3968c2ecf20Sopenharmony_ci	unsigned int channel = mbo->hdm_channel_id;
3978c2ecf20Sopenharmony_ci	struct device *dev = &mdev->usb_device->dev;
3988c2ecf20Sopenharmony_ci	spinlock_t *lock = mdev->channel_lock + channel;
3998c2ecf20Sopenharmony_ci	unsigned long flags;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	spin_lock_irqsave(lock, flags);
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	mbo->processed_length = 0;
4048c2ecf20Sopenharmony_ci	mbo->status = MBO_E_INVAL;
4058c2ecf20Sopenharmony_ci	if (likely(mdev->is_channel_healthy[channel])) {
4068c2ecf20Sopenharmony_ci		switch (urb->status) {
4078c2ecf20Sopenharmony_ci		case 0:
4088c2ecf20Sopenharmony_ci		case -ESHUTDOWN:
4098c2ecf20Sopenharmony_ci			mbo->processed_length = urb->actual_length;
4108c2ecf20Sopenharmony_ci			mbo->status = MBO_SUCCESS;
4118c2ecf20Sopenharmony_ci			if (mdev->padding_active[channel] &&
4128c2ecf20Sopenharmony_ci			    hdm_remove_padding(mdev, channel, mbo)) {
4138c2ecf20Sopenharmony_ci				mbo->processed_length = 0;
4148c2ecf20Sopenharmony_ci				mbo->status = MBO_E_INVAL;
4158c2ecf20Sopenharmony_ci			}
4168c2ecf20Sopenharmony_ci			break;
4178c2ecf20Sopenharmony_ci		case -EPIPE:
4188c2ecf20Sopenharmony_ci			dev_warn(dev, "Broken pipe on ep%02x\n",
4198c2ecf20Sopenharmony_ci				 mdev->ep_address[channel]);
4208c2ecf20Sopenharmony_ci			mdev->is_channel_healthy[channel] = false;
4218c2ecf20Sopenharmony_ci			mdev->clear_work[channel].pipe = urb->pipe;
4228c2ecf20Sopenharmony_ci			schedule_work(&mdev->clear_work[channel].ws);
4238c2ecf20Sopenharmony_ci			break;
4248c2ecf20Sopenharmony_ci		case -ENODEV:
4258c2ecf20Sopenharmony_ci		case -EPROTO:
4268c2ecf20Sopenharmony_ci			mbo->status = MBO_E_CLOSE;
4278c2ecf20Sopenharmony_ci			break;
4288c2ecf20Sopenharmony_ci		case -EOVERFLOW:
4298c2ecf20Sopenharmony_ci			dev_warn(dev, "Babble on ep%02x\n",
4308c2ecf20Sopenharmony_ci				 mdev->ep_address[channel]);
4318c2ecf20Sopenharmony_ci			break;
4328c2ecf20Sopenharmony_ci		}
4338c2ecf20Sopenharmony_ci	}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(lock, flags);
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	if (likely(mbo->complete))
4388c2ecf20Sopenharmony_ci		mbo->complete(mbo);
4398c2ecf20Sopenharmony_ci	usb_free_urb(urb);
4408c2ecf20Sopenharmony_ci}
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci/**
4438c2ecf20Sopenharmony_ci * hdm_enqueue - receive a buffer to be used for data transfer
4448c2ecf20Sopenharmony_ci * @iface: interface to enqueue to
4458c2ecf20Sopenharmony_ci * @channel: ID of the channel
4468c2ecf20Sopenharmony_ci * @mbo: pointer to the buffer object
4478c2ecf20Sopenharmony_ci *
4488c2ecf20Sopenharmony_ci * This allocates a new URB and fills it according to the channel
4498c2ecf20Sopenharmony_ci * that is being used for transmission of data. Before the URB is
4508c2ecf20Sopenharmony_ci * submitted it is stored in the private anchor list.
4518c2ecf20Sopenharmony_ci *
4528c2ecf20Sopenharmony_ci * Returns 0 on success. On any error the URB is freed and a error code
4538c2ecf20Sopenharmony_ci * is returned.
4548c2ecf20Sopenharmony_ci *
4558c2ecf20Sopenharmony_ci * Context: Could in _some_ cases be interrupt!
4568c2ecf20Sopenharmony_ci */
4578c2ecf20Sopenharmony_cistatic int hdm_enqueue(struct most_interface *iface, int channel,
4588c2ecf20Sopenharmony_ci		       struct mbo *mbo)
4598c2ecf20Sopenharmony_ci{
4608c2ecf20Sopenharmony_ci	struct most_dev *mdev = to_mdev(iface);
4618c2ecf20Sopenharmony_ci	struct most_channel_config *conf;
4628c2ecf20Sopenharmony_ci	int retval = 0;
4638c2ecf20Sopenharmony_ci	struct urb *urb;
4648c2ecf20Sopenharmony_ci	unsigned long length;
4658c2ecf20Sopenharmony_ci	void *virt_address;
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	if (!mbo)
4688c2ecf20Sopenharmony_ci		return -EINVAL;
4698c2ecf20Sopenharmony_ci	if (iface->num_channels <= channel || channel < 0)
4708c2ecf20Sopenharmony_ci		return -ECHRNG;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	urb = usb_alloc_urb(NO_ISOCHRONOUS_URB, GFP_KERNEL);
4738c2ecf20Sopenharmony_ci	if (!urb)
4748c2ecf20Sopenharmony_ci		return -ENOMEM;
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	conf = &mdev->conf[channel];
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	mutex_lock(&mdev->io_mutex);
4798c2ecf20Sopenharmony_ci	if (!mdev->usb_device) {
4808c2ecf20Sopenharmony_ci		retval = -ENODEV;
4818c2ecf20Sopenharmony_ci		goto err_free_urb;
4828c2ecf20Sopenharmony_ci	}
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	if ((conf->direction & MOST_CH_TX) && mdev->padding_active[channel] &&
4858c2ecf20Sopenharmony_ci	    hdm_add_padding(mdev, channel, mbo)) {
4868c2ecf20Sopenharmony_ci		retval = -EINVAL;
4878c2ecf20Sopenharmony_ci		goto err_free_urb;
4888c2ecf20Sopenharmony_ci	}
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	urb->transfer_dma = mbo->bus_address;
4918c2ecf20Sopenharmony_ci	virt_address = mbo->virt_address;
4928c2ecf20Sopenharmony_ci	length = mbo->buffer_length;
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	if (conf->direction & MOST_CH_TX) {
4958c2ecf20Sopenharmony_ci		usb_fill_bulk_urb(urb, mdev->usb_device,
4968c2ecf20Sopenharmony_ci				  usb_sndbulkpipe(mdev->usb_device,
4978c2ecf20Sopenharmony_ci						  mdev->ep_address[channel]),
4988c2ecf20Sopenharmony_ci				  virt_address,
4998c2ecf20Sopenharmony_ci				  length,
5008c2ecf20Sopenharmony_ci				  hdm_write_completion,
5018c2ecf20Sopenharmony_ci				  mbo);
5028c2ecf20Sopenharmony_ci		if (conf->data_type != MOST_CH_ISOC &&
5038c2ecf20Sopenharmony_ci		    conf->data_type != MOST_CH_SYNC)
5048c2ecf20Sopenharmony_ci			urb->transfer_flags |= URB_ZERO_PACKET;
5058c2ecf20Sopenharmony_ci	} else {
5068c2ecf20Sopenharmony_ci		usb_fill_bulk_urb(urb, mdev->usb_device,
5078c2ecf20Sopenharmony_ci				  usb_rcvbulkpipe(mdev->usb_device,
5088c2ecf20Sopenharmony_ci						  mdev->ep_address[channel]),
5098c2ecf20Sopenharmony_ci				  virt_address,
5108c2ecf20Sopenharmony_ci				  length + conf->extra_len,
5118c2ecf20Sopenharmony_ci				  hdm_read_completion,
5128c2ecf20Sopenharmony_ci				  mbo);
5138c2ecf20Sopenharmony_ci	}
5148c2ecf20Sopenharmony_ci	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	usb_anchor_urb(urb, &mdev->busy_urbs[channel]);
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	retval = usb_submit_urb(urb, GFP_KERNEL);
5198c2ecf20Sopenharmony_ci	if (retval) {
5208c2ecf20Sopenharmony_ci		dev_err(&mdev->usb_device->dev,
5218c2ecf20Sopenharmony_ci			"URB submit failed with error %d.\n", retval);
5228c2ecf20Sopenharmony_ci		goto err_unanchor_urb;
5238c2ecf20Sopenharmony_ci	}
5248c2ecf20Sopenharmony_ci	mutex_unlock(&mdev->io_mutex);
5258c2ecf20Sopenharmony_ci	return 0;
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_cierr_unanchor_urb:
5288c2ecf20Sopenharmony_ci	usb_unanchor_urb(urb);
5298c2ecf20Sopenharmony_cierr_free_urb:
5308c2ecf20Sopenharmony_ci	usb_free_urb(urb);
5318c2ecf20Sopenharmony_ci	mutex_unlock(&mdev->io_mutex);
5328c2ecf20Sopenharmony_ci	return retval;
5338c2ecf20Sopenharmony_ci}
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_cistatic void *hdm_dma_alloc(struct mbo *mbo, u32 size)
5368c2ecf20Sopenharmony_ci{
5378c2ecf20Sopenharmony_ci	struct most_dev *mdev = to_mdev(mbo->ifp);
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	return usb_alloc_coherent(mdev->usb_device, size, GFP_KERNEL,
5408c2ecf20Sopenharmony_ci				  &mbo->bus_address);
5418c2ecf20Sopenharmony_ci}
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_cistatic void hdm_dma_free(struct mbo *mbo, u32 size)
5448c2ecf20Sopenharmony_ci{
5458c2ecf20Sopenharmony_ci	struct most_dev *mdev = to_mdev(mbo->ifp);
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	usb_free_coherent(mdev->usb_device, size, mbo->virt_address,
5488c2ecf20Sopenharmony_ci			  mbo->bus_address);
5498c2ecf20Sopenharmony_ci}
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci/**
5528c2ecf20Sopenharmony_ci * hdm_configure_channel - receive channel configuration from core
5538c2ecf20Sopenharmony_ci * @iface: interface
5548c2ecf20Sopenharmony_ci * @channel: channel ID
5558c2ecf20Sopenharmony_ci * @conf: structure that holds the configuration information
5568c2ecf20Sopenharmony_ci *
5578c2ecf20Sopenharmony_ci * The attached network interface controller (NIC) supports a padding mode
5588c2ecf20Sopenharmony_ci * to avoid short packets on USB, hence increasing the performance due to a
5598c2ecf20Sopenharmony_ci * lower interrupt load. This mode is default for synchronous data and can
5608c2ecf20Sopenharmony_ci * be switched on for isochronous data. In case padding is active the
5618c2ecf20Sopenharmony_ci * driver needs to know the frame size of the payload in order to calculate
5628c2ecf20Sopenharmony_ci * the number of bytes it needs to pad when transmitting or to cut off when
5638c2ecf20Sopenharmony_ci * receiving data.
5648c2ecf20Sopenharmony_ci *
5658c2ecf20Sopenharmony_ci */
5668c2ecf20Sopenharmony_cistatic int hdm_configure_channel(struct most_interface *iface, int channel,
5678c2ecf20Sopenharmony_ci				 struct most_channel_config *conf)
5688c2ecf20Sopenharmony_ci{
5698c2ecf20Sopenharmony_ci	unsigned int num_frames;
5708c2ecf20Sopenharmony_ci	unsigned int frame_size;
5718c2ecf20Sopenharmony_ci	struct most_dev *mdev = to_mdev(iface);
5728c2ecf20Sopenharmony_ci	struct device *dev = &mdev->usb_device->dev;
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	if (!conf) {
5758c2ecf20Sopenharmony_ci		dev_err(dev, "Bad config pointer.\n");
5768c2ecf20Sopenharmony_ci		return -EINVAL;
5778c2ecf20Sopenharmony_ci	}
5788c2ecf20Sopenharmony_ci	if (channel < 0 || channel >= iface->num_channels) {
5798c2ecf20Sopenharmony_ci		dev_err(dev, "Channel ID out of range.\n");
5808c2ecf20Sopenharmony_ci		return -EINVAL;
5818c2ecf20Sopenharmony_ci	}
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	mdev->is_channel_healthy[channel] = true;
5848c2ecf20Sopenharmony_ci	mdev->clear_work[channel].channel = channel;
5858c2ecf20Sopenharmony_ci	mdev->clear_work[channel].mdev = mdev;
5868c2ecf20Sopenharmony_ci	INIT_WORK(&mdev->clear_work[channel].ws, wq_clear_halt);
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	if (!conf->num_buffers || !conf->buffer_size) {
5898c2ecf20Sopenharmony_ci		dev_err(dev, "Misconfig: buffer size or #buffers zero.\n");
5908c2ecf20Sopenharmony_ci		return -EINVAL;
5918c2ecf20Sopenharmony_ci	}
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	if (conf->data_type != MOST_CH_SYNC &&
5948c2ecf20Sopenharmony_ci	    !(conf->data_type == MOST_CH_ISOC &&
5958c2ecf20Sopenharmony_ci	      conf->packets_per_xact != 0xFF)) {
5968c2ecf20Sopenharmony_ci		mdev->padding_active[channel] = false;
5978c2ecf20Sopenharmony_ci		/*
5988c2ecf20Sopenharmony_ci		 * Since the NIC's padding mode is not going to be
5998c2ecf20Sopenharmony_ci		 * used, we can skip the frame size calculations and
6008c2ecf20Sopenharmony_ci		 * move directly on to exit.
6018c2ecf20Sopenharmony_ci		 */
6028c2ecf20Sopenharmony_ci		goto exit;
6038c2ecf20Sopenharmony_ci	}
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	mdev->padding_active[channel] = true;
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	frame_size = get_stream_frame_size(&mdev->dev, conf);
6088c2ecf20Sopenharmony_ci	if (frame_size == 0 || frame_size > USB_MTU) {
6098c2ecf20Sopenharmony_ci		dev_warn(dev, "Misconfig: frame size wrong\n");
6108c2ecf20Sopenharmony_ci		return -EINVAL;
6118c2ecf20Sopenharmony_ci	}
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	num_frames = conf->buffer_size / frame_size;
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	if (conf->buffer_size % frame_size) {
6168c2ecf20Sopenharmony_ci		u16 old_size = conf->buffer_size;
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci		conf->buffer_size = num_frames * frame_size;
6198c2ecf20Sopenharmony_ci		dev_warn(dev, "%s: fixed buffer size (%d -> %d)\n",
6208c2ecf20Sopenharmony_ci			 mdev->suffix[channel], old_size, conf->buffer_size);
6218c2ecf20Sopenharmony_ci	}
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci	/* calculate extra length to comply w/ HW padding */
6248c2ecf20Sopenharmony_ci	conf->extra_len = num_frames * (USB_MTU - frame_size);
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ciexit:
6278c2ecf20Sopenharmony_ci	mdev->conf[channel] = *conf;
6288c2ecf20Sopenharmony_ci	if (conf->data_type == MOST_CH_ASYNC) {
6298c2ecf20Sopenharmony_ci		u16 ep = mdev->ep_address[channel];
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci		if (start_sync_ep(mdev->usb_device, ep) < 0)
6328c2ecf20Sopenharmony_ci			dev_warn(dev, "sync for ep%02x failed", ep);
6338c2ecf20Sopenharmony_ci	}
6348c2ecf20Sopenharmony_ci	return 0;
6358c2ecf20Sopenharmony_ci}
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci/**
6388c2ecf20Sopenharmony_ci * hdm_request_netinfo - request network information
6398c2ecf20Sopenharmony_ci * @iface: pointer to interface
6408c2ecf20Sopenharmony_ci * @channel: channel ID
6418c2ecf20Sopenharmony_ci *
6428c2ecf20Sopenharmony_ci * This is used as trigger to set up the link status timer that
6438c2ecf20Sopenharmony_ci * polls for the NI state of the INIC every 2 seconds.
6448c2ecf20Sopenharmony_ci *
6458c2ecf20Sopenharmony_ci */
6468c2ecf20Sopenharmony_cistatic void hdm_request_netinfo(struct most_interface *iface, int channel,
6478c2ecf20Sopenharmony_ci				void (*on_netinfo)(struct most_interface *,
6488c2ecf20Sopenharmony_ci						   unsigned char,
6498c2ecf20Sopenharmony_ci						   unsigned char *))
6508c2ecf20Sopenharmony_ci{
6518c2ecf20Sopenharmony_ci	struct most_dev *mdev = to_mdev(iface);
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	mdev->on_netinfo = on_netinfo;
6548c2ecf20Sopenharmony_ci	if (!on_netinfo)
6558c2ecf20Sopenharmony_ci		return;
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	mdev->link_stat_timer.expires = jiffies + HZ;
6588c2ecf20Sopenharmony_ci	mod_timer(&mdev->link_stat_timer, mdev->link_stat_timer.expires);
6598c2ecf20Sopenharmony_ci}
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci/**
6628c2ecf20Sopenharmony_ci * link_stat_timer_handler - schedule work obtaining mac address and link status
6638c2ecf20Sopenharmony_ci * @data: pointer to USB device instance
6648c2ecf20Sopenharmony_ci *
6658c2ecf20Sopenharmony_ci * The handler runs in interrupt context. That's why we need to defer the
6668c2ecf20Sopenharmony_ci * tasks to a work queue.
6678c2ecf20Sopenharmony_ci */
6688c2ecf20Sopenharmony_cistatic void link_stat_timer_handler(struct timer_list *t)
6698c2ecf20Sopenharmony_ci{
6708c2ecf20Sopenharmony_ci	struct most_dev *mdev = from_timer(mdev, t, link_stat_timer);
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	schedule_work(&mdev->poll_work_obj);
6738c2ecf20Sopenharmony_ci	mdev->link_stat_timer.expires = jiffies + (2 * HZ);
6748c2ecf20Sopenharmony_ci	add_timer(&mdev->link_stat_timer);
6758c2ecf20Sopenharmony_ci}
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci/**
6788c2ecf20Sopenharmony_ci * wq_netinfo - work queue function to deliver latest networking information
6798c2ecf20Sopenharmony_ci * @wq_obj: object that holds data for our deferred work to do
6808c2ecf20Sopenharmony_ci *
6818c2ecf20Sopenharmony_ci * This retrieves the network interface status of the USB INIC
6828c2ecf20Sopenharmony_ci */
6838c2ecf20Sopenharmony_cistatic void wq_netinfo(struct work_struct *wq_obj)
6848c2ecf20Sopenharmony_ci{
6858c2ecf20Sopenharmony_ci	struct most_dev *mdev = to_mdev_from_work(wq_obj);
6868c2ecf20Sopenharmony_ci	struct usb_device *usb_device = mdev->usb_device;
6878c2ecf20Sopenharmony_ci	struct device *dev = &usb_device->dev;
6888c2ecf20Sopenharmony_ci	u16 hi, mi, lo, link;
6898c2ecf20Sopenharmony_ci	u8 hw_addr[6];
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	if (drci_rd_reg(usb_device, DRCI_REG_HW_ADDR_HI, &hi)) {
6928c2ecf20Sopenharmony_ci		dev_err(dev, "Vendor request 'hw_addr_hi' failed\n");
6938c2ecf20Sopenharmony_ci		return;
6948c2ecf20Sopenharmony_ci	}
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci	if (drci_rd_reg(usb_device, DRCI_REG_HW_ADDR_MI, &mi)) {
6978c2ecf20Sopenharmony_ci		dev_err(dev, "Vendor request 'hw_addr_mid' failed\n");
6988c2ecf20Sopenharmony_ci		return;
6998c2ecf20Sopenharmony_ci	}
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci	if (drci_rd_reg(usb_device, DRCI_REG_HW_ADDR_LO, &lo)) {
7028c2ecf20Sopenharmony_ci		dev_err(dev, "Vendor request 'hw_addr_low' failed\n");
7038c2ecf20Sopenharmony_ci		return;
7048c2ecf20Sopenharmony_ci	}
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci	if (drci_rd_reg(usb_device, DRCI_REG_NI_STATE, &link)) {
7078c2ecf20Sopenharmony_ci		dev_err(dev, "Vendor request 'link status' failed\n");
7088c2ecf20Sopenharmony_ci		return;
7098c2ecf20Sopenharmony_ci	}
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	hw_addr[0] = hi >> 8;
7128c2ecf20Sopenharmony_ci	hw_addr[1] = hi;
7138c2ecf20Sopenharmony_ci	hw_addr[2] = mi >> 8;
7148c2ecf20Sopenharmony_ci	hw_addr[3] = mi;
7158c2ecf20Sopenharmony_ci	hw_addr[4] = lo >> 8;
7168c2ecf20Sopenharmony_ci	hw_addr[5] = lo;
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	if (mdev->on_netinfo)
7198c2ecf20Sopenharmony_ci		mdev->on_netinfo(&mdev->iface, link, hw_addr);
7208c2ecf20Sopenharmony_ci}
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci/**
7238c2ecf20Sopenharmony_ci * wq_clear_halt - work queue function
7248c2ecf20Sopenharmony_ci * @wq_obj: work_struct object to execute
7258c2ecf20Sopenharmony_ci *
7268c2ecf20Sopenharmony_ci * This sends a clear_halt to the given USB pipe.
7278c2ecf20Sopenharmony_ci */
7288c2ecf20Sopenharmony_cistatic void wq_clear_halt(struct work_struct *wq_obj)
7298c2ecf20Sopenharmony_ci{
7308c2ecf20Sopenharmony_ci	struct clear_hold_work *clear_work = to_clear_hold_work(wq_obj);
7318c2ecf20Sopenharmony_ci	struct most_dev *mdev = clear_work->mdev;
7328c2ecf20Sopenharmony_ci	unsigned int channel = clear_work->channel;
7338c2ecf20Sopenharmony_ci	int pipe = clear_work->pipe;
7348c2ecf20Sopenharmony_ci	int snd_pipe;
7358c2ecf20Sopenharmony_ci	int peer;
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci	mutex_lock(&mdev->io_mutex);
7388c2ecf20Sopenharmony_ci	most_stop_enqueue(&mdev->iface, channel);
7398c2ecf20Sopenharmony_ci	usb_kill_anchored_urbs(&mdev->busy_urbs[channel]);
7408c2ecf20Sopenharmony_ci	if (usb_clear_halt(mdev->usb_device, pipe))
7418c2ecf20Sopenharmony_ci		dev_warn(&mdev->usb_device->dev, "Failed to reset endpoint.\n");
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci	/* If the functional Stall condition has been set on an
7448c2ecf20Sopenharmony_ci	 * asynchronous rx channel, we need to clear the tx channel
7458c2ecf20Sopenharmony_ci	 * too, since the hardware runs its clean-up sequence on both
7468c2ecf20Sopenharmony_ci	 * channels, as they are physically one on the network.
7478c2ecf20Sopenharmony_ci	 *
7488c2ecf20Sopenharmony_ci	 * The USB interface that exposes the asynchronous channels
7498c2ecf20Sopenharmony_ci	 * contains always two endpoints, and two only.
7508c2ecf20Sopenharmony_ci	 */
7518c2ecf20Sopenharmony_ci	if (mdev->conf[channel].data_type == MOST_CH_ASYNC &&
7528c2ecf20Sopenharmony_ci	    mdev->conf[channel].direction == MOST_CH_RX) {
7538c2ecf20Sopenharmony_ci		if (channel == 0)
7548c2ecf20Sopenharmony_ci			peer = 1;
7558c2ecf20Sopenharmony_ci		else
7568c2ecf20Sopenharmony_ci			peer = 0;
7578c2ecf20Sopenharmony_ci		snd_pipe = usb_sndbulkpipe(mdev->usb_device,
7588c2ecf20Sopenharmony_ci					   mdev->ep_address[peer]);
7598c2ecf20Sopenharmony_ci		usb_clear_halt(mdev->usb_device, snd_pipe);
7608c2ecf20Sopenharmony_ci	}
7618c2ecf20Sopenharmony_ci	mdev->is_channel_healthy[channel] = true;
7628c2ecf20Sopenharmony_ci	most_resume_enqueue(&mdev->iface, channel);
7638c2ecf20Sopenharmony_ci	mutex_unlock(&mdev->io_mutex);
7648c2ecf20Sopenharmony_ci}
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci/**
7678c2ecf20Sopenharmony_ci * hdm_usb_fops - file operation table for USB driver
7688c2ecf20Sopenharmony_ci */
7698c2ecf20Sopenharmony_cistatic const struct file_operations hdm_usb_fops = {
7708c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
7718c2ecf20Sopenharmony_ci};
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci/**
7748c2ecf20Sopenharmony_ci * usb_device_id - ID table for HCD device probing
7758c2ecf20Sopenharmony_ci */
7768c2ecf20Sopenharmony_cistatic const struct usb_device_id usbid[] = {
7778c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_SMSC, USB_DEV_ID_BRDG), },
7788c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_SMSC, USB_DEV_ID_OS81118), },
7798c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_SMSC, USB_DEV_ID_OS81119), },
7808c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_SMSC, USB_DEV_ID_OS81210), },
7818c2ecf20Sopenharmony_ci	{ } /* Terminating entry */
7828c2ecf20Sopenharmony_ci};
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_cistruct regs {
7858c2ecf20Sopenharmony_ci	const char *name;
7868c2ecf20Sopenharmony_ci	u16 reg;
7878c2ecf20Sopenharmony_ci};
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_cistatic const struct regs ro_regs[] = {
7908c2ecf20Sopenharmony_ci	{ "ni_state", DRCI_REG_NI_STATE },
7918c2ecf20Sopenharmony_ci	{ "packet_bandwidth", DRCI_REG_PACKET_BW },
7928c2ecf20Sopenharmony_ci	{ "node_address", DRCI_REG_NODE_ADDR },
7938c2ecf20Sopenharmony_ci	{ "node_position", DRCI_REG_NODE_POS },
7948c2ecf20Sopenharmony_ci};
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_cistatic const struct regs rw_regs[] = {
7978c2ecf20Sopenharmony_ci	{ "mep_filter", DRCI_REG_MEP_FILTER },
7988c2ecf20Sopenharmony_ci	{ "mep_hash0", DRCI_REG_HASH_TBL0 },
7998c2ecf20Sopenharmony_ci	{ "mep_hash1", DRCI_REG_HASH_TBL1 },
8008c2ecf20Sopenharmony_ci	{ "mep_hash2", DRCI_REG_HASH_TBL2 },
8018c2ecf20Sopenharmony_ci	{ "mep_hash3", DRCI_REG_HASH_TBL3 },
8028c2ecf20Sopenharmony_ci	{ "mep_eui48_hi", DRCI_REG_HW_ADDR_HI },
8038c2ecf20Sopenharmony_ci	{ "mep_eui48_mi", DRCI_REG_HW_ADDR_MI },
8048c2ecf20Sopenharmony_ci	{ "mep_eui48_lo", DRCI_REG_HW_ADDR_LO },
8058c2ecf20Sopenharmony_ci};
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_cistatic int get_stat_reg_addr(const struct regs *regs, int size,
8088c2ecf20Sopenharmony_ci			     const char *name, u16 *reg_addr)
8098c2ecf20Sopenharmony_ci{
8108c2ecf20Sopenharmony_ci	int i;
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	for (i = 0; i < size; i++) {
8138c2ecf20Sopenharmony_ci		if (sysfs_streq(name, regs[i].name)) {
8148c2ecf20Sopenharmony_ci			*reg_addr = regs[i].reg;
8158c2ecf20Sopenharmony_ci			return 0;
8168c2ecf20Sopenharmony_ci		}
8178c2ecf20Sopenharmony_ci	}
8188c2ecf20Sopenharmony_ci	return -EINVAL;
8198c2ecf20Sopenharmony_ci}
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci#define get_static_reg_addr(regs, name, reg_addr) \
8228c2ecf20Sopenharmony_ci	get_stat_reg_addr(regs, ARRAY_SIZE(regs), name, reg_addr)
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_cistatic ssize_t value_show(struct device *dev, struct device_attribute *attr,
8258c2ecf20Sopenharmony_ci			  char *buf)
8268c2ecf20Sopenharmony_ci{
8278c2ecf20Sopenharmony_ci	const char *name = attr->attr.name;
8288c2ecf20Sopenharmony_ci	struct most_dci_obj *dci_obj = to_dci_obj(dev);
8298c2ecf20Sopenharmony_ci	u16 val;
8308c2ecf20Sopenharmony_ci	u16 reg_addr;
8318c2ecf20Sopenharmony_ci	int err;
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci	if (sysfs_streq(name, "arb_address"))
8348c2ecf20Sopenharmony_ci		return snprintf(buf, PAGE_SIZE, "%04x\n", dci_obj->reg_addr);
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci	if (sysfs_streq(name, "arb_value"))
8378c2ecf20Sopenharmony_ci		reg_addr = dci_obj->reg_addr;
8388c2ecf20Sopenharmony_ci	else if (get_static_reg_addr(ro_regs, name, &reg_addr) &&
8398c2ecf20Sopenharmony_ci		 get_static_reg_addr(rw_regs, name, &reg_addr))
8408c2ecf20Sopenharmony_ci		return -EINVAL;
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci	err = drci_rd_reg(dci_obj->usb_device, reg_addr, &val);
8438c2ecf20Sopenharmony_ci	if (err < 0)
8448c2ecf20Sopenharmony_ci		return err;
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "%04x\n", val);
8478c2ecf20Sopenharmony_ci}
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_cistatic ssize_t value_store(struct device *dev, struct device_attribute *attr,
8508c2ecf20Sopenharmony_ci			   const char *buf, size_t count)
8518c2ecf20Sopenharmony_ci{
8528c2ecf20Sopenharmony_ci	u16 val;
8538c2ecf20Sopenharmony_ci	u16 reg_addr;
8548c2ecf20Sopenharmony_ci	const char *name = attr->attr.name;
8558c2ecf20Sopenharmony_ci	struct most_dci_obj *dci_obj = to_dci_obj(dev);
8568c2ecf20Sopenharmony_ci	struct usb_device *usb_dev = dci_obj->usb_device;
8578c2ecf20Sopenharmony_ci	int err;
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci	err = kstrtou16(buf, 16, &val);
8608c2ecf20Sopenharmony_ci	if (err)
8618c2ecf20Sopenharmony_ci		return err;
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci	if (sysfs_streq(name, "arb_address")) {
8648c2ecf20Sopenharmony_ci		dci_obj->reg_addr = val;
8658c2ecf20Sopenharmony_ci		return count;
8668c2ecf20Sopenharmony_ci	}
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci	if (sysfs_streq(name, "arb_value"))
8698c2ecf20Sopenharmony_ci		err = drci_wr_reg(usb_dev, dci_obj->reg_addr, val);
8708c2ecf20Sopenharmony_ci	else if (sysfs_streq(name, "sync_ep"))
8718c2ecf20Sopenharmony_ci		err = start_sync_ep(usb_dev, val);
8728c2ecf20Sopenharmony_ci	else if (!get_static_reg_addr(rw_regs, name, &reg_addr))
8738c2ecf20Sopenharmony_ci		err = drci_wr_reg(usb_dev, reg_addr, val);
8748c2ecf20Sopenharmony_ci	else
8758c2ecf20Sopenharmony_ci		return -EINVAL;
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci	if (err < 0)
8788c2ecf20Sopenharmony_ci		return err;
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_ci	return count;
8818c2ecf20Sopenharmony_ci}
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_cistatic DEVICE_ATTR(ni_state, 0444, value_show, NULL);
8848c2ecf20Sopenharmony_cistatic DEVICE_ATTR(packet_bandwidth, 0444, value_show, NULL);
8858c2ecf20Sopenharmony_cistatic DEVICE_ATTR(node_address, 0444, value_show, NULL);
8868c2ecf20Sopenharmony_cistatic DEVICE_ATTR(node_position, 0444, value_show, NULL);
8878c2ecf20Sopenharmony_cistatic DEVICE_ATTR(sync_ep, 0200, NULL, value_store);
8888c2ecf20Sopenharmony_cistatic DEVICE_ATTR(mep_filter, 0644, value_show, value_store);
8898c2ecf20Sopenharmony_cistatic DEVICE_ATTR(mep_hash0, 0644, value_show, value_store);
8908c2ecf20Sopenharmony_cistatic DEVICE_ATTR(mep_hash1, 0644, value_show, value_store);
8918c2ecf20Sopenharmony_cistatic DEVICE_ATTR(mep_hash2, 0644, value_show, value_store);
8928c2ecf20Sopenharmony_cistatic DEVICE_ATTR(mep_hash3, 0644, value_show, value_store);
8938c2ecf20Sopenharmony_cistatic DEVICE_ATTR(mep_eui48_hi, 0644, value_show, value_store);
8948c2ecf20Sopenharmony_cistatic DEVICE_ATTR(mep_eui48_mi, 0644, value_show, value_store);
8958c2ecf20Sopenharmony_cistatic DEVICE_ATTR(mep_eui48_lo, 0644, value_show, value_store);
8968c2ecf20Sopenharmony_cistatic DEVICE_ATTR(arb_address, 0644, value_show, value_store);
8978c2ecf20Sopenharmony_cistatic DEVICE_ATTR(arb_value, 0644, value_show, value_store);
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_cistatic struct attribute *dci_attrs[] = {
9008c2ecf20Sopenharmony_ci	&dev_attr_ni_state.attr,
9018c2ecf20Sopenharmony_ci	&dev_attr_packet_bandwidth.attr,
9028c2ecf20Sopenharmony_ci	&dev_attr_node_address.attr,
9038c2ecf20Sopenharmony_ci	&dev_attr_node_position.attr,
9048c2ecf20Sopenharmony_ci	&dev_attr_sync_ep.attr,
9058c2ecf20Sopenharmony_ci	&dev_attr_mep_filter.attr,
9068c2ecf20Sopenharmony_ci	&dev_attr_mep_hash0.attr,
9078c2ecf20Sopenharmony_ci	&dev_attr_mep_hash1.attr,
9088c2ecf20Sopenharmony_ci	&dev_attr_mep_hash2.attr,
9098c2ecf20Sopenharmony_ci	&dev_attr_mep_hash3.attr,
9108c2ecf20Sopenharmony_ci	&dev_attr_mep_eui48_hi.attr,
9118c2ecf20Sopenharmony_ci	&dev_attr_mep_eui48_mi.attr,
9128c2ecf20Sopenharmony_ci	&dev_attr_mep_eui48_lo.attr,
9138c2ecf20Sopenharmony_ci	&dev_attr_arb_address.attr,
9148c2ecf20Sopenharmony_ci	&dev_attr_arb_value.attr,
9158c2ecf20Sopenharmony_ci	NULL,
9168c2ecf20Sopenharmony_ci};
9178c2ecf20Sopenharmony_ci
9188c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(dci);
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_cistatic void release_dci(struct device *dev)
9218c2ecf20Sopenharmony_ci{
9228c2ecf20Sopenharmony_ci	struct most_dci_obj *dci = to_dci_obj(dev);
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci	put_device(dev->parent);
9258c2ecf20Sopenharmony_ci	kfree(dci);
9268c2ecf20Sopenharmony_ci}
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_cistatic void release_mdev(struct device *dev)
9298c2ecf20Sopenharmony_ci{
9308c2ecf20Sopenharmony_ci	struct most_dev *mdev = to_mdev_from_dev(dev);
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_ci	kfree(mdev);
9338c2ecf20Sopenharmony_ci}
9348c2ecf20Sopenharmony_ci/**
9358c2ecf20Sopenharmony_ci * hdm_probe - probe function of USB device driver
9368c2ecf20Sopenharmony_ci * @interface: Interface of the attached USB device
9378c2ecf20Sopenharmony_ci * @id: Pointer to the USB ID table.
9388c2ecf20Sopenharmony_ci *
9398c2ecf20Sopenharmony_ci * This allocates and initializes the device instance, adds the new
9408c2ecf20Sopenharmony_ci * entry to the internal list, scans the USB descriptors and registers
9418c2ecf20Sopenharmony_ci * the interface with the core.
9428c2ecf20Sopenharmony_ci * Additionally, the DCI objects are created and the hardware is sync'd.
9438c2ecf20Sopenharmony_ci *
9448c2ecf20Sopenharmony_ci * Return 0 on success. In case of an error a negative number is returned.
9458c2ecf20Sopenharmony_ci */
9468c2ecf20Sopenharmony_cistatic int
9478c2ecf20Sopenharmony_cihdm_probe(struct usb_interface *interface, const struct usb_device_id *id)
9488c2ecf20Sopenharmony_ci{
9498c2ecf20Sopenharmony_ci	struct usb_host_interface *usb_iface_desc = interface->cur_altsetting;
9508c2ecf20Sopenharmony_ci	struct usb_device *usb_dev = interface_to_usbdev(interface);
9518c2ecf20Sopenharmony_ci	struct device *dev = &usb_dev->dev;
9528c2ecf20Sopenharmony_ci	struct most_dev *mdev;
9538c2ecf20Sopenharmony_ci	unsigned int i;
9548c2ecf20Sopenharmony_ci	unsigned int num_endpoints;
9558c2ecf20Sopenharmony_ci	struct most_channel_capability *tmp_cap;
9568c2ecf20Sopenharmony_ci	struct usb_endpoint_descriptor *ep_desc;
9578c2ecf20Sopenharmony_ci	int ret = -ENOMEM;
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_ci	mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
9608c2ecf20Sopenharmony_ci	if (!mdev)
9618c2ecf20Sopenharmony_ci		return -ENOMEM;
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci	usb_set_intfdata(interface, mdev);
9648c2ecf20Sopenharmony_ci	num_endpoints = usb_iface_desc->desc.bNumEndpoints;
9658c2ecf20Sopenharmony_ci	if (num_endpoints > MAX_NUM_ENDPOINTS) {
9668c2ecf20Sopenharmony_ci		kfree(mdev);
9678c2ecf20Sopenharmony_ci		return -EINVAL;
9688c2ecf20Sopenharmony_ci	}
9698c2ecf20Sopenharmony_ci	mutex_init(&mdev->io_mutex);
9708c2ecf20Sopenharmony_ci	INIT_WORK(&mdev->poll_work_obj, wq_netinfo);
9718c2ecf20Sopenharmony_ci	timer_setup(&mdev->link_stat_timer, link_stat_timer_handler, 0);
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci	mdev->usb_device = usb_dev;
9748c2ecf20Sopenharmony_ci	mdev->link_stat_timer.expires = jiffies + (2 * HZ);
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_ci	mdev->iface.mod = hdm_usb_fops.owner;
9778c2ecf20Sopenharmony_ci	mdev->iface.dev = &mdev->dev;
9788c2ecf20Sopenharmony_ci	mdev->iface.driver_dev = &interface->dev;
9798c2ecf20Sopenharmony_ci	mdev->iface.interface = ITYPE_USB;
9808c2ecf20Sopenharmony_ci	mdev->iface.configure = hdm_configure_channel;
9818c2ecf20Sopenharmony_ci	mdev->iface.request_netinfo = hdm_request_netinfo;
9828c2ecf20Sopenharmony_ci	mdev->iface.enqueue = hdm_enqueue;
9838c2ecf20Sopenharmony_ci	mdev->iface.poison_channel = hdm_poison_channel;
9848c2ecf20Sopenharmony_ci	mdev->iface.dma_alloc = hdm_dma_alloc;
9858c2ecf20Sopenharmony_ci	mdev->iface.dma_free = hdm_dma_free;
9868c2ecf20Sopenharmony_ci	mdev->iface.description = mdev->description;
9878c2ecf20Sopenharmony_ci	mdev->iface.num_channels = num_endpoints;
9888c2ecf20Sopenharmony_ci
9898c2ecf20Sopenharmony_ci	snprintf(mdev->description, sizeof(mdev->description),
9908c2ecf20Sopenharmony_ci		 "%d-%s:%d.%d",
9918c2ecf20Sopenharmony_ci		 usb_dev->bus->busnum,
9928c2ecf20Sopenharmony_ci		 usb_dev->devpath,
9938c2ecf20Sopenharmony_ci		 usb_dev->config->desc.bConfigurationValue,
9948c2ecf20Sopenharmony_ci		 usb_iface_desc->desc.bInterfaceNumber);
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_ci	mdev->dev.init_name = mdev->description;
9978c2ecf20Sopenharmony_ci	mdev->dev.parent = &interface->dev;
9988c2ecf20Sopenharmony_ci	mdev->dev.release = release_mdev;
9998c2ecf20Sopenharmony_ci	mdev->conf = kcalloc(num_endpoints, sizeof(*mdev->conf), GFP_KERNEL);
10008c2ecf20Sopenharmony_ci	if (!mdev->conf)
10018c2ecf20Sopenharmony_ci		goto err_free_mdev;
10028c2ecf20Sopenharmony_ci
10038c2ecf20Sopenharmony_ci	mdev->cap = kcalloc(num_endpoints, sizeof(*mdev->cap), GFP_KERNEL);
10048c2ecf20Sopenharmony_ci	if (!mdev->cap)
10058c2ecf20Sopenharmony_ci		goto err_free_conf;
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci	mdev->iface.channel_vector = mdev->cap;
10088c2ecf20Sopenharmony_ci	mdev->ep_address =
10098c2ecf20Sopenharmony_ci		kcalloc(num_endpoints, sizeof(*mdev->ep_address), GFP_KERNEL);
10108c2ecf20Sopenharmony_ci	if (!mdev->ep_address)
10118c2ecf20Sopenharmony_ci		goto err_free_cap;
10128c2ecf20Sopenharmony_ci
10138c2ecf20Sopenharmony_ci	mdev->busy_urbs =
10148c2ecf20Sopenharmony_ci		kcalloc(num_endpoints, sizeof(*mdev->busy_urbs), GFP_KERNEL);
10158c2ecf20Sopenharmony_ci	if (!mdev->busy_urbs)
10168c2ecf20Sopenharmony_ci		goto err_free_ep_address;
10178c2ecf20Sopenharmony_ci
10188c2ecf20Sopenharmony_ci	tmp_cap = mdev->cap;
10198c2ecf20Sopenharmony_ci	for (i = 0; i < num_endpoints; i++) {
10208c2ecf20Sopenharmony_ci		ep_desc = &usb_iface_desc->endpoint[i].desc;
10218c2ecf20Sopenharmony_ci		mdev->ep_address[i] = ep_desc->bEndpointAddress;
10228c2ecf20Sopenharmony_ci		mdev->padding_active[i] = false;
10238c2ecf20Sopenharmony_ci		mdev->is_channel_healthy[i] = true;
10248c2ecf20Sopenharmony_ci
10258c2ecf20Sopenharmony_ci		snprintf(&mdev->suffix[i][0], MAX_SUFFIX_LEN, "ep%02x",
10268c2ecf20Sopenharmony_ci			 mdev->ep_address[i]);
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_ci		tmp_cap->name_suffix = &mdev->suffix[i][0];
10298c2ecf20Sopenharmony_ci		tmp_cap->buffer_size_packet = MAX_BUF_SIZE;
10308c2ecf20Sopenharmony_ci		tmp_cap->buffer_size_streaming = MAX_BUF_SIZE;
10318c2ecf20Sopenharmony_ci		tmp_cap->num_buffers_packet = BUF_CHAIN_SIZE;
10328c2ecf20Sopenharmony_ci		tmp_cap->num_buffers_streaming = BUF_CHAIN_SIZE;
10338c2ecf20Sopenharmony_ci		tmp_cap->data_type = MOST_CH_CONTROL | MOST_CH_ASYNC |
10348c2ecf20Sopenharmony_ci				     MOST_CH_ISOC | MOST_CH_SYNC;
10358c2ecf20Sopenharmony_ci		if (usb_endpoint_dir_in(ep_desc))
10368c2ecf20Sopenharmony_ci			tmp_cap->direction = MOST_CH_RX;
10378c2ecf20Sopenharmony_ci		else
10388c2ecf20Sopenharmony_ci			tmp_cap->direction = MOST_CH_TX;
10398c2ecf20Sopenharmony_ci		tmp_cap++;
10408c2ecf20Sopenharmony_ci		init_usb_anchor(&mdev->busy_urbs[i]);
10418c2ecf20Sopenharmony_ci		spin_lock_init(&mdev->channel_lock[i]);
10428c2ecf20Sopenharmony_ci	}
10438c2ecf20Sopenharmony_ci	dev_dbg(dev, "claimed gadget: Vendor=%4.4x ProdID=%4.4x Bus=%02x Device=%02x\n",
10448c2ecf20Sopenharmony_ci		le16_to_cpu(usb_dev->descriptor.idVendor),
10458c2ecf20Sopenharmony_ci		le16_to_cpu(usb_dev->descriptor.idProduct),
10468c2ecf20Sopenharmony_ci		usb_dev->bus->busnum,
10478c2ecf20Sopenharmony_ci		usb_dev->devnum);
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_ci	dev_dbg(dev, "device path: /sys/bus/usb/devices/%d-%s:%d.%d\n",
10508c2ecf20Sopenharmony_ci		usb_dev->bus->busnum,
10518c2ecf20Sopenharmony_ci		usb_dev->devpath,
10528c2ecf20Sopenharmony_ci		usb_dev->config->desc.bConfigurationValue,
10538c2ecf20Sopenharmony_ci		usb_iface_desc->desc.bInterfaceNumber);
10548c2ecf20Sopenharmony_ci
10558c2ecf20Sopenharmony_ci	ret = most_register_interface(&mdev->iface);
10568c2ecf20Sopenharmony_ci	if (ret)
10578c2ecf20Sopenharmony_ci		goto err_free_busy_urbs;
10588c2ecf20Sopenharmony_ci
10598c2ecf20Sopenharmony_ci	mutex_lock(&mdev->io_mutex);
10608c2ecf20Sopenharmony_ci	if (le16_to_cpu(usb_dev->descriptor.idProduct) == USB_DEV_ID_OS81118 ||
10618c2ecf20Sopenharmony_ci	    le16_to_cpu(usb_dev->descriptor.idProduct) == USB_DEV_ID_OS81119 ||
10628c2ecf20Sopenharmony_ci	    le16_to_cpu(usb_dev->descriptor.idProduct) == USB_DEV_ID_OS81210) {
10638c2ecf20Sopenharmony_ci		mdev->dci = kzalloc(sizeof(*mdev->dci), GFP_KERNEL);
10648c2ecf20Sopenharmony_ci		if (!mdev->dci) {
10658c2ecf20Sopenharmony_ci			mutex_unlock(&mdev->io_mutex);
10668c2ecf20Sopenharmony_ci			most_deregister_interface(&mdev->iface);
10678c2ecf20Sopenharmony_ci			ret = -ENOMEM;
10688c2ecf20Sopenharmony_ci			goto err_free_busy_urbs;
10698c2ecf20Sopenharmony_ci		}
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_ci		mdev->dci->dev.init_name = "dci";
10728c2ecf20Sopenharmony_ci		mdev->dci->dev.parent = get_device(mdev->iface.dev);
10738c2ecf20Sopenharmony_ci		mdev->dci->dev.groups = dci_groups;
10748c2ecf20Sopenharmony_ci		mdev->dci->dev.release = release_dci;
10758c2ecf20Sopenharmony_ci		if (device_register(&mdev->dci->dev)) {
10768c2ecf20Sopenharmony_ci			mutex_unlock(&mdev->io_mutex);
10778c2ecf20Sopenharmony_ci			most_deregister_interface(&mdev->iface);
10788c2ecf20Sopenharmony_ci			ret = -ENOMEM;
10798c2ecf20Sopenharmony_ci			goto err_free_dci;
10808c2ecf20Sopenharmony_ci		}
10818c2ecf20Sopenharmony_ci		mdev->dci->usb_device = mdev->usb_device;
10828c2ecf20Sopenharmony_ci	}
10838c2ecf20Sopenharmony_ci	mutex_unlock(&mdev->io_mutex);
10848c2ecf20Sopenharmony_ci	return 0;
10858c2ecf20Sopenharmony_cierr_free_dci:
10868c2ecf20Sopenharmony_ci	put_device(&mdev->dci->dev);
10878c2ecf20Sopenharmony_cierr_free_busy_urbs:
10888c2ecf20Sopenharmony_ci	kfree(mdev->busy_urbs);
10898c2ecf20Sopenharmony_cierr_free_ep_address:
10908c2ecf20Sopenharmony_ci	kfree(mdev->ep_address);
10918c2ecf20Sopenharmony_cierr_free_cap:
10928c2ecf20Sopenharmony_ci	kfree(mdev->cap);
10938c2ecf20Sopenharmony_cierr_free_conf:
10948c2ecf20Sopenharmony_ci	kfree(mdev->conf);
10958c2ecf20Sopenharmony_cierr_free_mdev:
10968c2ecf20Sopenharmony_ci	put_device(&mdev->dev);
10978c2ecf20Sopenharmony_ci	return ret;
10988c2ecf20Sopenharmony_ci}
10998c2ecf20Sopenharmony_ci
11008c2ecf20Sopenharmony_ci/**
11018c2ecf20Sopenharmony_ci * hdm_disconnect - disconnect function of USB device driver
11028c2ecf20Sopenharmony_ci * @interface: Interface of the attached USB device
11038c2ecf20Sopenharmony_ci *
11048c2ecf20Sopenharmony_ci * This deregisters the interface with the core, removes the kernel timer
11058c2ecf20Sopenharmony_ci * and frees resources.
11068c2ecf20Sopenharmony_ci *
11078c2ecf20Sopenharmony_ci * Context: hub kernel thread
11088c2ecf20Sopenharmony_ci */
11098c2ecf20Sopenharmony_cistatic void hdm_disconnect(struct usb_interface *interface)
11108c2ecf20Sopenharmony_ci{
11118c2ecf20Sopenharmony_ci	struct most_dev *mdev = usb_get_intfdata(interface);
11128c2ecf20Sopenharmony_ci
11138c2ecf20Sopenharmony_ci	mutex_lock(&mdev->io_mutex);
11148c2ecf20Sopenharmony_ci	usb_set_intfdata(interface, NULL);
11158c2ecf20Sopenharmony_ci	mdev->usb_device = NULL;
11168c2ecf20Sopenharmony_ci	mutex_unlock(&mdev->io_mutex);
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_ci	del_timer_sync(&mdev->link_stat_timer);
11198c2ecf20Sopenharmony_ci	cancel_work_sync(&mdev->poll_work_obj);
11208c2ecf20Sopenharmony_ci
11218c2ecf20Sopenharmony_ci	if (mdev->dci)
11228c2ecf20Sopenharmony_ci		device_unregister(&mdev->dci->dev);
11238c2ecf20Sopenharmony_ci	most_deregister_interface(&mdev->iface);
11248c2ecf20Sopenharmony_ci
11258c2ecf20Sopenharmony_ci	kfree(mdev->busy_urbs);
11268c2ecf20Sopenharmony_ci	kfree(mdev->cap);
11278c2ecf20Sopenharmony_ci	kfree(mdev->conf);
11288c2ecf20Sopenharmony_ci	kfree(mdev->ep_address);
11298c2ecf20Sopenharmony_ci	put_device(&mdev->dci->dev);
11308c2ecf20Sopenharmony_ci	put_device(&mdev->dev);
11318c2ecf20Sopenharmony_ci}
11328c2ecf20Sopenharmony_ci
11338c2ecf20Sopenharmony_cistatic int hdm_suspend(struct usb_interface *interface, pm_message_t message)
11348c2ecf20Sopenharmony_ci{
11358c2ecf20Sopenharmony_ci	struct most_dev *mdev = usb_get_intfdata(interface);
11368c2ecf20Sopenharmony_ci	int i;
11378c2ecf20Sopenharmony_ci
11388c2ecf20Sopenharmony_ci	mutex_lock(&mdev->io_mutex);
11398c2ecf20Sopenharmony_ci	for (i = 0; i < mdev->iface.num_channels; i++) {
11408c2ecf20Sopenharmony_ci		most_stop_enqueue(&mdev->iface, i);
11418c2ecf20Sopenharmony_ci		usb_kill_anchored_urbs(&mdev->busy_urbs[i]);
11428c2ecf20Sopenharmony_ci	}
11438c2ecf20Sopenharmony_ci	mutex_unlock(&mdev->io_mutex);
11448c2ecf20Sopenharmony_ci	return 0;
11458c2ecf20Sopenharmony_ci}
11468c2ecf20Sopenharmony_ci
11478c2ecf20Sopenharmony_cistatic int hdm_resume(struct usb_interface *interface)
11488c2ecf20Sopenharmony_ci{
11498c2ecf20Sopenharmony_ci	struct most_dev *mdev = usb_get_intfdata(interface);
11508c2ecf20Sopenharmony_ci	int i;
11518c2ecf20Sopenharmony_ci
11528c2ecf20Sopenharmony_ci	mutex_lock(&mdev->io_mutex);
11538c2ecf20Sopenharmony_ci	for (i = 0; i < mdev->iface.num_channels; i++)
11548c2ecf20Sopenharmony_ci		most_resume_enqueue(&mdev->iface, i);
11558c2ecf20Sopenharmony_ci	mutex_unlock(&mdev->io_mutex);
11568c2ecf20Sopenharmony_ci	return 0;
11578c2ecf20Sopenharmony_ci}
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_cistatic struct usb_driver hdm_usb = {
11608c2ecf20Sopenharmony_ci	.name = "hdm_usb",
11618c2ecf20Sopenharmony_ci	.id_table = usbid,
11628c2ecf20Sopenharmony_ci	.probe = hdm_probe,
11638c2ecf20Sopenharmony_ci	.disconnect = hdm_disconnect,
11648c2ecf20Sopenharmony_ci	.resume = hdm_resume,
11658c2ecf20Sopenharmony_ci	.suspend = hdm_suspend,
11668c2ecf20Sopenharmony_ci};
11678c2ecf20Sopenharmony_ci
11688c2ecf20Sopenharmony_cimodule_usb_driver(hdm_usb);
11698c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
11708c2ecf20Sopenharmony_ciMODULE_AUTHOR("Christian Gromm <christian.gromm@microchip.com>");
11718c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("HDM_4_USB");
1172