xref: /kernel/linux/linux-6.6/drivers/most/most_usb.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * usb.c - Hardware dependent module for USB
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2013-2015 Microchip Technology Germany II GmbH & Co. KG
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/module.h>
962306a36Sopenharmony_ci#include <linux/fs.h>
1062306a36Sopenharmony_ci#include <linux/usb.h>
1162306a36Sopenharmony_ci#include <linux/slab.h>
1262306a36Sopenharmony_ci#include <linux/init.h>
1362306a36Sopenharmony_ci#include <linux/cdev.h>
1462306a36Sopenharmony_ci#include <linux/device.h>
1562306a36Sopenharmony_ci#include <linux/list.h>
1662306a36Sopenharmony_ci#include <linux/completion.h>
1762306a36Sopenharmony_ci#include <linux/mutex.h>
1862306a36Sopenharmony_ci#include <linux/spinlock.h>
1962306a36Sopenharmony_ci#include <linux/interrupt.h>
2062306a36Sopenharmony_ci#include <linux/workqueue.h>
2162306a36Sopenharmony_ci#include <linux/sysfs.h>
2262306a36Sopenharmony_ci#include <linux/dma-mapping.h>
2362306a36Sopenharmony_ci#include <linux/etherdevice.h>
2462306a36Sopenharmony_ci#include <linux/uaccess.h>
2562306a36Sopenharmony_ci#include <linux/most.h>
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#define USB_MTU			512
2862306a36Sopenharmony_ci#define NO_ISOCHRONOUS_URB	0
2962306a36Sopenharmony_ci#define AV_PACKETS_PER_XACT	2
3062306a36Sopenharmony_ci#define BUF_CHAIN_SIZE		0xFFFF
3162306a36Sopenharmony_ci#define MAX_NUM_ENDPOINTS	30
3262306a36Sopenharmony_ci#define MAX_SUFFIX_LEN		10
3362306a36Sopenharmony_ci#define MAX_STRING_LEN		80
3462306a36Sopenharmony_ci#define MAX_BUF_SIZE		0xFFFF
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci#define USB_VENDOR_ID_SMSC	0x0424  /* VID: SMSC */
3762306a36Sopenharmony_ci#define USB_DEV_ID_BRDG		0xC001  /* PID: USB Bridge */
3862306a36Sopenharmony_ci#define USB_DEV_ID_OS81118	0xCF18  /* PID: USB OS81118 */
3962306a36Sopenharmony_ci#define USB_DEV_ID_OS81119	0xCF19  /* PID: USB OS81119 */
4062306a36Sopenharmony_ci#define USB_DEV_ID_OS81210	0xCF30  /* PID: USB OS81210 */
4162306a36Sopenharmony_ci/* DRCI Addresses */
4262306a36Sopenharmony_ci#define DRCI_REG_NI_STATE	0x0100
4362306a36Sopenharmony_ci#define DRCI_REG_PACKET_BW	0x0101
4462306a36Sopenharmony_ci#define DRCI_REG_NODE_ADDR	0x0102
4562306a36Sopenharmony_ci#define DRCI_REG_NODE_POS	0x0103
4662306a36Sopenharmony_ci#define DRCI_REG_MEP_FILTER	0x0140
4762306a36Sopenharmony_ci#define DRCI_REG_HASH_TBL0	0x0141
4862306a36Sopenharmony_ci#define DRCI_REG_HASH_TBL1	0x0142
4962306a36Sopenharmony_ci#define DRCI_REG_HASH_TBL2	0x0143
5062306a36Sopenharmony_ci#define DRCI_REG_HASH_TBL3	0x0144
5162306a36Sopenharmony_ci#define DRCI_REG_HW_ADDR_HI	0x0145
5262306a36Sopenharmony_ci#define DRCI_REG_HW_ADDR_MI	0x0146
5362306a36Sopenharmony_ci#define DRCI_REG_HW_ADDR_LO	0x0147
5462306a36Sopenharmony_ci#define DRCI_REG_BASE		0x1100
5562306a36Sopenharmony_ci#define DRCI_COMMAND		0x02
5662306a36Sopenharmony_ci#define DRCI_READ_REQ		0xA0
5762306a36Sopenharmony_ci#define DRCI_WRITE_REQ		0xA1
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci/**
6062306a36Sopenharmony_ci * struct most_dci_obj - Direct Communication Interface
6162306a36Sopenharmony_ci * @kobj:position in sysfs
6262306a36Sopenharmony_ci * @usb_device: pointer to the usb device
6362306a36Sopenharmony_ci * @reg_addr: register address for arbitrary DCI access
6462306a36Sopenharmony_ci */
6562306a36Sopenharmony_cistruct most_dci_obj {
6662306a36Sopenharmony_ci	struct device dev;
6762306a36Sopenharmony_ci	struct usb_device *usb_device;
6862306a36Sopenharmony_ci	u16 reg_addr;
6962306a36Sopenharmony_ci};
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci#define to_dci_obj(p) container_of(p, struct most_dci_obj, dev)
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistruct most_dev;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistruct clear_hold_work {
7662306a36Sopenharmony_ci	struct work_struct ws;
7762306a36Sopenharmony_ci	struct most_dev *mdev;
7862306a36Sopenharmony_ci	unsigned int channel;
7962306a36Sopenharmony_ci	int pipe;
8062306a36Sopenharmony_ci};
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci#define to_clear_hold_work(w) container_of(w, struct clear_hold_work, ws)
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci/**
8562306a36Sopenharmony_ci * struct most_dev - holds all usb interface specific stuff
8662306a36Sopenharmony_ci * @usb_device: pointer to usb device
8762306a36Sopenharmony_ci * @iface: hardware interface
8862306a36Sopenharmony_ci * @cap: channel capabilities
8962306a36Sopenharmony_ci * @conf: channel configuration
9062306a36Sopenharmony_ci * @dci: direct communication interface of hardware
9162306a36Sopenharmony_ci * @ep_address: endpoint address table
9262306a36Sopenharmony_ci * @description: device description
9362306a36Sopenharmony_ci * @suffix: suffix for channel name
9462306a36Sopenharmony_ci * @channel_lock: synchronize channel access
9562306a36Sopenharmony_ci * @padding_active: indicates channel uses padding
9662306a36Sopenharmony_ci * @is_channel_healthy: health status table of each channel
9762306a36Sopenharmony_ci * @busy_urbs: list of anchored items
9862306a36Sopenharmony_ci * @io_mutex: synchronize I/O with disconnect
9962306a36Sopenharmony_ci * @link_stat_timer: timer for link status reports
10062306a36Sopenharmony_ci * @poll_work_obj: work for polling link status
10162306a36Sopenharmony_ci */
10262306a36Sopenharmony_cistruct most_dev {
10362306a36Sopenharmony_ci	struct device dev;
10462306a36Sopenharmony_ci	struct usb_device *usb_device;
10562306a36Sopenharmony_ci	struct most_interface iface;
10662306a36Sopenharmony_ci	struct most_channel_capability *cap;
10762306a36Sopenharmony_ci	struct most_channel_config *conf;
10862306a36Sopenharmony_ci	struct most_dci_obj *dci;
10962306a36Sopenharmony_ci	u8 *ep_address;
11062306a36Sopenharmony_ci	char description[MAX_STRING_LEN];
11162306a36Sopenharmony_ci	char suffix[MAX_NUM_ENDPOINTS][MAX_SUFFIX_LEN];
11262306a36Sopenharmony_ci	spinlock_t channel_lock[MAX_NUM_ENDPOINTS]; /* sync channel access */
11362306a36Sopenharmony_ci	bool padding_active[MAX_NUM_ENDPOINTS];
11462306a36Sopenharmony_ci	bool is_channel_healthy[MAX_NUM_ENDPOINTS];
11562306a36Sopenharmony_ci	struct clear_hold_work clear_work[MAX_NUM_ENDPOINTS];
11662306a36Sopenharmony_ci	struct usb_anchor *busy_urbs;
11762306a36Sopenharmony_ci	struct mutex io_mutex;
11862306a36Sopenharmony_ci	struct timer_list link_stat_timer;
11962306a36Sopenharmony_ci	struct work_struct poll_work_obj;
12062306a36Sopenharmony_ci	void (*on_netinfo)(struct most_interface *most_iface,
12162306a36Sopenharmony_ci			   unsigned char link_state, unsigned char *addrs);
12262306a36Sopenharmony_ci};
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci#define to_mdev(d) container_of(d, struct most_dev, iface)
12562306a36Sopenharmony_ci#define to_mdev_from_dev(d) container_of(d, struct most_dev, dev)
12662306a36Sopenharmony_ci#define to_mdev_from_work(w) container_of(w, struct most_dev, poll_work_obj)
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_cistatic void wq_clear_halt(struct work_struct *wq_obj);
12962306a36Sopenharmony_cistatic void wq_netinfo(struct work_struct *wq_obj);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci/**
13262306a36Sopenharmony_ci * drci_rd_reg - read a DCI register
13362306a36Sopenharmony_ci * @dev: usb device
13462306a36Sopenharmony_ci * @reg: register address
13562306a36Sopenharmony_ci * @buf: buffer to store data
13662306a36Sopenharmony_ci *
13762306a36Sopenharmony_ci * This is reads data from INIC's direct register communication interface
13862306a36Sopenharmony_ci */
13962306a36Sopenharmony_cistatic inline int drci_rd_reg(struct usb_device *dev, u16 reg, u16 *buf)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	int retval;
14262306a36Sopenharmony_ci	__le16 *dma_buf;
14362306a36Sopenharmony_ci	u8 req_type = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	dma_buf = kzalloc(sizeof(*dma_buf), GFP_KERNEL);
14662306a36Sopenharmony_ci	if (!dma_buf)
14762306a36Sopenharmony_ci		return -ENOMEM;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	retval = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
15062306a36Sopenharmony_ci				 DRCI_READ_REQ, req_type,
15162306a36Sopenharmony_ci				 0x0000,
15262306a36Sopenharmony_ci				 reg, dma_buf, sizeof(*dma_buf),
15362306a36Sopenharmony_ci				 USB_CTRL_GET_TIMEOUT);
15462306a36Sopenharmony_ci	*buf = le16_to_cpu(*dma_buf);
15562306a36Sopenharmony_ci	kfree(dma_buf);
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	if (retval < 0)
15862306a36Sopenharmony_ci		return retval;
15962306a36Sopenharmony_ci	return 0;
16062306a36Sopenharmony_ci}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci/**
16362306a36Sopenharmony_ci * drci_wr_reg - write a DCI register
16462306a36Sopenharmony_ci * @dev: usb device
16562306a36Sopenharmony_ci * @reg: register address
16662306a36Sopenharmony_ci * @data: data to write
16762306a36Sopenharmony_ci *
16862306a36Sopenharmony_ci * This is writes data to INIC's direct register communication interface
16962306a36Sopenharmony_ci */
17062306a36Sopenharmony_cistatic inline int drci_wr_reg(struct usb_device *dev, u16 reg, u16 data)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	return usb_control_msg(dev,
17362306a36Sopenharmony_ci			       usb_sndctrlpipe(dev, 0),
17462306a36Sopenharmony_ci			       DRCI_WRITE_REQ,
17562306a36Sopenharmony_ci			       USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
17662306a36Sopenharmony_ci			       data,
17762306a36Sopenharmony_ci			       reg,
17862306a36Sopenharmony_ci			       NULL,
17962306a36Sopenharmony_ci			       0,
18062306a36Sopenharmony_ci			       USB_CTRL_SET_TIMEOUT);
18162306a36Sopenharmony_ci}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_cistatic inline int start_sync_ep(struct usb_device *usb_dev, u16 ep)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	return drci_wr_reg(usb_dev, DRCI_REG_BASE + DRCI_COMMAND + ep * 16, 1);
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci/**
18962306a36Sopenharmony_ci * get_stream_frame_size - calculate frame size of current configuration
19062306a36Sopenharmony_ci * @dev: device structure
19162306a36Sopenharmony_ci * @cfg: channel configuration
19262306a36Sopenharmony_ci */
19362306a36Sopenharmony_cistatic unsigned int get_stream_frame_size(struct device *dev,
19462306a36Sopenharmony_ci					  struct most_channel_config *cfg)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	unsigned int frame_size;
19762306a36Sopenharmony_ci	unsigned int sub_size = cfg->subbuffer_size;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	if (!sub_size) {
20062306a36Sopenharmony_ci		dev_warn(dev, "Misconfig: Subbuffer size zero.\n");
20162306a36Sopenharmony_ci		return 0;
20262306a36Sopenharmony_ci	}
20362306a36Sopenharmony_ci	switch (cfg->data_type) {
20462306a36Sopenharmony_ci	case MOST_CH_ISOC:
20562306a36Sopenharmony_ci		frame_size = AV_PACKETS_PER_XACT * sub_size;
20662306a36Sopenharmony_ci		break;
20762306a36Sopenharmony_ci	case MOST_CH_SYNC:
20862306a36Sopenharmony_ci		if (cfg->packets_per_xact == 0) {
20962306a36Sopenharmony_ci			dev_warn(dev, "Misconfig: Packets per XACT zero\n");
21062306a36Sopenharmony_ci			frame_size = 0;
21162306a36Sopenharmony_ci		} else if (cfg->packets_per_xact == 0xFF) {
21262306a36Sopenharmony_ci			frame_size = (USB_MTU / sub_size) * sub_size;
21362306a36Sopenharmony_ci		} else {
21462306a36Sopenharmony_ci			frame_size = cfg->packets_per_xact * sub_size;
21562306a36Sopenharmony_ci		}
21662306a36Sopenharmony_ci		break;
21762306a36Sopenharmony_ci	default:
21862306a36Sopenharmony_ci		dev_warn(dev, "Query frame size of non-streaming channel\n");
21962306a36Sopenharmony_ci		frame_size = 0;
22062306a36Sopenharmony_ci		break;
22162306a36Sopenharmony_ci	}
22262306a36Sopenharmony_ci	return frame_size;
22362306a36Sopenharmony_ci}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci/**
22662306a36Sopenharmony_ci * hdm_poison_channel - mark buffers of this channel as invalid
22762306a36Sopenharmony_ci * @iface: pointer to the interface
22862306a36Sopenharmony_ci * @channel: channel ID
22962306a36Sopenharmony_ci *
23062306a36Sopenharmony_ci * This unlinks all URBs submitted to the HCD,
23162306a36Sopenharmony_ci * calls the associated completion function of the core and removes
23262306a36Sopenharmony_ci * them from the list.
23362306a36Sopenharmony_ci *
23462306a36Sopenharmony_ci * Returns 0 on success or error code otherwise.
23562306a36Sopenharmony_ci */
23662306a36Sopenharmony_cistatic int hdm_poison_channel(struct most_interface *iface, int channel)
23762306a36Sopenharmony_ci{
23862306a36Sopenharmony_ci	struct most_dev *mdev = to_mdev(iface);
23962306a36Sopenharmony_ci	unsigned long flags;
24062306a36Sopenharmony_ci	spinlock_t *lock; /* temp. lock */
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	if (channel < 0 || channel >= iface->num_channels) {
24362306a36Sopenharmony_ci		dev_warn(&mdev->usb_device->dev, "Channel ID out of range.\n");
24462306a36Sopenharmony_ci		return -ECHRNG;
24562306a36Sopenharmony_ci	}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	lock = mdev->channel_lock + channel;
24862306a36Sopenharmony_ci	spin_lock_irqsave(lock, flags);
24962306a36Sopenharmony_ci	mdev->is_channel_healthy[channel] = false;
25062306a36Sopenharmony_ci	spin_unlock_irqrestore(lock, flags);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	cancel_work_sync(&mdev->clear_work[channel].ws);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	mutex_lock(&mdev->io_mutex);
25562306a36Sopenharmony_ci	usb_kill_anchored_urbs(&mdev->busy_urbs[channel]);
25662306a36Sopenharmony_ci	if (mdev->padding_active[channel])
25762306a36Sopenharmony_ci		mdev->padding_active[channel] = false;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	if (mdev->conf[channel].data_type == MOST_CH_ASYNC) {
26062306a36Sopenharmony_ci		del_timer_sync(&mdev->link_stat_timer);
26162306a36Sopenharmony_ci		cancel_work_sync(&mdev->poll_work_obj);
26262306a36Sopenharmony_ci	}
26362306a36Sopenharmony_ci	mutex_unlock(&mdev->io_mutex);
26462306a36Sopenharmony_ci	return 0;
26562306a36Sopenharmony_ci}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci/**
26862306a36Sopenharmony_ci * hdm_add_padding - add padding bytes
26962306a36Sopenharmony_ci * @mdev: most device
27062306a36Sopenharmony_ci * @channel: channel ID
27162306a36Sopenharmony_ci * @mbo: buffer object
27262306a36Sopenharmony_ci *
27362306a36Sopenharmony_ci * This inserts the INIC hardware specific padding bytes into a streaming
27462306a36Sopenharmony_ci * channel's buffer
27562306a36Sopenharmony_ci */
27662306a36Sopenharmony_cistatic int hdm_add_padding(struct most_dev *mdev, int channel, struct mbo *mbo)
27762306a36Sopenharmony_ci{
27862306a36Sopenharmony_ci	struct most_channel_config *conf = &mdev->conf[channel];
27962306a36Sopenharmony_ci	unsigned int frame_size = get_stream_frame_size(&mdev->dev, conf);
28062306a36Sopenharmony_ci	unsigned int j, num_frames;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	if (!frame_size)
28362306a36Sopenharmony_ci		return -EINVAL;
28462306a36Sopenharmony_ci	num_frames = mbo->buffer_length / frame_size;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	if (num_frames < 1) {
28762306a36Sopenharmony_ci		dev_err(&mdev->usb_device->dev,
28862306a36Sopenharmony_ci			"Missed minimal transfer unit.\n");
28962306a36Sopenharmony_ci		return -EINVAL;
29062306a36Sopenharmony_ci	}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	for (j = num_frames - 1; j > 0; j--)
29362306a36Sopenharmony_ci		memmove(mbo->virt_address + j * USB_MTU,
29462306a36Sopenharmony_ci			mbo->virt_address + j * frame_size,
29562306a36Sopenharmony_ci			frame_size);
29662306a36Sopenharmony_ci	mbo->buffer_length = num_frames * USB_MTU;
29762306a36Sopenharmony_ci	return 0;
29862306a36Sopenharmony_ci}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci/**
30162306a36Sopenharmony_ci * hdm_remove_padding - remove padding bytes
30262306a36Sopenharmony_ci * @mdev: most device
30362306a36Sopenharmony_ci * @channel: channel ID
30462306a36Sopenharmony_ci * @mbo: buffer object
30562306a36Sopenharmony_ci *
30662306a36Sopenharmony_ci * This takes the INIC hardware specific padding bytes off a streaming
30762306a36Sopenharmony_ci * channel's buffer.
30862306a36Sopenharmony_ci */
30962306a36Sopenharmony_cistatic int hdm_remove_padding(struct most_dev *mdev, int channel,
31062306a36Sopenharmony_ci			      struct mbo *mbo)
31162306a36Sopenharmony_ci{
31262306a36Sopenharmony_ci	struct most_channel_config *const conf = &mdev->conf[channel];
31362306a36Sopenharmony_ci	unsigned int frame_size = get_stream_frame_size(&mdev->dev, conf);
31462306a36Sopenharmony_ci	unsigned int j, num_frames;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	if (!frame_size)
31762306a36Sopenharmony_ci		return -EINVAL;
31862306a36Sopenharmony_ci	num_frames = mbo->processed_length / USB_MTU;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	for (j = 1; j < num_frames; j++)
32162306a36Sopenharmony_ci		memmove(mbo->virt_address + frame_size * j,
32262306a36Sopenharmony_ci			mbo->virt_address + USB_MTU * j,
32362306a36Sopenharmony_ci			frame_size);
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	mbo->processed_length = frame_size * num_frames;
32662306a36Sopenharmony_ci	return 0;
32762306a36Sopenharmony_ci}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci/**
33062306a36Sopenharmony_ci * hdm_write_completion - completion function for submitted Tx URBs
33162306a36Sopenharmony_ci * @urb: the URB that has been completed
33262306a36Sopenharmony_ci *
33362306a36Sopenharmony_ci * This checks the status of the completed URB. In case the URB has been
33462306a36Sopenharmony_ci * unlinked before, it is immediately freed. On any other error the MBO
33562306a36Sopenharmony_ci * transfer flag is set. On success it frees allocated resources and calls
33662306a36Sopenharmony_ci * the completion function.
33762306a36Sopenharmony_ci *
33862306a36Sopenharmony_ci * Context: interrupt!
33962306a36Sopenharmony_ci */
34062306a36Sopenharmony_cistatic void hdm_write_completion(struct urb *urb)
34162306a36Sopenharmony_ci{
34262306a36Sopenharmony_ci	struct mbo *mbo = urb->context;
34362306a36Sopenharmony_ci	struct most_dev *mdev = to_mdev(mbo->ifp);
34462306a36Sopenharmony_ci	unsigned int channel = mbo->hdm_channel_id;
34562306a36Sopenharmony_ci	spinlock_t *lock = mdev->channel_lock + channel;
34662306a36Sopenharmony_ci	unsigned long flags;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	spin_lock_irqsave(lock, flags);
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	mbo->processed_length = 0;
35162306a36Sopenharmony_ci	mbo->status = MBO_E_INVAL;
35262306a36Sopenharmony_ci	if (likely(mdev->is_channel_healthy[channel])) {
35362306a36Sopenharmony_ci		switch (urb->status) {
35462306a36Sopenharmony_ci		case 0:
35562306a36Sopenharmony_ci		case -ESHUTDOWN:
35662306a36Sopenharmony_ci			mbo->processed_length = urb->actual_length;
35762306a36Sopenharmony_ci			mbo->status = MBO_SUCCESS;
35862306a36Sopenharmony_ci			break;
35962306a36Sopenharmony_ci		case -EPIPE:
36062306a36Sopenharmony_ci			dev_warn(&mdev->usb_device->dev,
36162306a36Sopenharmony_ci				 "Broken pipe on ep%02x\n",
36262306a36Sopenharmony_ci				 mdev->ep_address[channel]);
36362306a36Sopenharmony_ci			mdev->is_channel_healthy[channel] = false;
36462306a36Sopenharmony_ci			mdev->clear_work[channel].pipe = urb->pipe;
36562306a36Sopenharmony_ci			schedule_work(&mdev->clear_work[channel].ws);
36662306a36Sopenharmony_ci			break;
36762306a36Sopenharmony_ci		case -ENODEV:
36862306a36Sopenharmony_ci		case -EPROTO:
36962306a36Sopenharmony_ci			mbo->status = MBO_E_CLOSE;
37062306a36Sopenharmony_ci			break;
37162306a36Sopenharmony_ci		}
37262306a36Sopenharmony_ci	}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	spin_unlock_irqrestore(lock, flags);
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	if (likely(mbo->complete))
37762306a36Sopenharmony_ci		mbo->complete(mbo);
37862306a36Sopenharmony_ci	usb_free_urb(urb);
37962306a36Sopenharmony_ci}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci/**
38262306a36Sopenharmony_ci * hdm_read_completion - completion function for submitted Rx URBs
38362306a36Sopenharmony_ci * @urb: the URB that has been completed
38462306a36Sopenharmony_ci *
38562306a36Sopenharmony_ci * This checks the status of the completed URB. In case the URB has been
38662306a36Sopenharmony_ci * unlinked before it is immediately freed. On any other error the MBO transfer
38762306a36Sopenharmony_ci * flag is set. On success it frees allocated resources, removes
38862306a36Sopenharmony_ci * padding bytes -if necessary- and calls the completion function.
38962306a36Sopenharmony_ci *
39062306a36Sopenharmony_ci * Context: interrupt!
39162306a36Sopenharmony_ci */
39262306a36Sopenharmony_cistatic void hdm_read_completion(struct urb *urb)
39362306a36Sopenharmony_ci{
39462306a36Sopenharmony_ci	struct mbo *mbo = urb->context;
39562306a36Sopenharmony_ci	struct most_dev *mdev = to_mdev(mbo->ifp);
39662306a36Sopenharmony_ci	unsigned int channel = mbo->hdm_channel_id;
39762306a36Sopenharmony_ci	struct device *dev = &mdev->usb_device->dev;
39862306a36Sopenharmony_ci	spinlock_t *lock = mdev->channel_lock + channel;
39962306a36Sopenharmony_ci	unsigned long flags;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	spin_lock_irqsave(lock, flags);
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	mbo->processed_length = 0;
40462306a36Sopenharmony_ci	mbo->status = MBO_E_INVAL;
40562306a36Sopenharmony_ci	if (likely(mdev->is_channel_healthy[channel])) {
40662306a36Sopenharmony_ci		switch (urb->status) {
40762306a36Sopenharmony_ci		case 0:
40862306a36Sopenharmony_ci		case -ESHUTDOWN:
40962306a36Sopenharmony_ci			mbo->processed_length = urb->actual_length;
41062306a36Sopenharmony_ci			mbo->status = MBO_SUCCESS;
41162306a36Sopenharmony_ci			if (mdev->padding_active[channel] &&
41262306a36Sopenharmony_ci			    hdm_remove_padding(mdev, channel, mbo)) {
41362306a36Sopenharmony_ci				mbo->processed_length = 0;
41462306a36Sopenharmony_ci				mbo->status = MBO_E_INVAL;
41562306a36Sopenharmony_ci			}
41662306a36Sopenharmony_ci			break;
41762306a36Sopenharmony_ci		case -EPIPE:
41862306a36Sopenharmony_ci			dev_warn(dev, "Broken pipe on ep%02x\n",
41962306a36Sopenharmony_ci				 mdev->ep_address[channel]);
42062306a36Sopenharmony_ci			mdev->is_channel_healthy[channel] = false;
42162306a36Sopenharmony_ci			mdev->clear_work[channel].pipe = urb->pipe;
42262306a36Sopenharmony_ci			schedule_work(&mdev->clear_work[channel].ws);
42362306a36Sopenharmony_ci			break;
42462306a36Sopenharmony_ci		case -ENODEV:
42562306a36Sopenharmony_ci		case -EPROTO:
42662306a36Sopenharmony_ci			mbo->status = MBO_E_CLOSE;
42762306a36Sopenharmony_ci			break;
42862306a36Sopenharmony_ci		case -EOVERFLOW:
42962306a36Sopenharmony_ci			dev_warn(dev, "Babble on ep%02x\n",
43062306a36Sopenharmony_ci				 mdev->ep_address[channel]);
43162306a36Sopenharmony_ci			break;
43262306a36Sopenharmony_ci		}
43362306a36Sopenharmony_ci	}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	spin_unlock_irqrestore(lock, flags);
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	if (likely(mbo->complete))
43862306a36Sopenharmony_ci		mbo->complete(mbo);
43962306a36Sopenharmony_ci	usb_free_urb(urb);
44062306a36Sopenharmony_ci}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci/**
44362306a36Sopenharmony_ci * hdm_enqueue - receive a buffer to be used for data transfer
44462306a36Sopenharmony_ci * @iface: interface to enqueue to
44562306a36Sopenharmony_ci * @channel: ID of the channel
44662306a36Sopenharmony_ci * @mbo: pointer to the buffer object
44762306a36Sopenharmony_ci *
44862306a36Sopenharmony_ci * This allocates a new URB and fills it according to the channel
44962306a36Sopenharmony_ci * that is being used for transmission of data. Before the URB is
45062306a36Sopenharmony_ci * submitted it is stored in the private anchor list.
45162306a36Sopenharmony_ci *
45262306a36Sopenharmony_ci * Returns 0 on success. On any error the URB is freed and a error code
45362306a36Sopenharmony_ci * is returned.
45462306a36Sopenharmony_ci *
45562306a36Sopenharmony_ci * Context: Could in _some_ cases be interrupt!
45662306a36Sopenharmony_ci */
45762306a36Sopenharmony_cistatic int hdm_enqueue(struct most_interface *iface, int channel,
45862306a36Sopenharmony_ci		       struct mbo *mbo)
45962306a36Sopenharmony_ci{
46062306a36Sopenharmony_ci	struct most_dev *mdev = to_mdev(iface);
46162306a36Sopenharmony_ci	struct most_channel_config *conf;
46262306a36Sopenharmony_ci	int retval = 0;
46362306a36Sopenharmony_ci	struct urb *urb;
46462306a36Sopenharmony_ci	unsigned long length;
46562306a36Sopenharmony_ci	void *virt_address;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	if (!mbo)
46862306a36Sopenharmony_ci		return -EINVAL;
46962306a36Sopenharmony_ci	if (iface->num_channels <= channel || channel < 0)
47062306a36Sopenharmony_ci		return -ECHRNG;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	urb = usb_alloc_urb(NO_ISOCHRONOUS_URB, GFP_KERNEL);
47362306a36Sopenharmony_ci	if (!urb)
47462306a36Sopenharmony_ci		return -ENOMEM;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	conf = &mdev->conf[channel];
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	mutex_lock(&mdev->io_mutex);
47962306a36Sopenharmony_ci	if (!mdev->usb_device) {
48062306a36Sopenharmony_ci		retval = -ENODEV;
48162306a36Sopenharmony_ci		goto err_free_urb;
48262306a36Sopenharmony_ci	}
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	if ((conf->direction & MOST_CH_TX) && mdev->padding_active[channel] &&
48562306a36Sopenharmony_ci	    hdm_add_padding(mdev, channel, mbo)) {
48662306a36Sopenharmony_ci		retval = -EINVAL;
48762306a36Sopenharmony_ci		goto err_free_urb;
48862306a36Sopenharmony_ci	}
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	urb->transfer_dma = mbo->bus_address;
49162306a36Sopenharmony_ci	virt_address = mbo->virt_address;
49262306a36Sopenharmony_ci	length = mbo->buffer_length;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	if (conf->direction & MOST_CH_TX) {
49562306a36Sopenharmony_ci		usb_fill_bulk_urb(urb, mdev->usb_device,
49662306a36Sopenharmony_ci				  usb_sndbulkpipe(mdev->usb_device,
49762306a36Sopenharmony_ci						  mdev->ep_address[channel]),
49862306a36Sopenharmony_ci				  virt_address,
49962306a36Sopenharmony_ci				  length,
50062306a36Sopenharmony_ci				  hdm_write_completion,
50162306a36Sopenharmony_ci				  mbo);
50262306a36Sopenharmony_ci		if (conf->data_type != MOST_CH_ISOC &&
50362306a36Sopenharmony_ci		    conf->data_type != MOST_CH_SYNC)
50462306a36Sopenharmony_ci			urb->transfer_flags |= URB_ZERO_PACKET;
50562306a36Sopenharmony_ci	} else {
50662306a36Sopenharmony_ci		usb_fill_bulk_urb(urb, mdev->usb_device,
50762306a36Sopenharmony_ci				  usb_rcvbulkpipe(mdev->usb_device,
50862306a36Sopenharmony_ci						  mdev->ep_address[channel]),
50962306a36Sopenharmony_ci				  virt_address,
51062306a36Sopenharmony_ci				  length + conf->extra_len,
51162306a36Sopenharmony_ci				  hdm_read_completion,
51262306a36Sopenharmony_ci				  mbo);
51362306a36Sopenharmony_ci	}
51462306a36Sopenharmony_ci	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	usb_anchor_urb(urb, &mdev->busy_urbs[channel]);
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	retval = usb_submit_urb(urb, GFP_KERNEL);
51962306a36Sopenharmony_ci	if (retval) {
52062306a36Sopenharmony_ci		dev_err(&mdev->usb_device->dev,
52162306a36Sopenharmony_ci			"URB submit failed with error %d.\n", retval);
52262306a36Sopenharmony_ci		goto err_unanchor_urb;
52362306a36Sopenharmony_ci	}
52462306a36Sopenharmony_ci	mutex_unlock(&mdev->io_mutex);
52562306a36Sopenharmony_ci	return 0;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_cierr_unanchor_urb:
52862306a36Sopenharmony_ci	usb_unanchor_urb(urb);
52962306a36Sopenharmony_cierr_free_urb:
53062306a36Sopenharmony_ci	usb_free_urb(urb);
53162306a36Sopenharmony_ci	mutex_unlock(&mdev->io_mutex);
53262306a36Sopenharmony_ci	return retval;
53362306a36Sopenharmony_ci}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_cistatic void *hdm_dma_alloc(struct mbo *mbo, u32 size)
53662306a36Sopenharmony_ci{
53762306a36Sopenharmony_ci	struct most_dev *mdev = to_mdev(mbo->ifp);
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	return usb_alloc_coherent(mdev->usb_device, size, GFP_KERNEL,
54062306a36Sopenharmony_ci				  &mbo->bus_address);
54162306a36Sopenharmony_ci}
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_cistatic void hdm_dma_free(struct mbo *mbo, u32 size)
54462306a36Sopenharmony_ci{
54562306a36Sopenharmony_ci	struct most_dev *mdev = to_mdev(mbo->ifp);
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	usb_free_coherent(mdev->usb_device, size, mbo->virt_address,
54862306a36Sopenharmony_ci			  mbo->bus_address);
54962306a36Sopenharmony_ci}
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci/**
55262306a36Sopenharmony_ci * hdm_configure_channel - receive channel configuration from core
55362306a36Sopenharmony_ci * @iface: interface
55462306a36Sopenharmony_ci * @channel: channel ID
55562306a36Sopenharmony_ci * @conf: structure that holds the configuration information
55662306a36Sopenharmony_ci *
55762306a36Sopenharmony_ci * The attached network interface controller (NIC) supports a padding mode
55862306a36Sopenharmony_ci * to avoid short packets on USB, hence increasing the performance due to a
55962306a36Sopenharmony_ci * lower interrupt load. This mode is default for synchronous data and can
56062306a36Sopenharmony_ci * be switched on for isochronous data. In case padding is active the
56162306a36Sopenharmony_ci * driver needs to know the frame size of the payload in order to calculate
56262306a36Sopenharmony_ci * the number of bytes it needs to pad when transmitting or to cut off when
56362306a36Sopenharmony_ci * receiving data.
56462306a36Sopenharmony_ci *
56562306a36Sopenharmony_ci */
56662306a36Sopenharmony_cistatic int hdm_configure_channel(struct most_interface *iface, int channel,
56762306a36Sopenharmony_ci				 struct most_channel_config *conf)
56862306a36Sopenharmony_ci{
56962306a36Sopenharmony_ci	unsigned int num_frames;
57062306a36Sopenharmony_ci	unsigned int frame_size;
57162306a36Sopenharmony_ci	struct most_dev *mdev = to_mdev(iface);
57262306a36Sopenharmony_ci	struct device *dev = &mdev->usb_device->dev;
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	if (!conf) {
57562306a36Sopenharmony_ci		dev_err(dev, "Bad config pointer.\n");
57662306a36Sopenharmony_ci		return -EINVAL;
57762306a36Sopenharmony_ci	}
57862306a36Sopenharmony_ci	if (channel < 0 || channel >= iface->num_channels) {
57962306a36Sopenharmony_ci		dev_err(dev, "Channel ID out of range.\n");
58062306a36Sopenharmony_ci		return -EINVAL;
58162306a36Sopenharmony_ci	}
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	mdev->is_channel_healthy[channel] = true;
58462306a36Sopenharmony_ci	mdev->clear_work[channel].channel = channel;
58562306a36Sopenharmony_ci	mdev->clear_work[channel].mdev = mdev;
58662306a36Sopenharmony_ci	INIT_WORK(&mdev->clear_work[channel].ws, wq_clear_halt);
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	if (!conf->num_buffers || !conf->buffer_size) {
58962306a36Sopenharmony_ci		dev_err(dev, "Misconfig: buffer size or #buffers zero.\n");
59062306a36Sopenharmony_ci		return -EINVAL;
59162306a36Sopenharmony_ci	}
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	if (conf->data_type != MOST_CH_SYNC &&
59462306a36Sopenharmony_ci	    !(conf->data_type == MOST_CH_ISOC &&
59562306a36Sopenharmony_ci	      conf->packets_per_xact != 0xFF)) {
59662306a36Sopenharmony_ci		mdev->padding_active[channel] = false;
59762306a36Sopenharmony_ci		/*
59862306a36Sopenharmony_ci		 * Since the NIC's padding mode is not going to be
59962306a36Sopenharmony_ci		 * used, we can skip the frame size calculations and
60062306a36Sopenharmony_ci		 * move directly on to exit.
60162306a36Sopenharmony_ci		 */
60262306a36Sopenharmony_ci		goto exit;
60362306a36Sopenharmony_ci	}
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	mdev->padding_active[channel] = true;
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	frame_size = get_stream_frame_size(&mdev->dev, conf);
60862306a36Sopenharmony_ci	if (frame_size == 0 || frame_size > USB_MTU) {
60962306a36Sopenharmony_ci		dev_warn(dev, "Misconfig: frame size wrong\n");
61062306a36Sopenharmony_ci		return -EINVAL;
61162306a36Sopenharmony_ci	}
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	num_frames = conf->buffer_size / frame_size;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	if (conf->buffer_size % frame_size) {
61662306a36Sopenharmony_ci		u16 old_size = conf->buffer_size;
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci		conf->buffer_size = num_frames * frame_size;
61962306a36Sopenharmony_ci		dev_warn(dev, "%s: fixed buffer size (%d -> %d)\n",
62062306a36Sopenharmony_ci			 mdev->suffix[channel], old_size, conf->buffer_size);
62162306a36Sopenharmony_ci	}
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	/* calculate extra length to comply w/ HW padding */
62462306a36Sopenharmony_ci	conf->extra_len = num_frames * (USB_MTU - frame_size);
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ciexit:
62762306a36Sopenharmony_ci	mdev->conf[channel] = *conf;
62862306a36Sopenharmony_ci	if (conf->data_type == MOST_CH_ASYNC) {
62962306a36Sopenharmony_ci		u16 ep = mdev->ep_address[channel];
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci		if (start_sync_ep(mdev->usb_device, ep) < 0)
63262306a36Sopenharmony_ci			dev_warn(dev, "sync for ep%02x failed", ep);
63362306a36Sopenharmony_ci	}
63462306a36Sopenharmony_ci	return 0;
63562306a36Sopenharmony_ci}
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci/**
63862306a36Sopenharmony_ci * hdm_request_netinfo - request network information
63962306a36Sopenharmony_ci * @iface: pointer to interface
64062306a36Sopenharmony_ci * @channel: channel ID
64162306a36Sopenharmony_ci *
64262306a36Sopenharmony_ci * This is used as trigger to set up the link status timer that
64362306a36Sopenharmony_ci * polls for the NI state of the INIC every 2 seconds.
64462306a36Sopenharmony_ci *
64562306a36Sopenharmony_ci */
64662306a36Sopenharmony_cistatic void hdm_request_netinfo(struct most_interface *iface, int channel,
64762306a36Sopenharmony_ci				void (*on_netinfo)(struct most_interface *,
64862306a36Sopenharmony_ci						   unsigned char,
64962306a36Sopenharmony_ci						   unsigned char *))
65062306a36Sopenharmony_ci{
65162306a36Sopenharmony_ci	struct most_dev *mdev = to_mdev(iface);
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	mdev->on_netinfo = on_netinfo;
65462306a36Sopenharmony_ci	if (!on_netinfo)
65562306a36Sopenharmony_ci		return;
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	mdev->link_stat_timer.expires = jiffies + HZ;
65862306a36Sopenharmony_ci	mod_timer(&mdev->link_stat_timer, mdev->link_stat_timer.expires);
65962306a36Sopenharmony_ci}
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci/**
66262306a36Sopenharmony_ci * link_stat_timer_handler - schedule work obtaining mac address and link status
66362306a36Sopenharmony_ci * @t: pointer to timer_list which holds a pointer to the USB device instance
66462306a36Sopenharmony_ci *
66562306a36Sopenharmony_ci * The handler runs in interrupt context. That's why we need to defer the
66662306a36Sopenharmony_ci * tasks to a work queue.
66762306a36Sopenharmony_ci */
66862306a36Sopenharmony_cistatic void link_stat_timer_handler(struct timer_list *t)
66962306a36Sopenharmony_ci{
67062306a36Sopenharmony_ci	struct most_dev *mdev = from_timer(mdev, t, link_stat_timer);
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	schedule_work(&mdev->poll_work_obj);
67362306a36Sopenharmony_ci	mdev->link_stat_timer.expires = jiffies + (2 * HZ);
67462306a36Sopenharmony_ci	add_timer(&mdev->link_stat_timer);
67562306a36Sopenharmony_ci}
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci/**
67862306a36Sopenharmony_ci * wq_netinfo - work queue function to deliver latest networking information
67962306a36Sopenharmony_ci * @wq_obj: object that holds data for our deferred work to do
68062306a36Sopenharmony_ci *
68162306a36Sopenharmony_ci * This retrieves the network interface status of the USB INIC
68262306a36Sopenharmony_ci */
68362306a36Sopenharmony_cistatic void wq_netinfo(struct work_struct *wq_obj)
68462306a36Sopenharmony_ci{
68562306a36Sopenharmony_ci	struct most_dev *mdev = to_mdev_from_work(wq_obj);
68662306a36Sopenharmony_ci	struct usb_device *usb_device = mdev->usb_device;
68762306a36Sopenharmony_ci	struct device *dev = &usb_device->dev;
68862306a36Sopenharmony_ci	u16 hi, mi, lo, link;
68962306a36Sopenharmony_ci	u8 hw_addr[6];
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	if (drci_rd_reg(usb_device, DRCI_REG_HW_ADDR_HI, &hi)) {
69262306a36Sopenharmony_ci		dev_err(dev, "Vendor request 'hw_addr_hi' failed\n");
69362306a36Sopenharmony_ci		return;
69462306a36Sopenharmony_ci	}
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	if (drci_rd_reg(usb_device, DRCI_REG_HW_ADDR_MI, &mi)) {
69762306a36Sopenharmony_ci		dev_err(dev, "Vendor request 'hw_addr_mid' failed\n");
69862306a36Sopenharmony_ci		return;
69962306a36Sopenharmony_ci	}
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	if (drci_rd_reg(usb_device, DRCI_REG_HW_ADDR_LO, &lo)) {
70262306a36Sopenharmony_ci		dev_err(dev, "Vendor request 'hw_addr_low' failed\n");
70362306a36Sopenharmony_ci		return;
70462306a36Sopenharmony_ci	}
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	if (drci_rd_reg(usb_device, DRCI_REG_NI_STATE, &link)) {
70762306a36Sopenharmony_ci		dev_err(dev, "Vendor request 'link status' failed\n");
70862306a36Sopenharmony_ci		return;
70962306a36Sopenharmony_ci	}
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	hw_addr[0] = hi >> 8;
71262306a36Sopenharmony_ci	hw_addr[1] = hi;
71362306a36Sopenharmony_ci	hw_addr[2] = mi >> 8;
71462306a36Sopenharmony_ci	hw_addr[3] = mi;
71562306a36Sopenharmony_ci	hw_addr[4] = lo >> 8;
71662306a36Sopenharmony_ci	hw_addr[5] = lo;
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	if (mdev->on_netinfo)
71962306a36Sopenharmony_ci		mdev->on_netinfo(&mdev->iface, link, hw_addr);
72062306a36Sopenharmony_ci}
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci/**
72362306a36Sopenharmony_ci * wq_clear_halt - work queue function
72462306a36Sopenharmony_ci * @wq_obj: work_struct object to execute
72562306a36Sopenharmony_ci *
72662306a36Sopenharmony_ci * This sends a clear_halt to the given USB pipe.
72762306a36Sopenharmony_ci */
72862306a36Sopenharmony_cistatic void wq_clear_halt(struct work_struct *wq_obj)
72962306a36Sopenharmony_ci{
73062306a36Sopenharmony_ci	struct clear_hold_work *clear_work = to_clear_hold_work(wq_obj);
73162306a36Sopenharmony_ci	struct most_dev *mdev = clear_work->mdev;
73262306a36Sopenharmony_ci	unsigned int channel = clear_work->channel;
73362306a36Sopenharmony_ci	int pipe = clear_work->pipe;
73462306a36Sopenharmony_ci	int snd_pipe;
73562306a36Sopenharmony_ci	int peer;
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	mutex_lock(&mdev->io_mutex);
73862306a36Sopenharmony_ci	most_stop_enqueue(&mdev->iface, channel);
73962306a36Sopenharmony_ci	usb_kill_anchored_urbs(&mdev->busy_urbs[channel]);
74062306a36Sopenharmony_ci	if (usb_clear_halt(mdev->usb_device, pipe))
74162306a36Sopenharmony_ci		dev_warn(&mdev->usb_device->dev, "Failed to reset endpoint.\n");
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	/* If the functional Stall condition has been set on an
74462306a36Sopenharmony_ci	 * asynchronous rx channel, we need to clear the tx channel
74562306a36Sopenharmony_ci	 * too, since the hardware runs its clean-up sequence on both
74662306a36Sopenharmony_ci	 * channels, as they are physically one on the network.
74762306a36Sopenharmony_ci	 *
74862306a36Sopenharmony_ci	 * The USB interface that exposes the asynchronous channels
74962306a36Sopenharmony_ci	 * contains always two endpoints, and two only.
75062306a36Sopenharmony_ci	 */
75162306a36Sopenharmony_ci	if (mdev->conf[channel].data_type == MOST_CH_ASYNC &&
75262306a36Sopenharmony_ci	    mdev->conf[channel].direction == MOST_CH_RX) {
75362306a36Sopenharmony_ci		if (channel == 0)
75462306a36Sopenharmony_ci			peer = 1;
75562306a36Sopenharmony_ci		else
75662306a36Sopenharmony_ci			peer = 0;
75762306a36Sopenharmony_ci		snd_pipe = usb_sndbulkpipe(mdev->usb_device,
75862306a36Sopenharmony_ci					   mdev->ep_address[peer]);
75962306a36Sopenharmony_ci		usb_clear_halt(mdev->usb_device, snd_pipe);
76062306a36Sopenharmony_ci	}
76162306a36Sopenharmony_ci	mdev->is_channel_healthy[channel] = true;
76262306a36Sopenharmony_ci	most_resume_enqueue(&mdev->iface, channel);
76362306a36Sopenharmony_ci	mutex_unlock(&mdev->io_mutex);
76462306a36Sopenharmony_ci}
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci/*
76762306a36Sopenharmony_ci * hdm_usb_fops - file operation table for USB driver
76862306a36Sopenharmony_ci */
76962306a36Sopenharmony_cistatic const struct file_operations hdm_usb_fops = {
77062306a36Sopenharmony_ci	.owner = THIS_MODULE,
77162306a36Sopenharmony_ci};
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci/*
77462306a36Sopenharmony_ci * usb_device_id - ID table for HCD device probing
77562306a36Sopenharmony_ci */
77662306a36Sopenharmony_cistatic const struct usb_device_id usbid[] = {
77762306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_SMSC, USB_DEV_ID_BRDG), },
77862306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_SMSC, USB_DEV_ID_OS81118), },
77962306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_SMSC, USB_DEV_ID_OS81119), },
78062306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_SMSC, USB_DEV_ID_OS81210), },
78162306a36Sopenharmony_ci	{ } /* Terminating entry */
78262306a36Sopenharmony_ci};
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_cistruct regs {
78562306a36Sopenharmony_ci	const char *name;
78662306a36Sopenharmony_ci	u16 reg;
78762306a36Sopenharmony_ci};
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_cistatic const struct regs ro_regs[] = {
79062306a36Sopenharmony_ci	{ "ni_state", DRCI_REG_NI_STATE },
79162306a36Sopenharmony_ci	{ "packet_bandwidth", DRCI_REG_PACKET_BW },
79262306a36Sopenharmony_ci	{ "node_address", DRCI_REG_NODE_ADDR },
79362306a36Sopenharmony_ci	{ "node_position", DRCI_REG_NODE_POS },
79462306a36Sopenharmony_ci};
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_cistatic const struct regs rw_regs[] = {
79762306a36Sopenharmony_ci	{ "mep_filter", DRCI_REG_MEP_FILTER },
79862306a36Sopenharmony_ci	{ "mep_hash0", DRCI_REG_HASH_TBL0 },
79962306a36Sopenharmony_ci	{ "mep_hash1", DRCI_REG_HASH_TBL1 },
80062306a36Sopenharmony_ci	{ "mep_hash2", DRCI_REG_HASH_TBL2 },
80162306a36Sopenharmony_ci	{ "mep_hash3", DRCI_REG_HASH_TBL3 },
80262306a36Sopenharmony_ci	{ "mep_eui48_hi", DRCI_REG_HW_ADDR_HI },
80362306a36Sopenharmony_ci	{ "mep_eui48_mi", DRCI_REG_HW_ADDR_MI },
80462306a36Sopenharmony_ci	{ "mep_eui48_lo", DRCI_REG_HW_ADDR_LO },
80562306a36Sopenharmony_ci};
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_cistatic int get_stat_reg_addr(const struct regs *regs, int size,
80862306a36Sopenharmony_ci			     const char *name, u16 *reg_addr)
80962306a36Sopenharmony_ci{
81062306a36Sopenharmony_ci	int i;
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	for (i = 0; i < size; i++) {
81362306a36Sopenharmony_ci		if (sysfs_streq(name, regs[i].name)) {
81462306a36Sopenharmony_ci			*reg_addr = regs[i].reg;
81562306a36Sopenharmony_ci			return 0;
81662306a36Sopenharmony_ci		}
81762306a36Sopenharmony_ci	}
81862306a36Sopenharmony_ci	return -EINVAL;
81962306a36Sopenharmony_ci}
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci#define get_static_reg_addr(regs, name, reg_addr) \
82262306a36Sopenharmony_ci	get_stat_reg_addr(regs, ARRAY_SIZE(regs), name, reg_addr)
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_cistatic ssize_t value_show(struct device *dev, struct device_attribute *attr,
82562306a36Sopenharmony_ci			  char *buf)
82662306a36Sopenharmony_ci{
82762306a36Sopenharmony_ci	const char *name = attr->attr.name;
82862306a36Sopenharmony_ci	struct most_dci_obj *dci_obj = to_dci_obj(dev);
82962306a36Sopenharmony_ci	u16 val;
83062306a36Sopenharmony_ci	u16 reg_addr;
83162306a36Sopenharmony_ci	int err;
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	if (sysfs_streq(name, "arb_address"))
83462306a36Sopenharmony_ci		return sysfs_emit(buf, "%04x\n", dci_obj->reg_addr);
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	if (sysfs_streq(name, "arb_value"))
83762306a36Sopenharmony_ci		reg_addr = dci_obj->reg_addr;
83862306a36Sopenharmony_ci	else if (get_static_reg_addr(ro_regs, name, &reg_addr) &&
83962306a36Sopenharmony_ci		 get_static_reg_addr(rw_regs, name, &reg_addr))
84062306a36Sopenharmony_ci		return -EINVAL;
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	err = drci_rd_reg(dci_obj->usb_device, reg_addr, &val);
84362306a36Sopenharmony_ci	if (err < 0)
84462306a36Sopenharmony_ci		return err;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	return sysfs_emit(buf, "%04x\n", val);
84762306a36Sopenharmony_ci}
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_cistatic ssize_t value_store(struct device *dev, struct device_attribute *attr,
85062306a36Sopenharmony_ci			   const char *buf, size_t count)
85162306a36Sopenharmony_ci{
85262306a36Sopenharmony_ci	u16 val;
85362306a36Sopenharmony_ci	u16 reg_addr;
85462306a36Sopenharmony_ci	const char *name = attr->attr.name;
85562306a36Sopenharmony_ci	struct most_dci_obj *dci_obj = to_dci_obj(dev);
85662306a36Sopenharmony_ci	struct usb_device *usb_dev = dci_obj->usb_device;
85762306a36Sopenharmony_ci	int err;
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	err = kstrtou16(buf, 16, &val);
86062306a36Sopenharmony_ci	if (err)
86162306a36Sopenharmony_ci		return err;
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	if (sysfs_streq(name, "arb_address")) {
86462306a36Sopenharmony_ci		dci_obj->reg_addr = val;
86562306a36Sopenharmony_ci		return count;
86662306a36Sopenharmony_ci	}
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	if (sysfs_streq(name, "arb_value"))
86962306a36Sopenharmony_ci		err = drci_wr_reg(usb_dev, dci_obj->reg_addr, val);
87062306a36Sopenharmony_ci	else if (sysfs_streq(name, "sync_ep"))
87162306a36Sopenharmony_ci		err = start_sync_ep(usb_dev, val);
87262306a36Sopenharmony_ci	else if (!get_static_reg_addr(rw_regs, name, &reg_addr))
87362306a36Sopenharmony_ci		err = drci_wr_reg(usb_dev, reg_addr, val);
87462306a36Sopenharmony_ci	else
87562306a36Sopenharmony_ci		return -EINVAL;
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	if (err < 0)
87862306a36Sopenharmony_ci		return err;
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	return count;
88162306a36Sopenharmony_ci}
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_cistatic DEVICE_ATTR(ni_state, 0444, value_show, NULL);
88462306a36Sopenharmony_cistatic DEVICE_ATTR(packet_bandwidth, 0444, value_show, NULL);
88562306a36Sopenharmony_cistatic DEVICE_ATTR(node_address, 0444, value_show, NULL);
88662306a36Sopenharmony_cistatic DEVICE_ATTR(node_position, 0444, value_show, NULL);
88762306a36Sopenharmony_cistatic DEVICE_ATTR(sync_ep, 0200, NULL, value_store);
88862306a36Sopenharmony_cistatic DEVICE_ATTR(mep_filter, 0644, value_show, value_store);
88962306a36Sopenharmony_cistatic DEVICE_ATTR(mep_hash0, 0644, value_show, value_store);
89062306a36Sopenharmony_cistatic DEVICE_ATTR(mep_hash1, 0644, value_show, value_store);
89162306a36Sopenharmony_cistatic DEVICE_ATTR(mep_hash2, 0644, value_show, value_store);
89262306a36Sopenharmony_cistatic DEVICE_ATTR(mep_hash3, 0644, value_show, value_store);
89362306a36Sopenharmony_cistatic DEVICE_ATTR(mep_eui48_hi, 0644, value_show, value_store);
89462306a36Sopenharmony_cistatic DEVICE_ATTR(mep_eui48_mi, 0644, value_show, value_store);
89562306a36Sopenharmony_cistatic DEVICE_ATTR(mep_eui48_lo, 0644, value_show, value_store);
89662306a36Sopenharmony_cistatic DEVICE_ATTR(arb_address, 0644, value_show, value_store);
89762306a36Sopenharmony_cistatic DEVICE_ATTR(arb_value, 0644, value_show, value_store);
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_cistatic struct attribute *dci_attrs[] = {
90062306a36Sopenharmony_ci	&dev_attr_ni_state.attr,
90162306a36Sopenharmony_ci	&dev_attr_packet_bandwidth.attr,
90262306a36Sopenharmony_ci	&dev_attr_node_address.attr,
90362306a36Sopenharmony_ci	&dev_attr_node_position.attr,
90462306a36Sopenharmony_ci	&dev_attr_sync_ep.attr,
90562306a36Sopenharmony_ci	&dev_attr_mep_filter.attr,
90662306a36Sopenharmony_ci	&dev_attr_mep_hash0.attr,
90762306a36Sopenharmony_ci	&dev_attr_mep_hash1.attr,
90862306a36Sopenharmony_ci	&dev_attr_mep_hash2.attr,
90962306a36Sopenharmony_ci	&dev_attr_mep_hash3.attr,
91062306a36Sopenharmony_ci	&dev_attr_mep_eui48_hi.attr,
91162306a36Sopenharmony_ci	&dev_attr_mep_eui48_mi.attr,
91262306a36Sopenharmony_ci	&dev_attr_mep_eui48_lo.attr,
91362306a36Sopenharmony_ci	&dev_attr_arb_address.attr,
91462306a36Sopenharmony_ci	&dev_attr_arb_value.attr,
91562306a36Sopenharmony_ci	NULL,
91662306a36Sopenharmony_ci};
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ciATTRIBUTE_GROUPS(dci);
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_cistatic void release_dci(struct device *dev)
92162306a36Sopenharmony_ci{
92262306a36Sopenharmony_ci	struct most_dci_obj *dci = to_dci_obj(dev);
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci	put_device(dev->parent);
92562306a36Sopenharmony_ci	kfree(dci);
92662306a36Sopenharmony_ci}
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_cistatic void release_mdev(struct device *dev)
92962306a36Sopenharmony_ci{
93062306a36Sopenharmony_ci	struct most_dev *mdev = to_mdev_from_dev(dev);
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	kfree(mdev);
93362306a36Sopenharmony_ci}
93462306a36Sopenharmony_ci/**
93562306a36Sopenharmony_ci * hdm_probe - probe function of USB device driver
93662306a36Sopenharmony_ci * @interface: Interface of the attached USB device
93762306a36Sopenharmony_ci * @id: Pointer to the USB ID table.
93862306a36Sopenharmony_ci *
93962306a36Sopenharmony_ci * This allocates and initializes the device instance, adds the new
94062306a36Sopenharmony_ci * entry to the internal list, scans the USB descriptors and registers
94162306a36Sopenharmony_ci * the interface with the core.
94262306a36Sopenharmony_ci * Additionally, the DCI objects are created and the hardware is sync'd.
94362306a36Sopenharmony_ci *
94462306a36Sopenharmony_ci * Return 0 on success. In case of an error a negative number is returned.
94562306a36Sopenharmony_ci */
94662306a36Sopenharmony_cistatic int
94762306a36Sopenharmony_cihdm_probe(struct usb_interface *interface, const struct usb_device_id *id)
94862306a36Sopenharmony_ci{
94962306a36Sopenharmony_ci	struct usb_host_interface *usb_iface_desc = interface->cur_altsetting;
95062306a36Sopenharmony_ci	struct usb_device *usb_dev = interface_to_usbdev(interface);
95162306a36Sopenharmony_ci	struct device *dev = &usb_dev->dev;
95262306a36Sopenharmony_ci	struct most_dev *mdev;
95362306a36Sopenharmony_ci	unsigned int i;
95462306a36Sopenharmony_ci	unsigned int num_endpoints;
95562306a36Sopenharmony_ci	struct most_channel_capability *tmp_cap;
95662306a36Sopenharmony_ci	struct usb_endpoint_descriptor *ep_desc;
95762306a36Sopenharmony_ci	int ret = -ENOMEM;
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
96062306a36Sopenharmony_ci	if (!mdev)
96162306a36Sopenharmony_ci		return -ENOMEM;
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	usb_set_intfdata(interface, mdev);
96462306a36Sopenharmony_ci	num_endpoints = usb_iface_desc->desc.bNumEndpoints;
96562306a36Sopenharmony_ci	if (num_endpoints > MAX_NUM_ENDPOINTS) {
96662306a36Sopenharmony_ci		kfree(mdev);
96762306a36Sopenharmony_ci		return -EINVAL;
96862306a36Sopenharmony_ci	}
96962306a36Sopenharmony_ci	mutex_init(&mdev->io_mutex);
97062306a36Sopenharmony_ci	INIT_WORK(&mdev->poll_work_obj, wq_netinfo);
97162306a36Sopenharmony_ci	timer_setup(&mdev->link_stat_timer, link_stat_timer_handler, 0);
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	mdev->usb_device = usb_dev;
97462306a36Sopenharmony_ci	mdev->link_stat_timer.expires = jiffies + (2 * HZ);
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci	mdev->iface.mod = hdm_usb_fops.owner;
97762306a36Sopenharmony_ci	mdev->iface.dev = &mdev->dev;
97862306a36Sopenharmony_ci	mdev->iface.driver_dev = &interface->dev;
97962306a36Sopenharmony_ci	mdev->iface.interface = ITYPE_USB;
98062306a36Sopenharmony_ci	mdev->iface.configure = hdm_configure_channel;
98162306a36Sopenharmony_ci	mdev->iface.request_netinfo = hdm_request_netinfo;
98262306a36Sopenharmony_ci	mdev->iface.enqueue = hdm_enqueue;
98362306a36Sopenharmony_ci	mdev->iface.poison_channel = hdm_poison_channel;
98462306a36Sopenharmony_ci	mdev->iface.dma_alloc = hdm_dma_alloc;
98562306a36Sopenharmony_ci	mdev->iface.dma_free = hdm_dma_free;
98662306a36Sopenharmony_ci	mdev->iface.description = mdev->description;
98762306a36Sopenharmony_ci	mdev->iface.num_channels = num_endpoints;
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	snprintf(mdev->description, sizeof(mdev->description),
99062306a36Sopenharmony_ci		 "%d-%s:%d.%d",
99162306a36Sopenharmony_ci		 usb_dev->bus->busnum,
99262306a36Sopenharmony_ci		 usb_dev->devpath,
99362306a36Sopenharmony_ci		 usb_dev->config->desc.bConfigurationValue,
99462306a36Sopenharmony_ci		 usb_iface_desc->desc.bInterfaceNumber);
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	mdev->dev.init_name = mdev->description;
99762306a36Sopenharmony_ci	mdev->dev.parent = &interface->dev;
99862306a36Sopenharmony_ci	mdev->dev.release = release_mdev;
99962306a36Sopenharmony_ci	mdev->conf = kcalloc(num_endpoints, sizeof(*mdev->conf), GFP_KERNEL);
100062306a36Sopenharmony_ci	if (!mdev->conf)
100162306a36Sopenharmony_ci		goto err_free_mdev;
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci	mdev->cap = kcalloc(num_endpoints, sizeof(*mdev->cap), GFP_KERNEL);
100462306a36Sopenharmony_ci	if (!mdev->cap)
100562306a36Sopenharmony_ci		goto err_free_conf;
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	mdev->iface.channel_vector = mdev->cap;
100862306a36Sopenharmony_ci	mdev->ep_address =
100962306a36Sopenharmony_ci		kcalloc(num_endpoints, sizeof(*mdev->ep_address), GFP_KERNEL);
101062306a36Sopenharmony_ci	if (!mdev->ep_address)
101162306a36Sopenharmony_ci		goto err_free_cap;
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci	mdev->busy_urbs =
101462306a36Sopenharmony_ci		kcalloc(num_endpoints, sizeof(*mdev->busy_urbs), GFP_KERNEL);
101562306a36Sopenharmony_ci	if (!mdev->busy_urbs)
101662306a36Sopenharmony_ci		goto err_free_ep_address;
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci	tmp_cap = mdev->cap;
101962306a36Sopenharmony_ci	for (i = 0; i < num_endpoints; i++) {
102062306a36Sopenharmony_ci		ep_desc = &usb_iface_desc->endpoint[i].desc;
102162306a36Sopenharmony_ci		mdev->ep_address[i] = ep_desc->bEndpointAddress;
102262306a36Sopenharmony_ci		mdev->padding_active[i] = false;
102362306a36Sopenharmony_ci		mdev->is_channel_healthy[i] = true;
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci		snprintf(&mdev->suffix[i][0], MAX_SUFFIX_LEN, "ep%02x",
102662306a36Sopenharmony_ci			 mdev->ep_address[i]);
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci		tmp_cap->name_suffix = &mdev->suffix[i][0];
102962306a36Sopenharmony_ci		tmp_cap->buffer_size_packet = MAX_BUF_SIZE;
103062306a36Sopenharmony_ci		tmp_cap->buffer_size_streaming = MAX_BUF_SIZE;
103162306a36Sopenharmony_ci		tmp_cap->num_buffers_packet = BUF_CHAIN_SIZE;
103262306a36Sopenharmony_ci		tmp_cap->num_buffers_streaming = BUF_CHAIN_SIZE;
103362306a36Sopenharmony_ci		tmp_cap->data_type = MOST_CH_CONTROL | MOST_CH_ASYNC |
103462306a36Sopenharmony_ci				     MOST_CH_ISOC | MOST_CH_SYNC;
103562306a36Sopenharmony_ci		if (usb_endpoint_dir_in(ep_desc))
103662306a36Sopenharmony_ci			tmp_cap->direction = MOST_CH_RX;
103762306a36Sopenharmony_ci		else
103862306a36Sopenharmony_ci			tmp_cap->direction = MOST_CH_TX;
103962306a36Sopenharmony_ci		tmp_cap++;
104062306a36Sopenharmony_ci		init_usb_anchor(&mdev->busy_urbs[i]);
104162306a36Sopenharmony_ci		spin_lock_init(&mdev->channel_lock[i]);
104262306a36Sopenharmony_ci	}
104362306a36Sopenharmony_ci	dev_dbg(dev, "claimed gadget: Vendor=%4.4x ProdID=%4.4x Bus=%02x Device=%02x\n",
104462306a36Sopenharmony_ci		le16_to_cpu(usb_dev->descriptor.idVendor),
104562306a36Sopenharmony_ci		le16_to_cpu(usb_dev->descriptor.idProduct),
104662306a36Sopenharmony_ci		usb_dev->bus->busnum,
104762306a36Sopenharmony_ci		usb_dev->devnum);
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci	dev_dbg(dev, "device path: /sys/bus/usb/devices/%d-%s:%d.%d\n",
105062306a36Sopenharmony_ci		usb_dev->bus->busnum,
105162306a36Sopenharmony_ci		usb_dev->devpath,
105262306a36Sopenharmony_ci		usb_dev->config->desc.bConfigurationValue,
105362306a36Sopenharmony_ci		usb_iface_desc->desc.bInterfaceNumber);
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci	ret = most_register_interface(&mdev->iface);
105662306a36Sopenharmony_ci	if (ret)
105762306a36Sopenharmony_ci		goto err_free_busy_urbs;
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci	mutex_lock(&mdev->io_mutex);
106062306a36Sopenharmony_ci	if (le16_to_cpu(usb_dev->descriptor.idProduct) == USB_DEV_ID_OS81118 ||
106162306a36Sopenharmony_ci	    le16_to_cpu(usb_dev->descriptor.idProduct) == USB_DEV_ID_OS81119 ||
106262306a36Sopenharmony_ci	    le16_to_cpu(usb_dev->descriptor.idProduct) == USB_DEV_ID_OS81210) {
106362306a36Sopenharmony_ci		mdev->dci = kzalloc(sizeof(*mdev->dci), GFP_KERNEL);
106462306a36Sopenharmony_ci		if (!mdev->dci) {
106562306a36Sopenharmony_ci			mutex_unlock(&mdev->io_mutex);
106662306a36Sopenharmony_ci			most_deregister_interface(&mdev->iface);
106762306a36Sopenharmony_ci			ret = -ENOMEM;
106862306a36Sopenharmony_ci			goto err_free_busy_urbs;
106962306a36Sopenharmony_ci		}
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci		mdev->dci->dev.init_name = "dci";
107262306a36Sopenharmony_ci		mdev->dci->dev.parent = get_device(mdev->iface.dev);
107362306a36Sopenharmony_ci		mdev->dci->dev.groups = dci_groups;
107462306a36Sopenharmony_ci		mdev->dci->dev.release = release_dci;
107562306a36Sopenharmony_ci		if (device_register(&mdev->dci->dev)) {
107662306a36Sopenharmony_ci			mutex_unlock(&mdev->io_mutex);
107762306a36Sopenharmony_ci			most_deregister_interface(&mdev->iface);
107862306a36Sopenharmony_ci			ret = -ENOMEM;
107962306a36Sopenharmony_ci			goto err_free_dci;
108062306a36Sopenharmony_ci		}
108162306a36Sopenharmony_ci		mdev->dci->usb_device = mdev->usb_device;
108262306a36Sopenharmony_ci	}
108362306a36Sopenharmony_ci	mutex_unlock(&mdev->io_mutex);
108462306a36Sopenharmony_ci	return 0;
108562306a36Sopenharmony_cierr_free_dci:
108662306a36Sopenharmony_ci	put_device(&mdev->dci->dev);
108762306a36Sopenharmony_cierr_free_busy_urbs:
108862306a36Sopenharmony_ci	kfree(mdev->busy_urbs);
108962306a36Sopenharmony_cierr_free_ep_address:
109062306a36Sopenharmony_ci	kfree(mdev->ep_address);
109162306a36Sopenharmony_cierr_free_cap:
109262306a36Sopenharmony_ci	kfree(mdev->cap);
109362306a36Sopenharmony_cierr_free_conf:
109462306a36Sopenharmony_ci	kfree(mdev->conf);
109562306a36Sopenharmony_cierr_free_mdev:
109662306a36Sopenharmony_ci	put_device(&mdev->dev);
109762306a36Sopenharmony_ci	return ret;
109862306a36Sopenharmony_ci}
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci/**
110162306a36Sopenharmony_ci * hdm_disconnect - disconnect function of USB device driver
110262306a36Sopenharmony_ci * @interface: Interface of the attached USB device
110362306a36Sopenharmony_ci *
110462306a36Sopenharmony_ci * This deregisters the interface with the core, removes the kernel timer
110562306a36Sopenharmony_ci * and frees resources.
110662306a36Sopenharmony_ci *
110762306a36Sopenharmony_ci * Context: hub kernel thread
110862306a36Sopenharmony_ci */
110962306a36Sopenharmony_cistatic void hdm_disconnect(struct usb_interface *interface)
111062306a36Sopenharmony_ci{
111162306a36Sopenharmony_ci	struct most_dev *mdev = usb_get_intfdata(interface);
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci	mutex_lock(&mdev->io_mutex);
111462306a36Sopenharmony_ci	usb_set_intfdata(interface, NULL);
111562306a36Sopenharmony_ci	mdev->usb_device = NULL;
111662306a36Sopenharmony_ci	mutex_unlock(&mdev->io_mutex);
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci	del_timer_sync(&mdev->link_stat_timer);
111962306a36Sopenharmony_ci	cancel_work_sync(&mdev->poll_work_obj);
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci	if (mdev->dci)
112262306a36Sopenharmony_ci		device_unregister(&mdev->dci->dev);
112362306a36Sopenharmony_ci	most_deregister_interface(&mdev->iface);
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci	kfree(mdev->busy_urbs);
112662306a36Sopenharmony_ci	kfree(mdev->cap);
112762306a36Sopenharmony_ci	kfree(mdev->conf);
112862306a36Sopenharmony_ci	kfree(mdev->ep_address);
112962306a36Sopenharmony_ci	put_device(&mdev->dci->dev);
113062306a36Sopenharmony_ci	put_device(&mdev->dev);
113162306a36Sopenharmony_ci}
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_cistatic int hdm_suspend(struct usb_interface *interface, pm_message_t message)
113462306a36Sopenharmony_ci{
113562306a36Sopenharmony_ci	struct most_dev *mdev = usb_get_intfdata(interface);
113662306a36Sopenharmony_ci	int i;
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci	mutex_lock(&mdev->io_mutex);
113962306a36Sopenharmony_ci	for (i = 0; i < mdev->iface.num_channels; i++) {
114062306a36Sopenharmony_ci		most_stop_enqueue(&mdev->iface, i);
114162306a36Sopenharmony_ci		usb_kill_anchored_urbs(&mdev->busy_urbs[i]);
114262306a36Sopenharmony_ci	}
114362306a36Sopenharmony_ci	mutex_unlock(&mdev->io_mutex);
114462306a36Sopenharmony_ci	return 0;
114562306a36Sopenharmony_ci}
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_cistatic int hdm_resume(struct usb_interface *interface)
114862306a36Sopenharmony_ci{
114962306a36Sopenharmony_ci	struct most_dev *mdev = usb_get_intfdata(interface);
115062306a36Sopenharmony_ci	int i;
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci	mutex_lock(&mdev->io_mutex);
115362306a36Sopenharmony_ci	for (i = 0; i < mdev->iface.num_channels; i++)
115462306a36Sopenharmony_ci		most_resume_enqueue(&mdev->iface, i);
115562306a36Sopenharmony_ci	mutex_unlock(&mdev->io_mutex);
115662306a36Sopenharmony_ci	return 0;
115762306a36Sopenharmony_ci}
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_cistatic struct usb_driver hdm_usb = {
116062306a36Sopenharmony_ci	.name = "hdm_usb",
116162306a36Sopenharmony_ci	.id_table = usbid,
116262306a36Sopenharmony_ci	.probe = hdm_probe,
116362306a36Sopenharmony_ci	.disconnect = hdm_disconnect,
116462306a36Sopenharmony_ci	.resume = hdm_resume,
116562306a36Sopenharmony_ci	.suspend = hdm_suspend,
116662306a36Sopenharmony_ci};
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_cimodule_usb_driver(hdm_usb);
116962306a36Sopenharmony_ciMODULE_LICENSE("GPL");
117062306a36Sopenharmony_ciMODULE_AUTHOR("Christian Gromm <christian.gromm@microchip.com>");
117162306a36Sopenharmony_ciMODULE_DESCRIPTION("HDM_4_USB");
1172