197a6f772SChristian Gromm // SPDX-License-Identifier: GPL-2.0
297a6f772SChristian Gromm /*
397a6f772SChristian Gromm * usb.c - Hardware dependent module for USB
497a6f772SChristian Gromm *
597a6f772SChristian Gromm * Copyright (C) 2013-2015 Microchip Technology Germany II GmbH & Co. KG
697a6f772SChristian Gromm */
797a6f772SChristian Gromm
897a6f772SChristian Gromm #include <linux/module.h>
997a6f772SChristian Gromm #include <linux/fs.h>
1097a6f772SChristian Gromm #include <linux/usb.h>
1197a6f772SChristian Gromm #include <linux/slab.h>
1297a6f772SChristian Gromm #include <linux/init.h>
1397a6f772SChristian Gromm #include <linux/cdev.h>
1497a6f772SChristian Gromm #include <linux/device.h>
1597a6f772SChristian Gromm #include <linux/list.h>
1697a6f772SChristian Gromm #include <linux/completion.h>
1797a6f772SChristian Gromm #include <linux/mutex.h>
1897a6f772SChristian Gromm #include <linux/spinlock.h>
1997a6f772SChristian Gromm #include <linux/interrupt.h>
2097a6f772SChristian Gromm #include <linux/workqueue.h>
2197a6f772SChristian Gromm #include <linux/sysfs.h>
2297a6f772SChristian Gromm #include <linux/dma-mapping.h>
2397a6f772SChristian Gromm #include <linux/etherdevice.h>
2497a6f772SChristian Gromm #include <linux/uaccess.h>
2597a6f772SChristian Gromm #include <linux/most.h>
2697a6f772SChristian Gromm
2797a6f772SChristian Gromm #define USB_MTU 512
2897a6f772SChristian Gromm #define NO_ISOCHRONOUS_URB 0
2997a6f772SChristian Gromm #define AV_PACKETS_PER_XACT 2
3097a6f772SChristian Gromm #define BUF_CHAIN_SIZE 0xFFFF
3197a6f772SChristian Gromm #define MAX_NUM_ENDPOINTS 30
3297a6f772SChristian Gromm #define MAX_SUFFIX_LEN 10
3397a6f772SChristian Gromm #define MAX_STRING_LEN 80
3497a6f772SChristian Gromm #define MAX_BUF_SIZE 0xFFFF
3597a6f772SChristian Gromm
3697a6f772SChristian Gromm #define USB_VENDOR_ID_SMSC 0x0424 /* VID: SMSC */
3797a6f772SChristian Gromm #define USB_DEV_ID_BRDG 0xC001 /* PID: USB Bridge */
3897a6f772SChristian Gromm #define USB_DEV_ID_OS81118 0xCF18 /* PID: USB OS81118 */
3997a6f772SChristian Gromm #define USB_DEV_ID_OS81119 0xCF19 /* PID: USB OS81119 */
4097a6f772SChristian Gromm #define USB_DEV_ID_OS81210 0xCF30 /* PID: USB OS81210 */
4197a6f772SChristian Gromm /* DRCI Addresses */
4297a6f772SChristian Gromm #define DRCI_REG_NI_STATE 0x0100
4397a6f772SChristian Gromm #define DRCI_REG_PACKET_BW 0x0101
4497a6f772SChristian Gromm #define DRCI_REG_NODE_ADDR 0x0102
4597a6f772SChristian Gromm #define DRCI_REG_NODE_POS 0x0103
4697a6f772SChristian Gromm #define DRCI_REG_MEP_FILTER 0x0140
4797a6f772SChristian Gromm #define DRCI_REG_HASH_TBL0 0x0141
4897a6f772SChristian Gromm #define DRCI_REG_HASH_TBL1 0x0142
4997a6f772SChristian Gromm #define DRCI_REG_HASH_TBL2 0x0143
5097a6f772SChristian Gromm #define DRCI_REG_HASH_TBL3 0x0144
5197a6f772SChristian Gromm #define DRCI_REG_HW_ADDR_HI 0x0145
5297a6f772SChristian Gromm #define DRCI_REG_HW_ADDR_MI 0x0146
5397a6f772SChristian Gromm #define DRCI_REG_HW_ADDR_LO 0x0147
5497a6f772SChristian Gromm #define DRCI_REG_BASE 0x1100
5597a6f772SChristian Gromm #define DRCI_COMMAND 0x02
5697a6f772SChristian Gromm #define DRCI_READ_REQ 0xA0
5797a6f772SChristian Gromm #define DRCI_WRITE_REQ 0xA1
5897a6f772SChristian Gromm
5997a6f772SChristian Gromm /**
6097a6f772SChristian Gromm * struct most_dci_obj - Direct Communication Interface
6197a6f772SChristian Gromm * @kobj:position in sysfs
6297a6f772SChristian Gromm * @usb_device: pointer to the usb device
6397a6f772SChristian Gromm * @reg_addr: register address for arbitrary DCI access
6497a6f772SChristian Gromm */
6597a6f772SChristian Gromm struct most_dci_obj {
6697a6f772SChristian Gromm struct device dev;
6797a6f772SChristian Gromm struct usb_device *usb_device;
6897a6f772SChristian Gromm u16 reg_addr;
6997a6f772SChristian Gromm };
7097a6f772SChristian Gromm
7197a6f772SChristian Gromm #define to_dci_obj(p) container_of(p, struct most_dci_obj, dev)
7297a6f772SChristian Gromm
7397a6f772SChristian Gromm struct most_dev;
7497a6f772SChristian Gromm
7597a6f772SChristian Gromm struct clear_hold_work {
7697a6f772SChristian Gromm struct work_struct ws;
7797a6f772SChristian Gromm struct most_dev *mdev;
7897a6f772SChristian Gromm unsigned int channel;
7997a6f772SChristian Gromm int pipe;
8097a6f772SChristian Gromm };
8197a6f772SChristian Gromm
8297a6f772SChristian Gromm #define to_clear_hold_work(w) container_of(w, struct clear_hold_work, ws)
8397a6f772SChristian Gromm
8497a6f772SChristian Gromm /**
8597a6f772SChristian Gromm * struct most_dev - holds all usb interface specific stuff
8697a6f772SChristian Gromm * @usb_device: pointer to usb device
8797a6f772SChristian Gromm * @iface: hardware interface
8897a6f772SChristian Gromm * @cap: channel capabilities
8997a6f772SChristian Gromm * @conf: channel configuration
9097a6f772SChristian Gromm * @dci: direct communication interface of hardware
9197a6f772SChristian Gromm * @ep_address: endpoint address table
9297a6f772SChristian Gromm * @description: device description
9397a6f772SChristian Gromm * @suffix: suffix for channel name
9497a6f772SChristian Gromm * @channel_lock: synchronize channel access
9597a6f772SChristian Gromm * @padding_active: indicates channel uses padding
9697a6f772SChristian Gromm * @is_channel_healthy: health status table of each channel
9797a6f772SChristian Gromm * @busy_urbs: list of anchored items
9897a6f772SChristian Gromm * @io_mutex: synchronize I/O with disconnect
9997a6f772SChristian Gromm * @link_stat_timer: timer for link status reports
10097a6f772SChristian Gromm * @poll_work_obj: work for polling link status
10197a6f772SChristian Gromm */
10297a6f772SChristian Gromm struct most_dev {
10397a6f772SChristian Gromm struct device dev;
10497a6f772SChristian Gromm struct usb_device *usb_device;
10597a6f772SChristian Gromm struct most_interface iface;
10697a6f772SChristian Gromm struct most_channel_capability *cap;
10797a6f772SChristian Gromm struct most_channel_config *conf;
10897a6f772SChristian Gromm struct most_dci_obj *dci;
10997a6f772SChristian Gromm u8 *ep_address;
11097a6f772SChristian Gromm char description[MAX_STRING_LEN];
11197a6f772SChristian Gromm char suffix[MAX_NUM_ENDPOINTS][MAX_SUFFIX_LEN];
11297a6f772SChristian Gromm spinlock_t channel_lock[MAX_NUM_ENDPOINTS]; /* sync channel access */
11397a6f772SChristian Gromm bool padding_active[MAX_NUM_ENDPOINTS];
11497a6f772SChristian Gromm bool is_channel_healthy[MAX_NUM_ENDPOINTS];
11597a6f772SChristian Gromm struct clear_hold_work clear_work[MAX_NUM_ENDPOINTS];
11697a6f772SChristian Gromm struct usb_anchor *busy_urbs;
11797a6f772SChristian Gromm struct mutex io_mutex;
11897a6f772SChristian Gromm struct timer_list link_stat_timer;
11997a6f772SChristian Gromm struct work_struct poll_work_obj;
12097a6f772SChristian Gromm void (*on_netinfo)(struct most_interface *most_iface,
12197a6f772SChristian Gromm unsigned char link_state, unsigned char *addrs);
12297a6f772SChristian Gromm };
12397a6f772SChristian Gromm
12497a6f772SChristian Gromm #define to_mdev(d) container_of(d, struct most_dev, iface)
12597a6f772SChristian Gromm #define to_mdev_from_dev(d) container_of(d, struct most_dev, dev)
12697a6f772SChristian Gromm #define to_mdev_from_work(w) container_of(w, struct most_dev, poll_work_obj)
12797a6f772SChristian Gromm
12897a6f772SChristian Gromm static void wq_clear_halt(struct work_struct *wq_obj);
12997a6f772SChristian Gromm static void wq_netinfo(struct work_struct *wq_obj);
13097a6f772SChristian Gromm
13197a6f772SChristian Gromm /**
13297a6f772SChristian Gromm * drci_rd_reg - read a DCI register
13397a6f772SChristian Gromm * @dev: usb device
13497a6f772SChristian Gromm * @reg: register address
13597a6f772SChristian Gromm * @buf: buffer to store data
13697a6f772SChristian Gromm *
13797a6f772SChristian Gromm * This is reads data from INIC's direct register communication interface
13897a6f772SChristian Gromm */
drci_rd_reg(struct usb_device * dev,u16 reg,u16 * buf)13997a6f772SChristian Gromm static inline int drci_rd_reg(struct usb_device *dev, u16 reg, u16 *buf)
14097a6f772SChristian Gromm {
14197a6f772SChristian Gromm int retval;
14297a6f772SChristian Gromm __le16 *dma_buf;
14397a6f772SChristian Gromm u8 req_type = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE;
14497a6f772SChristian Gromm
14597a6f772SChristian Gromm dma_buf = kzalloc(sizeof(*dma_buf), GFP_KERNEL);
14697a6f772SChristian Gromm if (!dma_buf)
14797a6f772SChristian Gromm return -ENOMEM;
14897a6f772SChristian Gromm
14997a6f772SChristian Gromm retval = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
15097a6f772SChristian Gromm DRCI_READ_REQ, req_type,
15197a6f772SChristian Gromm 0x0000,
15263b3e810SJohan Hovold reg, dma_buf, sizeof(*dma_buf),
15363b3e810SJohan Hovold USB_CTRL_GET_TIMEOUT);
15497a6f772SChristian Gromm *buf = le16_to_cpu(*dma_buf);
15597a6f772SChristian Gromm kfree(dma_buf);
15697a6f772SChristian Gromm
15797a6f772SChristian Gromm if (retval < 0)
15897a6f772SChristian Gromm return retval;
15997a6f772SChristian Gromm return 0;
16097a6f772SChristian Gromm }
16197a6f772SChristian Gromm
16297a6f772SChristian Gromm /**
16397a6f772SChristian Gromm * drci_wr_reg - write a DCI register
16497a6f772SChristian Gromm * @dev: usb device
16597a6f772SChristian Gromm * @reg: register address
16697a6f772SChristian Gromm * @data: data to write
16797a6f772SChristian Gromm *
16897a6f772SChristian Gromm * This is writes data to INIC's direct register communication interface
16997a6f772SChristian Gromm */
drci_wr_reg(struct usb_device * dev,u16 reg,u16 data)17097a6f772SChristian Gromm static inline int drci_wr_reg(struct usb_device *dev, u16 reg, u16 data)
17197a6f772SChristian Gromm {
17297a6f772SChristian Gromm return usb_control_msg(dev,
17397a6f772SChristian Gromm usb_sndctrlpipe(dev, 0),
17497a6f772SChristian Gromm DRCI_WRITE_REQ,
17597a6f772SChristian Gromm USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
17697a6f772SChristian Gromm data,
17797a6f772SChristian Gromm reg,
17897a6f772SChristian Gromm NULL,
17997a6f772SChristian Gromm 0,
18063b3e810SJohan Hovold USB_CTRL_SET_TIMEOUT);
18197a6f772SChristian Gromm }
18297a6f772SChristian Gromm
start_sync_ep(struct usb_device * usb_dev,u16 ep)18397a6f772SChristian Gromm static inline int start_sync_ep(struct usb_device *usb_dev, u16 ep)
18497a6f772SChristian Gromm {
18597a6f772SChristian Gromm return drci_wr_reg(usb_dev, DRCI_REG_BASE + DRCI_COMMAND + ep * 16, 1);
18697a6f772SChristian Gromm }
18797a6f772SChristian Gromm
18897a6f772SChristian Gromm /**
18997a6f772SChristian Gromm * get_stream_frame_size - calculate frame size of current configuration
19097a6f772SChristian Gromm * @dev: device structure
19197a6f772SChristian Gromm * @cfg: channel configuration
19297a6f772SChristian Gromm */
get_stream_frame_size(struct device * dev,struct most_channel_config * cfg)19397a6f772SChristian Gromm static unsigned int get_stream_frame_size(struct device *dev,
19497a6f772SChristian Gromm struct most_channel_config *cfg)
19597a6f772SChristian Gromm {
19697a6f772SChristian Gromm unsigned int frame_size;
19797a6f772SChristian Gromm unsigned int sub_size = cfg->subbuffer_size;
19897a6f772SChristian Gromm
19997a6f772SChristian Gromm if (!sub_size) {
20097a6f772SChristian Gromm dev_warn(dev, "Misconfig: Subbuffer size zero.\n");
20197a6f772SChristian Gromm return 0;
20297a6f772SChristian Gromm }
20397a6f772SChristian Gromm switch (cfg->data_type) {
20497a6f772SChristian Gromm case MOST_CH_ISOC:
20597a6f772SChristian Gromm frame_size = AV_PACKETS_PER_XACT * sub_size;
20697a6f772SChristian Gromm break;
20797a6f772SChristian Gromm case MOST_CH_SYNC:
20897a6f772SChristian Gromm if (cfg->packets_per_xact == 0) {
20997a6f772SChristian Gromm dev_warn(dev, "Misconfig: Packets per XACT zero\n");
21097a6f772SChristian Gromm frame_size = 0;
21197a6f772SChristian Gromm } else if (cfg->packets_per_xact == 0xFF) {
21297a6f772SChristian Gromm frame_size = (USB_MTU / sub_size) * sub_size;
21397a6f772SChristian Gromm } else {
21497a6f772SChristian Gromm frame_size = cfg->packets_per_xact * sub_size;
21597a6f772SChristian Gromm }
21697a6f772SChristian Gromm break;
21797a6f772SChristian Gromm default:
21897a6f772SChristian Gromm dev_warn(dev, "Query frame size of non-streaming channel\n");
21997a6f772SChristian Gromm frame_size = 0;
22097a6f772SChristian Gromm break;
22197a6f772SChristian Gromm }
22297a6f772SChristian Gromm return frame_size;
22397a6f772SChristian Gromm }
22497a6f772SChristian Gromm
22597a6f772SChristian Gromm /**
22697a6f772SChristian Gromm * hdm_poison_channel - mark buffers of this channel as invalid
22797a6f772SChristian Gromm * @iface: pointer to the interface
22897a6f772SChristian Gromm * @channel: channel ID
22997a6f772SChristian Gromm *
23097a6f772SChristian Gromm * This unlinks all URBs submitted to the HCD,
23197a6f772SChristian Gromm * calls the associated completion function of the core and removes
23297a6f772SChristian Gromm * them from the list.
23397a6f772SChristian Gromm *
23497a6f772SChristian Gromm * Returns 0 on success or error code otherwise.
23597a6f772SChristian Gromm */
hdm_poison_channel(struct most_interface * iface,int channel)23697a6f772SChristian Gromm static int hdm_poison_channel(struct most_interface *iface, int channel)
23797a6f772SChristian Gromm {
23897a6f772SChristian Gromm struct most_dev *mdev = to_mdev(iface);
23997a6f772SChristian Gromm unsigned long flags;
24097a6f772SChristian Gromm spinlock_t *lock; /* temp. lock */
24197a6f772SChristian Gromm
24297a6f772SChristian Gromm if (channel < 0 || channel >= iface->num_channels) {
24397a6f772SChristian Gromm dev_warn(&mdev->usb_device->dev, "Channel ID out of range.\n");
24497a6f772SChristian Gromm return -ECHRNG;
24597a6f772SChristian Gromm }
24697a6f772SChristian Gromm
24797a6f772SChristian Gromm lock = mdev->channel_lock + channel;
24897a6f772SChristian Gromm spin_lock_irqsave(lock, flags);
24997a6f772SChristian Gromm mdev->is_channel_healthy[channel] = false;
25097a6f772SChristian Gromm spin_unlock_irqrestore(lock, flags);
25197a6f772SChristian Gromm
25297a6f772SChristian Gromm cancel_work_sync(&mdev->clear_work[channel].ws);
25397a6f772SChristian Gromm
25497a6f772SChristian Gromm mutex_lock(&mdev->io_mutex);
25597a6f772SChristian Gromm usb_kill_anchored_urbs(&mdev->busy_urbs[channel]);
25697a6f772SChristian Gromm if (mdev->padding_active[channel])
25797a6f772SChristian Gromm mdev->padding_active[channel] = false;
25897a6f772SChristian Gromm
25997a6f772SChristian Gromm if (mdev->conf[channel].data_type == MOST_CH_ASYNC) {
26097a6f772SChristian Gromm del_timer_sync(&mdev->link_stat_timer);
26197a6f772SChristian Gromm cancel_work_sync(&mdev->poll_work_obj);
26297a6f772SChristian Gromm }
26397a6f772SChristian Gromm mutex_unlock(&mdev->io_mutex);
26497a6f772SChristian Gromm return 0;
26597a6f772SChristian Gromm }
26697a6f772SChristian Gromm
26797a6f772SChristian Gromm /**
26897a6f772SChristian Gromm * hdm_add_padding - add padding bytes
26997a6f772SChristian Gromm * @mdev: most device
27097a6f772SChristian Gromm * @channel: channel ID
27197a6f772SChristian Gromm * @mbo: buffer object
27297a6f772SChristian Gromm *
27397a6f772SChristian Gromm * This inserts the INIC hardware specific padding bytes into a streaming
27497a6f772SChristian Gromm * channel's buffer
27597a6f772SChristian Gromm */
hdm_add_padding(struct most_dev * mdev,int channel,struct mbo * mbo)27697a6f772SChristian Gromm static int hdm_add_padding(struct most_dev *mdev, int channel, struct mbo *mbo)
27797a6f772SChristian Gromm {
27897a6f772SChristian Gromm struct most_channel_config *conf = &mdev->conf[channel];
27997a6f772SChristian Gromm unsigned int frame_size = get_stream_frame_size(&mdev->dev, conf);
28097a6f772SChristian Gromm unsigned int j, num_frames;
28197a6f772SChristian Gromm
28297a6f772SChristian Gromm if (!frame_size)
28397a6f772SChristian Gromm return -EINVAL;
28497a6f772SChristian Gromm num_frames = mbo->buffer_length / frame_size;
28597a6f772SChristian Gromm
28697a6f772SChristian Gromm if (num_frames < 1) {
28797a6f772SChristian Gromm dev_err(&mdev->usb_device->dev,
28897a6f772SChristian Gromm "Missed minimal transfer unit.\n");
28997a6f772SChristian Gromm return -EINVAL;
29097a6f772SChristian Gromm }
29197a6f772SChristian Gromm
29297a6f772SChristian Gromm for (j = num_frames - 1; j > 0; j--)
29397a6f772SChristian Gromm memmove(mbo->virt_address + j * USB_MTU,
29497a6f772SChristian Gromm mbo->virt_address + j * frame_size,
29597a6f772SChristian Gromm frame_size);
29697a6f772SChristian Gromm mbo->buffer_length = num_frames * USB_MTU;
29797a6f772SChristian Gromm return 0;
29897a6f772SChristian Gromm }
29997a6f772SChristian Gromm
30097a6f772SChristian Gromm /**
30197a6f772SChristian Gromm * hdm_remove_padding - remove padding bytes
30297a6f772SChristian Gromm * @mdev: most device
30397a6f772SChristian Gromm * @channel: channel ID
30497a6f772SChristian Gromm * @mbo: buffer object
30597a6f772SChristian Gromm *
30697a6f772SChristian Gromm * This takes the INIC hardware specific padding bytes off a streaming
30797a6f772SChristian Gromm * channel's buffer.
30897a6f772SChristian Gromm */
hdm_remove_padding(struct most_dev * mdev,int channel,struct mbo * mbo)30997a6f772SChristian Gromm static int hdm_remove_padding(struct most_dev *mdev, int channel,
31097a6f772SChristian Gromm struct mbo *mbo)
31197a6f772SChristian Gromm {
31297a6f772SChristian Gromm struct most_channel_config *const conf = &mdev->conf[channel];
31397a6f772SChristian Gromm unsigned int frame_size = get_stream_frame_size(&mdev->dev, conf);
31497a6f772SChristian Gromm unsigned int j, num_frames;
31597a6f772SChristian Gromm
31697a6f772SChristian Gromm if (!frame_size)
31797a6f772SChristian Gromm return -EINVAL;
31897a6f772SChristian Gromm num_frames = mbo->processed_length / USB_MTU;
31997a6f772SChristian Gromm
32097a6f772SChristian Gromm for (j = 1; j < num_frames; j++)
32197a6f772SChristian Gromm memmove(mbo->virt_address + frame_size * j,
32297a6f772SChristian Gromm mbo->virt_address + USB_MTU * j,
32397a6f772SChristian Gromm frame_size);
32497a6f772SChristian Gromm
32597a6f772SChristian Gromm mbo->processed_length = frame_size * num_frames;
32697a6f772SChristian Gromm return 0;
32797a6f772SChristian Gromm }
32897a6f772SChristian Gromm
32997a6f772SChristian Gromm /**
33097a6f772SChristian Gromm * hdm_write_completion - completion function for submitted Tx URBs
33197a6f772SChristian Gromm * @urb: the URB that has been completed
33297a6f772SChristian Gromm *
33397a6f772SChristian Gromm * This checks the status of the completed URB. In case the URB has been
33497a6f772SChristian Gromm * unlinked before, it is immediately freed. On any other error the MBO
33597a6f772SChristian Gromm * transfer flag is set. On success it frees allocated resources and calls
33697a6f772SChristian Gromm * the completion function.
33797a6f772SChristian Gromm *
33897a6f772SChristian Gromm * Context: interrupt!
33997a6f772SChristian Gromm */
hdm_write_completion(struct urb * urb)34097a6f772SChristian Gromm static void hdm_write_completion(struct urb *urb)
34197a6f772SChristian Gromm {
34297a6f772SChristian Gromm struct mbo *mbo = urb->context;
34397a6f772SChristian Gromm struct most_dev *mdev = to_mdev(mbo->ifp);
34497a6f772SChristian Gromm unsigned int channel = mbo->hdm_channel_id;
34597a6f772SChristian Gromm spinlock_t *lock = mdev->channel_lock + channel;
34697a6f772SChristian Gromm unsigned long flags;
34797a6f772SChristian Gromm
34897a6f772SChristian Gromm spin_lock_irqsave(lock, flags);
34997a6f772SChristian Gromm
35097a6f772SChristian Gromm mbo->processed_length = 0;
35197a6f772SChristian Gromm mbo->status = MBO_E_INVAL;
35297a6f772SChristian Gromm if (likely(mdev->is_channel_healthy[channel])) {
35397a6f772SChristian Gromm switch (urb->status) {
35497a6f772SChristian Gromm case 0:
35597a6f772SChristian Gromm case -ESHUTDOWN:
35697a6f772SChristian Gromm mbo->processed_length = urb->actual_length;
35797a6f772SChristian Gromm mbo->status = MBO_SUCCESS;
35897a6f772SChristian Gromm break;
35997a6f772SChristian Gromm case -EPIPE:
36097a6f772SChristian Gromm dev_warn(&mdev->usb_device->dev,
36197a6f772SChristian Gromm "Broken pipe on ep%02x\n",
36297a6f772SChristian Gromm mdev->ep_address[channel]);
36397a6f772SChristian Gromm mdev->is_channel_healthy[channel] = false;
36497a6f772SChristian Gromm mdev->clear_work[channel].pipe = urb->pipe;
36597a6f772SChristian Gromm schedule_work(&mdev->clear_work[channel].ws);
36697a6f772SChristian Gromm break;
36797a6f772SChristian Gromm case -ENODEV:
36897a6f772SChristian Gromm case -EPROTO:
36997a6f772SChristian Gromm mbo->status = MBO_E_CLOSE;
37097a6f772SChristian Gromm break;
37197a6f772SChristian Gromm }
37297a6f772SChristian Gromm }
37397a6f772SChristian Gromm
37497a6f772SChristian Gromm spin_unlock_irqrestore(lock, flags);
37597a6f772SChristian Gromm
37697a6f772SChristian Gromm if (likely(mbo->complete))
37797a6f772SChristian Gromm mbo->complete(mbo);
37897a6f772SChristian Gromm usb_free_urb(urb);
37997a6f772SChristian Gromm }
38097a6f772SChristian Gromm
38197a6f772SChristian Gromm /**
38297a6f772SChristian Gromm * hdm_read_completion - completion function for submitted Rx URBs
38397a6f772SChristian Gromm * @urb: the URB that has been completed
38497a6f772SChristian Gromm *
38597a6f772SChristian Gromm * This checks the status of the completed URB. In case the URB has been
38697a6f772SChristian Gromm * unlinked before it is immediately freed. On any other error the MBO transfer
38797a6f772SChristian Gromm * flag is set. On success it frees allocated resources, removes
38897a6f772SChristian Gromm * padding bytes -if necessary- and calls the completion function.
38997a6f772SChristian Gromm *
39097a6f772SChristian Gromm * Context: interrupt!
39197a6f772SChristian Gromm */
hdm_read_completion(struct urb * urb)39297a6f772SChristian Gromm static void hdm_read_completion(struct urb *urb)
39397a6f772SChristian Gromm {
39497a6f772SChristian Gromm struct mbo *mbo = urb->context;
39597a6f772SChristian Gromm struct most_dev *mdev = to_mdev(mbo->ifp);
39697a6f772SChristian Gromm unsigned int channel = mbo->hdm_channel_id;
39797a6f772SChristian Gromm struct device *dev = &mdev->usb_device->dev;
39897a6f772SChristian Gromm spinlock_t *lock = mdev->channel_lock + channel;
39997a6f772SChristian Gromm unsigned long flags;
40097a6f772SChristian Gromm
40197a6f772SChristian Gromm spin_lock_irqsave(lock, flags);
40297a6f772SChristian Gromm
40397a6f772SChristian Gromm mbo->processed_length = 0;
40497a6f772SChristian Gromm mbo->status = MBO_E_INVAL;
40597a6f772SChristian Gromm if (likely(mdev->is_channel_healthy[channel])) {
40697a6f772SChristian Gromm switch (urb->status) {
40797a6f772SChristian Gromm case 0:
40897a6f772SChristian Gromm case -ESHUTDOWN:
40997a6f772SChristian Gromm mbo->processed_length = urb->actual_length;
41097a6f772SChristian Gromm mbo->status = MBO_SUCCESS;
41197a6f772SChristian Gromm if (mdev->padding_active[channel] &&
41297a6f772SChristian Gromm hdm_remove_padding(mdev, channel, mbo)) {
41397a6f772SChristian Gromm mbo->processed_length = 0;
41497a6f772SChristian Gromm mbo->status = MBO_E_INVAL;
41597a6f772SChristian Gromm }
41697a6f772SChristian Gromm break;
41797a6f772SChristian Gromm case -EPIPE:
41897a6f772SChristian Gromm dev_warn(dev, "Broken pipe on ep%02x\n",
41997a6f772SChristian Gromm mdev->ep_address[channel]);
42097a6f772SChristian Gromm mdev->is_channel_healthy[channel] = false;
42197a6f772SChristian Gromm mdev->clear_work[channel].pipe = urb->pipe;
42297a6f772SChristian Gromm schedule_work(&mdev->clear_work[channel].ws);
42397a6f772SChristian Gromm break;
42497a6f772SChristian Gromm case -ENODEV:
42597a6f772SChristian Gromm case -EPROTO:
42697a6f772SChristian Gromm mbo->status = MBO_E_CLOSE;
42797a6f772SChristian Gromm break;
42897a6f772SChristian Gromm case -EOVERFLOW:
42997a6f772SChristian Gromm dev_warn(dev, "Babble on ep%02x\n",
43097a6f772SChristian Gromm mdev->ep_address[channel]);
43197a6f772SChristian Gromm break;
43297a6f772SChristian Gromm }
43397a6f772SChristian Gromm }
43497a6f772SChristian Gromm
43597a6f772SChristian Gromm spin_unlock_irqrestore(lock, flags);
43697a6f772SChristian Gromm
43797a6f772SChristian Gromm if (likely(mbo->complete))
43897a6f772SChristian Gromm mbo->complete(mbo);
43997a6f772SChristian Gromm usb_free_urb(urb);
44097a6f772SChristian Gromm }
44197a6f772SChristian Gromm
44297a6f772SChristian Gromm /**
44397a6f772SChristian Gromm * hdm_enqueue - receive a buffer to be used for data transfer
44497a6f772SChristian Gromm * @iface: interface to enqueue to
44597a6f772SChristian Gromm * @channel: ID of the channel
44697a6f772SChristian Gromm * @mbo: pointer to the buffer object
44797a6f772SChristian Gromm *
44897a6f772SChristian Gromm * This allocates a new URB and fills it according to the channel
44997a6f772SChristian Gromm * that is being used for transmission of data. Before the URB is
45097a6f772SChristian Gromm * submitted it is stored in the private anchor list.
45197a6f772SChristian Gromm *
45297a6f772SChristian Gromm * Returns 0 on success. On any error the URB is freed and a error code
45397a6f772SChristian Gromm * is returned.
45497a6f772SChristian Gromm *
45597a6f772SChristian Gromm * Context: Could in _some_ cases be interrupt!
45697a6f772SChristian Gromm */
hdm_enqueue(struct most_interface * iface,int channel,struct mbo * mbo)45797a6f772SChristian Gromm static int hdm_enqueue(struct most_interface *iface, int channel,
45897a6f772SChristian Gromm struct mbo *mbo)
45997a6f772SChristian Gromm {
46097a6f772SChristian Gromm struct most_dev *mdev = to_mdev(iface);
46197a6f772SChristian Gromm struct most_channel_config *conf;
46297a6f772SChristian Gromm int retval = 0;
46397a6f772SChristian Gromm struct urb *urb;
46497a6f772SChristian Gromm unsigned long length;
46597a6f772SChristian Gromm void *virt_address;
46697a6f772SChristian Gromm
46797a6f772SChristian Gromm if (!mbo)
46897a6f772SChristian Gromm return -EINVAL;
46997a6f772SChristian Gromm if (iface->num_channels <= channel || channel < 0)
47097a6f772SChristian Gromm return -ECHRNG;
47197a6f772SChristian Gromm
47297a6f772SChristian Gromm urb = usb_alloc_urb(NO_ISOCHRONOUS_URB, GFP_KERNEL);
47397a6f772SChristian Gromm if (!urb)
47497a6f772SChristian Gromm return -ENOMEM;
47597a6f772SChristian Gromm
47697a6f772SChristian Gromm conf = &mdev->conf[channel];
47797a6f772SChristian Gromm
47897a6f772SChristian Gromm mutex_lock(&mdev->io_mutex);
47997a6f772SChristian Gromm if (!mdev->usb_device) {
48097a6f772SChristian Gromm retval = -ENODEV;
48197a6f772SChristian Gromm goto err_free_urb;
48297a6f772SChristian Gromm }
48397a6f772SChristian Gromm
48497a6f772SChristian Gromm if ((conf->direction & MOST_CH_TX) && mdev->padding_active[channel] &&
48597a6f772SChristian Gromm hdm_add_padding(mdev, channel, mbo)) {
48697a6f772SChristian Gromm retval = -EINVAL;
48797a6f772SChristian Gromm goto err_free_urb;
48897a6f772SChristian Gromm }
48997a6f772SChristian Gromm
49097a6f772SChristian Gromm urb->transfer_dma = mbo->bus_address;
49197a6f772SChristian Gromm virt_address = mbo->virt_address;
49297a6f772SChristian Gromm length = mbo->buffer_length;
49397a6f772SChristian Gromm
49497a6f772SChristian Gromm if (conf->direction & MOST_CH_TX) {
49597a6f772SChristian Gromm usb_fill_bulk_urb(urb, mdev->usb_device,
49697a6f772SChristian Gromm usb_sndbulkpipe(mdev->usb_device,
49797a6f772SChristian Gromm mdev->ep_address[channel]),
49897a6f772SChristian Gromm virt_address,
49997a6f772SChristian Gromm length,
50097a6f772SChristian Gromm hdm_write_completion,
50197a6f772SChristian Gromm mbo);
50297a6f772SChristian Gromm if (conf->data_type != MOST_CH_ISOC &&
50397a6f772SChristian Gromm conf->data_type != MOST_CH_SYNC)
50497a6f772SChristian Gromm urb->transfer_flags |= URB_ZERO_PACKET;
50597a6f772SChristian Gromm } else {
50697a6f772SChristian Gromm usb_fill_bulk_urb(urb, mdev->usb_device,
50797a6f772SChristian Gromm usb_rcvbulkpipe(mdev->usb_device,
50897a6f772SChristian Gromm mdev->ep_address[channel]),
50997a6f772SChristian Gromm virt_address,
51097a6f772SChristian Gromm length + conf->extra_len,
51197a6f772SChristian Gromm hdm_read_completion,
51297a6f772SChristian Gromm mbo);
51397a6f772SChristian Gromm }
51497a6f772SChristian Gromm urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
51597a6f772SChristian Gromm
51697a6f772SChristian Gromm usb_anchor_urb(urb, &mdev->busy_urbs[channel]);
51797a6f772SChristian Gromm
51897a6f772SChristian Gromm retval = usb_submit_urb(urb, GFP_KERNEL);
51997a6f772SChristian Gromm if (retval) {
52097a6f772SChristian Gromm dev_err(&mdev->usb_device->dev,
52197a6f772SChristian Gromm "URB submit failed with error %d.\n", retval);
52297a6f772SChristian Gromm goto err_unanchor_urb;
52397a6f772SChristian Gromm }
52497a6f772SChristian Gromm mutex_unlock(&mdev->io_mutex);
52597a6f772SChristian Gromm return 0;
52697a6f772SChristian Gromm
52797a6f772SChristian Gromm err_unanchor_urb:
52897a6f772SChristian Gromm usb_unanchor_urb(urb);
52997a6f772SChristian Gromm err_free_urb:
53097a6f772SChristian Gromm usb_free_urb(urb);
53197a6f772SChristian Gromm mutex_unlock(&mdev->io_mutex);
53297a6f772SChristian Gromm return retval;
53397a6f772SChristian Gromm }
53497a6f772SChristian Gromm
hdm_dma_alloc(struct mbo * mbo,u32 size)53597a6f772SChristian Gromm static void *hdm_dma_alloc(struct mbo *mbo, u32 size)
53697a6f772SChristian Gromm {
53797a6f772SChristian Gromm struct most_dev *mdev = to_mdev(mbo->ifp);
53897a6f772SChristian Gromm
53997a6f772SChristian Gromm return usb_alloc_coherent(mdev->usb_device, size, GFP_KERNEL,
54097a6f772SChristian Gromm &mbo->bus_address);
54197a6f772SChristian Gromm }
54297a6f772SChristian Gromm
hdm_dma_free(struct mbo * mbo,u32 size)54397a6f772SChristian Gromm static void hdm_dma_free(struct mbo *mbo, u32 size)
54497a6f772SChristian Gromm {
54597a6f772SChristian Gromm struct most_dev *mdev = to_mdev(mbo->ifp);
54697a6f772SChristian Gromm
54797a6f772SChristian Gromm usb_free_coherent(mdev->usb_device, size, mbo->virt_address,
54897a6f772SChristian Gromm mbo->bus_address);
54997a6f772SChristian Gromm }
55097a6f772SChristian Gromm
55197a6f772SChristian Gromm /**
55297a6f772SChristian Gromm * hdm_configure_channel - receive channel configuration from core
55397a6f772SChristian Gromm * @iface: interface
55497a6f772SChristian Gromm * @channel: channel ID
55597a6f772SChristian Gromm * @conf: structure that holds the configuration information
55697a6f772SChristian Gromm *
55797a6f772SChristian Gromm * The attached network interface controller (NIC) supports a padding mode
55897a6f772SChristian Gromm * to avoid short packets on USB, hence increasing the performance due to a
55997a6f772SChristian Gromm * lower interrupt load. This mode is default for synchronous data and can
56097a6f772SChristian Gromm * be switched on for isochronous data. In case padding is active the
56197a6f772SChristian Gromm * driver needs to know the frame size of the payload in order to calculate
56297a6f772SChristian Gromm * the number of bytes it needs to pad when transmitting or to cut off when
56397a6f772SChristian Gromm * receiving data.
56497a6f772SChristian Gromm *
56597a6f772SChristian Gromm */
hdm_configure_channel(struct most_interface * iface,int channel,struct most_channel_config * conf)56697a6f772SChristian Gromm static int hdm_configure_channel(struct most_interface *iface, int channel,
56797a6f772SChristian Gromm struct most_channel_config *conf)
56897a6f772SChristian Gromm {
56997a6f772SChristian Gromm unsigned int num_frames;
57097a6f772SChristian Gromm unsigned int frame_size;
57197a6f772SChristian Gromm struct most_dev *mdev = to_mdev(iface);
57297a6f772SChristian Gromm struct device *dev = &mdev->usb_device->dev;
57397a6f772SChristian Gromm
57497a6f772SChristian Gromm if (!conf) {
57597a6f772SChristian Gromm dev_err(dev, "Bad config pointer.\n");
57697a6f772SChristian Gromm return -EINVAL;
57797a6f772SChristian Gromm }
57897a6f772SChristian Gromm if (channel < 0 || channel >= iface->num_channels) {
57997a6f772SChristian Gromm dev_err(dev, "Channel ID out of range.\n");
58097a6f772SChristian Gromm return -EINVAL;
58197a6f772SChristian Gromm }
58297a6f772SChristian Gromm
58397a6f772SChristian Gromm mdev->is_channel_healthy[channel] = true;
58497a6f772SChristian Gromm mdev->clear_work[channel].channel = channel;
58597a6f772SChristian Gromm mdev->clear_work[channel].mdev = mdev;
58697a6f772SChristian Gromm INIT_WORK(&mdev->clear_work[channel].ws, wq_clear_halt);
58797a6f772SChristian Gromm
58897a6f772SChristian Gromm if (!conf->num_buffers || !conf->buffer_size) {
58997a6f772SChristian Gromm dev_err(dev, "Misconfig: buffer size or #buffers zero.\n");
59097a6f772SChristian Gromm return -EINVAL;
59197a6f772SChristian Gromm }
59297a6f772SChristian Gromm
59397a6f772SChristian Gromm if (conf->data_type != MOST_CH_SYNC &&
59497a6f772SChristian Gromm !(conf->data_type == MOST_CH_ISOC &&
59597a6f772SChristian Gromm conf->packets_per_xact != 0xFF)) {
59697a6f772SChristian Gromm mdev->padding_active[channel] = false;
59797a6f772SChristian Gromm /*
59897a6f772SChristian Gromm * Since the NIC's padding mode is not going to be
59997a6f772SChristian Gromm * used, we can skip the frame size calculations and
60097a6f772SChristian Gromm * move directly on to exit.
60197a6f772SChristian Gromm */
60297a6f772SChristian Gromm goto exit;
60397a6f772SChristian Gromm }
60497a6f772SChristian Gromm
60597a6f772SChristian Gromm mdev->padding_active[channel] = true;
60697a6f772SChristian Gromm
60797a6f772SChristian Gromm frame_size = get_stream_frame_size(&mdev->dev, conf);
60897a6f772SChristian Gromm if (frame_size == 0 || frame_size > USB_MTU) {
60997a6f772SChristian Gromm dev_warn(dev, "Misconfig: frame size wrong\n");
61097a6f772SChristian Gromm return -EINVAL;
61197a6f772SChristian Gromm }
61297a6f772SChristian Gromm
61397a6f772SChristian Gromm num_frames = conf->buffer_size / frame_size;
61497a6f772SChristian Gromm
61597a6f772SChristian Gromm if (conf->buffer_size % frame_size) {
61697a6f772SChristian Gromm u16 old_size = conf->buffer_size;
61797a6f772SChristian Gromm
61897a6f772SChristian Gromm conf->buffer_size = num_frames * frame_size;
61997a6f772SChristian Gromm dev_warn(dev, "%s: fixed buffer size (%d -> %d)\n",
62097a6f772SChristian Gromm mdev->suffix[channel], old_size, conf->buffer_size);
62197a6f772SChristian Gromm }
62297a6f772SChristian Gromm
62397a6f772SChristian Gromm /* calculate extra length to comply w/ HW padding */
62497a6f772SChristian Gromm conf->extra_len = num_frames * (USB_MTU - frame_size);
62597a6f772SChristian Gromm
62697a6f772SChristian Gromm exit:
62797a6f772SChristian Gromm mdev->conf[channel] = *conf;
62897a6f772SChristian Gromm if (conf->data_type == MOST_CH_ASYNC) {
62997a6f772SChristian Gromm u16 ep = mdev->ep_address[channel];
63097a6f772SChristian Gromm
63197a6f772SChristian Gromm if (start_sync_ep(mdev->usb_device, ep) < 0)
63297a6f772SChristian Gromm dev_warn(dev, "sync for ep%02x failed", ep);
63397a6f772SChristian Gromm }
63497a6f772SChristian Gromm return 0;
63597a6f772SChristian Gromm }
63697a6f772SChristian Gromm
63797a6f772SChristian Gromm /**
63897a6f772SChristian Gromm * hdm_request_netinfo - request network information
63997a6f772SChristian Gromm * @iface: pointer to interface
64097a6f772SChristian Gromm * @channel: channel ID
64197a6f772SChristian Gromm *
64297a6f772SChristian Gromm * This is used as trigger to set up the link status timer that
64397a6f772SChristian Gromm * polls for the NI state of the INIC every 2 seconds.
64497a6f772SChristian Gromm *
64597a6f772SChristian Gromm */
hdm_request_netinfo(struct most_interface * iface,int channel,void (* on_netinfo)(struct most_interface *,unsigned char,unsigned char *))64697a6f772SChristian Gromm static void hdm_request_netinfo(struct most_interface *iface, int channel,
64797a6f772SChristian Gromm void (*on_netinfo)(struct most_interface *,
64897a6f772SChristian Gromm unsigned char,
64997a6f772SChristian Gromm unsigned char *))
65097a6f772SChristian Gromm {
65197a6f772SChristian Gromm struct most_dev *mdev = to_mdev(iface);
65297a6f772SChristian Gromm
65397a6f772SChristian Gromm mdev->on_netinfo = on_netinfo;
65497a6f772SChristian Gromm if (!on_netinfo)
65597a6f772SChristian Gromm return;
65697a6f772SChristian Gromm
65797a6f772SChristian Gromm mdev->link_stat_timer.expires = jiffies + HZ;
65897a6f772SChristian Gromm mod_timer(&mdev->link_stat_timer, mdev->link_stat_timer.expires);
65997a6f772SChristian Gromm }
66097a6f772SChristian Gromm
66197a6f772SChristian Gromm /**
66297a6f772SChristian Gromm * link_stat_timer_handler - schedule work obtaining mac address and link status
663*fba3993eSRandy Dunlap * @t: pointer to timer_list which holds a pointer to the USB device instance
66497a6f772SChristian Gromm *
66597a6f772SChristian Gromm * The handler runs in interrupt context. That's why we need to defer the
66697a6f772SChristian Gromm * tasks to a work queue.
66797a6f772SChristian Gromm */
link_stat_timer_handler(struct timer_list * t)66897a6f772SChristian Gromm static void link_stat_timer_handler(struct timer_list *t)
66997a6f772SChristian Gromm {
67097a6f772SChristian Gromm struct most_dev *mdev = from_timer(mdev, t, link_stat_timer);
67197a6f772SChristian Gromm
67297a6f772SChristian Gromm schedule_work(&mdev->poll_work_obj);
67397a6f772SChristian Gromm mdev->link_stat_timer.expires = jiffies + (2 * HZ);
67497a6f772SChristian Gromm add_timer(&mdev->link_stat_timer);
67597a6f772SChristian Gromm }
67697a6f772SChristian Gromm
67797a6f772SChristian Gromm /**
67897a6f772SChristian Gromm * wq_netinfo - work queue function to deliver latest networking information
67997a6f772SChristian Gromm * @wq_obj: object that holds data for our deferred work to do
68097a6f772SChristian Gromm *
68197a6f772SChristian Gromm * This retrieves the network interface status of the USB INIC
68297a6f772SChristian Gromm */
wq_netinfo(struct work_struct * wq_obj)68397a6f772SChristian Gromm static void wq_netinfo(struct work_struct *wq_obj)
68497a6f772SChristian Gromm {
68597a6f772SChristian Gromm struct most_dev *mdev = to_mdev_from_work(wq_obj);
68697a6f772SChristian Gromm struct usb_device *usb_device = mdev->usb_device;
68797a6f772SChristian Gromm struct device *dev = &usb_device->dev;
68897a6f772SChristian Gromm u16 hi, mi, lo, link;
68997a6f772SChristian Gromm u8 hw_addr[6];
69097a6f772SChristian Gromm
69197a6f772SChristian Gromm if (drci_rd_reg(usb_device, DRCI_REG_HW_ADDR_HI, &hi)) {
69297a6f772SChristian Gromm dev_err(dev, "Vendor request 'hw_addr_hi' failed\n");
69397a6f772SChristian Gromm return;
69497a6f772SChristian Gromm }
69597a6f772SChristian Gromm
69697a6f772SChristian Gromm if (drci_rd_reg(usb_device, DRCI_REG_HW_ADDR_MI, &mi)) {
69797a6f772SChristian Gromm dev_err(dev, "Vendor request 'hw_addr_mid' failed\n");
69897a6f772SChristian Gromm return;
69997a6f772SChristian Gromm }
70097a6f772SChristian Gromm
70197a6f772SChristian Gromm if (drci_rd_reg(usb_device, DRCI_REG_HW_ADDR_LO, &lo)) {
70297a6f772SChristian Gromm dev_err(dev, "Vendor request 'hw_addr_low' failed\n");
70397a6f772SChristian Gromm return;
70497a6f772SChristian Gromm }
70597a6f772SChristian Gromm
70697a6f772SChristian Gromm if (drci_rd_reg(usb_device, DRCI_REG_NI_STATE, &link)) {
70797a6f772SChristian Gromm dev_err(dev, "Vendor request 'link status' failed\n");
70897a6f772SChristian Gromm return;
70997a6f772SChristian Gromm }
71097a6f772SChristian Gromm
71197a6f772SChristian Gromm hw_addr[0] = hi >> 8;
71297a6f772SChristian Gromm hw_addr[1] = hi;
71397a6f772SChristian Gromm hw_addr[2] = mi >> 8;
71497a6f772SChristian Gromm hw_addr[3] = mi;
71597a6f772SChristian Gromm hw_addr[4] = lo >> 8;
71697a6f772SChristian Gromm hw_addr[5] = lo;
71797a6f772SChristian Gromm
71897a6f772SChristian Gromm if (mdev->on_netinfo)
71997a6f772SChristian Gromm mdev->on_netinfo(&mdev->iface, link, hw_addr);
72097a6f772SChristian Gromm }
72197a6f772SChristian Gromm
72297a6f772SChristian Gromm /**
72397a6f772SChristian Gromm * wq_clear_halt - work queue function
72497a6f772SChristian Gromm * @wq_obj: work_struct object to execute
72597a6f772SChristian Gromm *
72697a6f772SChristian Gromm * This sends a clear_halt to the given USB pipe.
72797a6f772SChristian Gromm */
wq_clear_halt(struct work_struct * wq_obj)72897a6f772SChristian Gromm static void wq_clear_halt(struct work_struct *wq_obj)
72997a6f772SChristian Gromm {
73097a6f772SChristian Gromm struct clear_hold_work *clear_work = to_clear_hold_work(wq_obj);
73197a6f772SChristian Gromm struct most_dev *mdev = clear_work->mdev;
73297a6f772SChristian Gromm unsigned int channel = clear_work->channel;
73397a6f772SChristian Gromm int pipe = clear_work->pipe;
73497a6f772SChristian Gromm int snd_pipe;
73597a6f772SChristian Gromm int peer;
73697a6f772SChristian Gromm
73797a6f772SChristian Gromm mutex_lock(&mdev->io_mutex);
73897a6f772SChristian Gromm most_stop_enqueue(&mdev->iface, channel);
73997a6f772SChristian Gromm usb_kill_anchored_urbs(&mdev->busy_urbs[channel]);
74097a6f772SChristian Gromm if (usb_clear_halt(mdev->usb_device, pipe))
74197a6f772SChristian Gromm dev_warn(&mdev->usb_device->dev, "Failed to reset endpoint.\n");
74297a6f772SChristian Gromm
74397a6f772SChristian Gromm /* If the functional Stall condition has been set on an
74497a6f772SChristian Gromm * asynchronous rx channel, we need to clear the tx channel
74597a6f772SChristian Gromm * too, since the hardware runs its clean-up sequence on both
74697a6f772SChristian Gromm * channels, as they are physically one on the network.
74797a6f772SChristian Gromm *
74897a6f772SChristian Gromm * The USB interface that exposes the asynchronous channels
74997a6f772SChristian Gromm * contains always two endpoints, and two only.
75097a6f772SChristian Gromm */
75197a6f772SChristian Gromm if (mdev->conf[channel].data_type == MOST_CH_ASYNC &&
75297a6f772SChristian Gromm mdev->conf[channel].direction == MOST_CH_RX) {
75397a6f772SChristian Gromm if (channel == 0)
75497a6f772SChristian Gromm peer = 1;
75597a6f772SChristian Gromm else
75697a6f772SChristian Gromm peer = 0;
75797a6f772SChristian Gromm snd_pipe = usb_sndbulkpipe(mdev->usb_device,
75897a6f772SChristian Gromm mdev->ep_address[peer]);
75997a6f772SChristian Gromm usb_clear_halt(mdev->usb_device, snd_pipe);
76097a6f772SChristian Gromm }
76197a6f772SChristian Gromm mdev->is_channel_healthy[channel] = true;
76297a6f772SChristian Gromm most_resume_enqueue(&mdev->iface, channel);
76397a6f772SChristian Gromm mutex_unlock(&mdev->io_mutex);
76497a6f772SChristian Gromm }
76597a6f772SChristian Gromm
766*fba3993eSRandy Dunlap /*
76797a6f772SChristian Gromm * hdm_usb_fops - file operation table for USB driver
76897a6f772SChristian Gromm */
76997a6f772SChristian Gromm static const struct file_operations hdm_usb_fops = {
77097a6f772SChristian Gromm .owner = THIS_MODULE,
77197a6f772SChristian Gromm };
77297a6f772SChristian Gromm
773*fba3993eSRandy Dunlap /*
77497a6f772SChristian Gromm * usb_device_id - ID table for HCD device probing
77597a6f772SChristian Gromm */
77697a6f772SChristian Gromm static const struct usb_device_id usbid[] = {
77797a6f772SChristian Gromm { USB_DEVICE(USB_VENDOR_ID_SMSC, USB_DEV_ID_BRDG), },
77897a6f772SChristian Gromm { USB_DEVICE(USB_VENDOR_ID_SMSC, USB_DEV_ID_OS81118), },
77997a6f772SChristian Gromm { USB_DEVICE(USB_VENDOR_ID_SMSC, USB_DEV_ID_OS81119), },
78097a6f772SChristian Gromm { USB_DEVICE(USB_VENDOR_ID_SMSC, USB_DEV_ID_OS81210), },
78197a6f772SChristian Gromm { } /* Terminating entry */
78297a6f772SChristian Gromm };
78397a6f772SChristian Gromm
78497a6f772SChristian Gromm struct regs {
78597a6f772SChristian Gromm const char *name;
78697a6f772SChristian Gromm u16 reg;
78797a6f772SChristian Gromm };
78897a6f772SChristian Gromm
78997a6f772SChristian Gromm static const struct regs ro_regs[] = {
79097a6f772SChristian Gromm { "ni_state", DRCI_REG_NI_STATE },
79197a6f772SChristian Gromm { "packet_bandwidth", DRCI_REG_PACKET_BW },
79297a6f772SChristian Gromm { "node_address", DRCI_REG_NODE_ADDR },
79397a6f772SChristian Gromm { "node_position", DRCI_REG_NODE_POS },
79497a6f772SChristian Gromm };
79597a6f772SChristian Gromm
79697a6f772SChristian Gromm static const struct regs rw_regs[] = {
79797a6f772SChristian Gromm { "mep_filter", DRCI_REG_MEP_FILTER },
79897a6f772SChristian Gromm { "mep_hash0", DRCI_REG_HASH_TBL0 },
79997a6f772SChristian Gromm { "mep_hash1", DRCI_REG_HASH_TBL1 },
80097a6f772SChristian Gromm { "mep_hash2", DRCI_REG_HASH_TBL2 },
80197a6f772SChristian Gromm { "mep_hash3", DRCI_REG_HASH_TBL3 },
80297a6f772SChristian Gromm { "mep_eui48_hi", DRCI_REG_HW_ADDR_HI },
80397a6f772SChristian Gromm { "mep_eui48_mi", DRCI_REG_HW_ADDR_MI },
80497a6f772SChristian Gromm { "mep_eui48_lo", DRCI_REG_HW_ADDR_LO },
80597a6f772SChristian Gromm };
80697a6f772SChristian Gromm
get_stat_reg_addr(const struct regs * regs,int size,const char * name,u16 * reg_addr)80797a6f772SChristian Gromm static int get_stat_reg_addr(const struct regs *regs, int size,
80897a6f772SChristian Gromm const char *name, u16 *reg_addr)
80997a6f772SChristian Gromm {
81097a6f772SChristian Gromm int i;
81197a6f772SChristian Gromm
81297a6f772SChristian Gromm for (i = 0; i < size; i++) {
81397a6f772SChristian Gromm if (sysfs_streq(name, regs[i].name)) {
81497a6f772SChristian Gromm *reg_addr = regs[i].reg;
81597a6f772SChristian Gromm return 0;
81697a6f772SChristian Gromm }
81797a6f772SChristian Gromm }
81897a6f772SChristian Gromm return -EINVAL;
81997a6f772SChristian Gromm }
82097a6f772SChristian Gromm
82197a6f772SChristian Gromm #define get_static_reg_addr(regs, name, reg_addr) \
82297a6f772SChristian Gromm get_stat_reg_addr(regs, ARRAY_SIZE(regs), name, reg_addr)
82397a6f772SChristian Gromm
value_show(struct device * dev,struct device_attribute * attr,char * buf)82497a6f772SChristian Gromm static ssize_t value_show(struct device *dev, struct device_attribute *attr,
82597a6f772SChristian Gromm char *buf)
82697a6f772SChristian Gromm {
82797a6f772SChristian Gromm const char *name = attr->attr.name;
82897a6f772SChristian Gromm struct most_dci_obj *dci_obj = to_dci_obj(dev);
82997a6f772SChristian Gromm u16 val;
83097a6f772SChristian Gromm u16 reg_addr;
83197a6f772SChristian Gromm int err;
83297a6f772SChristian Gromm
83397a6f772SChristian Gromm if (sysfs_streq(name, "arb_address"))
834cd455ebbSJing Yao return sysfs_emit(buf, "%04x\n", dci_obj->reg_addr);
83597a6f772SChristian Gromm
83697a6f772SChristian Gromm if (sysfs_streq(name, "arb_value"))
83797a6f772SChristian Gromm reg_addr = dci_obj->reg_addr;
83897a6f772SChristian Gromm else if (get_static_reg_addr(ro_regs, name, ®_addr) &&
83997a6f772SChristian Gromm get_static_reg_addr(rw_regs, name, ®_addr))
84097a6f772SChristian Gromm return -EINVAL;
84197a6f772SChristian Gromm
84297a6f772SChristian Gromm err = drci_rd_reg(dci_obj->usb_device, reg_addr, &val);
84397a6f772SChristian Gromm if (err < 0)
84497a6f772SChristian Gromm return err;
84597a6f772SChristian Gromm
846cd455ebbSJing Yao return sysfs_emit(buf, "%04x\n", val);
84797a6f772SChristian Gromm }
84897a6f772SChristian Gromm
value_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)84997a6f772SChristian Gromm static ssize_t value_store(struct device *dev, struct device_attribute *attr,
85097a6f772SChristian Gromm const char *buf, size_t count)
85197a6f772SChristian Gromm {
85297a6f772SChristian Gromm u16 val;
85397a6f772SChristian Gromm u16 reg_addr;
85497a6f772SChristian Gromm const char *name = attr->attr.name;
85597a6f772SChristian Gromm struct most_dci_obj *dci_obj = to_dci_obj(dev);
85697a6f772SChristian Gromm struct usb_device *usb_dev = dci_obj->usb_device;
85797a6f772SChristian Gromm int err;
85897a6f772SChristian Gromm
85997a6f772SChristian Gromm err = kstrtou16(buf, 16, &val);
86097a6f772SChristian Gromm if (err)
86197a6f772SChristian Gromm return err;
86297a6f772SChristian Gromm
86397a6f772SChristian Gromm if (sysfs_streq(name, "arb_address")) {
86497a6f772SChristian Gromm dci_obj->reg_addr = val;
86597a6f772SChristian Gromm return count;
86697a6f772SChristian Gromm }
86797a6f772SChristian Gromm
86897a6f772SChristian Gromm if (sysfs_streq(name, "arb_value"))
86997a6f772SChristian Gromm err = drci_wr_reg(usb_dev, dci_obj->reg_addr, val);
87097a6f772SChristian Gromm else if (sysfs_streq(name, "sync_ep"))
87197a6f772SChristian Gromm err = start_sync_ep(usb_dev, val);
87297a6f772SChristian Gromm else if (!get_static_reg_addr(rw_regs, name, ®_addr))
87397a6f772SChristian Gromm err = drci_wr_reg(usb_dev, reg_addr, val);
87497a6f772SChristian Gromm else
87597a6f772SChristian Gromm return -EINVAL;
87697a6f772SChristian Gromm
87797a6f772SChristian Gromm if (err < 0)
87897a6f772SChristian Gromm return err;
87997a6f772SChristian Gromm
88097a6f772SChristian Gromm return count;
88197a6f772SChristian Gromm }
88297a6f772SChristian Gromm
88397a6f772SChristian Gromm static DEVICE_ATTR(ni_state, 0444, value_show, NULL);
88497a6f772SChristian Gromm static DEVICE_ATTR(packet_bandwidth, 0444, value_show, NULL);
88597a6f772SChristian Gromm static DEVICE_ATTR(node_address, 0444, value_show, NULL);
88697a6f772SChristian Gromm static DEVICE_ATTR(node_position, 0444, value_show, NULL);
88797a6f772SChristian Gromm static DEVICE_ATTR(sync_ep, 0200, NULL, value_store);
88897a6f772SChristian Gromm static DEVICE_ATTR(mep_filter, 0644, value_show, value_store);
88997a6f772SChristian Gromm static DEVICE_ATTR(mep_hash0, 0644, value_show, value_store);
89097a6f772SChristian Gromm static DEVICE_ATTR(mep_hash1, 0644, value_show, value_store);
89197a6f772SChristian Gromm static DEVICE_ATTR(mep_hash2, 0644, value_show, value_store);
89297a6f772SChristian Gromm static DEVICE_ATTR(mep_hash3, 0644, value_show, value_store);
89397a6f772SChristian Gromm static DEVICE_ATTR(mep_eui48_hi, 0644, value_show, value_store);
89497a6f772SChristian Gromm static DEVICE_ATTR(mep_eui48_mi, 0644, value_show, value_store);
89597a6f772SChristian Gromm static DEVICE_ATTR(mep_eui48_lo, 0644, value_show, value_store);
89697a6f772SChristian Gromm static DEVICE_ATTR(arb_address, 0644, value_show, value_store);
89797a6f772SChristian Gromm static DEVICE_ATTR(arb_value, 0644, value_show, value_store);
89897a6f772SChristian Gromm
89997a6f772SChristian Gromm static struct attribute *dci_attrs[] = {
90097a6f772SChristian Gromm &dev_attr_ni_state.attr,
90197a6f772SChristian Gromm &dev_attr_packet_bandwidth.attr,
90297a6f772SChristian Gromm &dev_attr_node_address.attr,
90397a6f772SChristian Gromm &dev_attr_node_position.attr,
90497a6f772SChristian Gromm &dev_attr_sync_ep.attr,
90597a6f772SChristian Gromm &dev_attr_mep_filter.attr,
90697a6f772SChristian Gromm &dev_attr_mep_hash0.attr,
90797a6f772SChristian Gromm &dev_attr_mep_hash1.attr,
90897a6f772SChristian Gromm &dev_attr_mep_hash2.attr,
90997a6f772SChristian Gromm &dev_attr_mep_hash3.attr,
91097a6f772SChristian Gromm &dev_attr_mep_eui48_hi.attr,
91197a6f772SChristian Gromm &dev_attr_mep_eui48_mi.attr,
91297a6f772SChristian Gromm &dev_attr_mep_eui48_lo.attr,
91397a6f772SChristian Gromm &dev_attr_arb_address.attr,
91497a6f772SChristian Gromm &dev_attr_arb_value.attr,
91597a6f772SChristian Gromm NULL,
91697a6f772SChristian Gromm };
91797a6f772SChristian Gromm
91897a6f772SChristian Gromm ATTRIBUTE_GROUPS(dci);
91997a6f772SChristian Gromm
release_dci(struct device * dev)92097a6f772SChristian Gromm static void release_dci(struct device *dev)
92197a6f772SChristian Gromm {
92297a6f772SChristian Gromm struct most_dci_obj *dci = to_dci_obj(dev);
92397a6f772SChristian Gromm
92497a6f772SChristian Gromm put_device(dev->parent);
92597a6f772SChristian Gromm kfree(dci);
92697a6f772SChristian Gromm }
92797a6f772SChristian Gromm
release_mdev(struct device * dev)92897a6f772SChristian Gromm static void release_mdev(struct device *dev)
92997a6f772SChristian Gromm {
93097a6f772SChristian Gromm struct most_dev *mdev = to_mdev_from_dev(dev);
93197a6f772SChristian Gromm
93297a6f772SChristian Gromm kfree(mdev);
93397a6f772SChristian Gromm }
93497a6f772SChristian Gromm /**
93597a6f772SChristian Gromm * hdm_probe - probe function of USB device driver
93697a6f772SChristian Gromm * @interface: Interface of the attached USB device
93797a6f772SChristian Gromm * @id: Pointer to the USB ID table.
93897a6f772SChristian Gromm *
93997a6f772SChristian Gromm * This allocates and initializes the device instance, adds the new
94097a6f772SChristian Gromm * entry to the internal list, scans the USB descriptors and registers
94197a6f772SChristian Gromm * the interface with the core.
94297a6f772SChristian Gromm * Additionally, the DCI objects are created and the hardware is sync'd.
94397a6f772SChristian Gromm *
94497a6f772SChristian Gromm * Return 0 on success. In case of an error a negative number is returned.
94597a6f772SChristian Gromm */
94697a6f772SChristian Gromm static int
hdm_probe(struct usb_interface * interface,const struct usb_device_id * id)94797a6f772SChristian Gromm hdm_probe(struct usb_interface *interface, const struct usb_device_id *id)
94897a6f772SChristian Gromm {
94997a6f772SChristian Gromm struct usb_host_interface *usb_iface_desc = interface->cur_altsetting;
95097a6f772SChristian Gromm struct usb_device *usb_dev = interface_to_usbdev(interface);
95197a6f772SChristian Gromm struct device *dev = &usb_dev->dev;
95297a6f772SChristian Gromm struct most_dev *mdev;
95397a6f772SChristian Gromm unsigned int i;
95497a6f772SChristian Gromm unsigned int num_endpoints;
95597a6f772SChristian Gromm struct most_channel_capability *tmp_cap;
95697a6f772SChristian Gromm struct usb_endpoint_descriptor *ep_desc;
95797a6f772SChristian Gromm int ret = -ENOMEM;
95897a6f772SChristian Gromm
95997a6f772SChristian Gromm mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
96097a6f772SChristian Gromm if (!mdev)
96197a6f772SChristian Gromm return -ENOMEM;
96297a6f772SChristian Gromm
96397a6f772SChristian Gromm usb_set_intfdata(interface, mdev);
96497a6f772SChristian Gromm num_endpoints = usb_iface_desc->desc.bNumEndpoints;
96597a6f772SChristian Gromm if (num_endpoints > MAX_NUM_ENDPOINTS) {
96697a6f772SChristian Gromm kfree(mdev);
96797a6f772SChristian Gromm return -EINVAL;
96897a6f772SChristian Gromm }
96997a6f772SChristian Gromm mutex_init(&mdev->io_mutex);
97097a6f772SChristian Gromm INIT_WORK(&mdev->poll_work_obj, wq_netinfo);
97197a6f772SChristian Gromm timer_setup(&mdev->link_stat_timer, link_stat_timer_handler, 0);
97297a6f772SChristian Gromm
97397a6f772SChristian Gromm mdev->usb_device = usb_dev;
97497a6f772SChristian Gromm mdev->link_stat_timer.expires = jiffies + (2 * HZ);
97597a6f772SChristian Gromm
97697a6f772SChristian Gromm mdev->iface.mod = hdm_usb_fops.owner;
97797a6f772SChristian Gromm mdev->iface.dev = &mdev->dev;
97897a6f772SChristian Gromm mdev->iface.driver_dev = &interface->dev;
97997a6f772SChristian Gromm mdev->iface.interface = ITYPE_USB;
98097a6f772SChristian Gromm mdev->iface.configure = hdm_configure_channel;
98197a6f772SChristian Gromm mdev->iface.request_netinfo = hdm_request_netinfo;
98297a6f772SChristian Gromm mdev->iface.enqueue = hdm_enqueue;
98397a6f772SChristian Gromm mdev->iface.poison_channel = hdm_poison_channel;
98497a6f772SChristian Gromm mdev->iface.dma_alloc = hdm_dma_alloc;
98597a6f772SChristian Gromm mdev->iface.dma_free = hdm_dma_free;
98697a6f772SChristian Gromm mdev->iface.description = mdev->description;
98797a6f772SChristian Gromm mdev->iface.num_channels = num_endpoints;
98897a6f772SChristian Gromm
98997a6f772SChristian Gromm snprintf(mdev->description, sizeof(mdev->description),
99097a6f772SChristian Gromm "%d-%s:%d.%d",
99197a6f772SChristian Gromm usb_dev->bus->busnum,
99297a6f772SChristian Gromm usb_dev->devpath,
99397a6f772SChristian Gromm usb_dev->config->desc.bConfigurationValue,
99497a6f772SChristian Gromm usb_iface_desc->desc.bInterfaceNumber);
99597a6f772SChristian Gromm
99697a6f772SChristian Gromm mdev->dev.init_name = mdev->description;
99797a6f772SChristian Gromm mdev->dev.parent = &interface->dev;
99897a6f772SChristian Gromm mdev->dev.release = release_mdev;
99997a6f772SChristian Gromm mdev->conf = kcalloc(num_endpoints, sizeof(*mdev->conf), GFP_KERNEL);
100097a6f772SChristian Gromm if (!mdev->conf)
100197a6f772SChristian Gromm goto err_free_mdev;
100297a6f772SChristian Gromm
100397a6f772SChristian Gromm mdev->cap = kcalloc(num_endpoints, sizeof(*mdev->cap), GFP_KERNEL);
100497a6f772SChristian Gromm if (!mdev->cap)
100597a6f772SChristian Gromm goto err_free_conf;
100697a6f772SChristian Gromm
100797a6f772SChristian Gromm mdev->iface.channel_vector = mdev->cap;
100897a6f772SChristian Gromm mdev->ep_address =
100997a6f772SChristian Gromm kcalloc(num_endpoints, sizeof(*mdev->ep_address), GFP_KERNEL);
101097a6f772SChristian Gromm if (!mdev->ep_address)
101197a6f772SChristian Gromm goto err_free_cap;
101297a6f772SChristian Gromm
101397a6f772SChristian Gromm mdev->busy_urbs =
101497a6f772SChristian Gromm kcalloc(num_endpoints, sizeof(*mdev->busy_urbs), GFP_KERNEL);
101597a6f772SChristian Gromm if (!mdev->busy_urbs)
101697a6f772SChristian Gromm goto err_free_ep_address;
101797a6f772SChristian Gromm
101897a6f772SChristian Gromm tmp_cap = mdev->cap;
101997a6f772SChristian Gromm for (i = 0; i < num_endpoints; i++) {
102097a6f772SChristian Gromm ep_desc = &usb_iface_desc->endpoint[i].desc;
102197a6f772SChristian Gromm mdev->ep_address[i] = ep_desc->bEndpointAddress;
102297a6f772SChristian Gromm mdev->padding_active[i] = false;
102397a6f772SChristian Gromm mdev->is_channel_healthy[i] = true;
102497a6f772SChristian Gromm
102597a6f772SChristian Gromm snprintf(&mdev->suffix[i][0], MAX_SUFFIX_LEN, "ep%02x",
102697a6f772SChristian Gromm mdev->ep_address[i]);
102797a6f772SChristian Gromm
102897a6f772SChristian Gromm tmp_cap->name_suffix = &mdev->suffix[i][0];
102997a6f772SChristian Gromm tmp_cap->buffer_size_packet = MAX_BUF_SIZE;
103097a6f772SChristian Gromm tmp_cap->buffer_size_streaming = MAX_BUF_SIZE;
103197a6f772SChristian Gromm tmp_cap->num_buffers_packet = BUF_CHAIN_SIZE;
103297a6f772SChristian Gromm tmp_cap->num_buffers_streaming = BUF_CHAIN_SIZE;
103397a6f772SChristian Gromm tmp_cap->data_type = MOST_CH_CONTROL | MOST_CH_ASYNC |
103497a6f772SChristian Gromm MOST_CH_ISOC | MOST_CH_SYNC;
103597a6f772SChristian Gromm if (usb_endpoint_dir_in(ep_desc))
103697a6f772SChristian Gromm tmp_cap->direction = MOST_CH_RX;
103797a6f772SChristian Gromm else
103897a6f772SChristian Gromm tmp_cap->direction = MOST_CH_TX;
103997a6f772SChristian Gromm tmp_cap++;
104097a6f772SChristian Gromm init_usb_anchor(&mdev->busy_urbs[i]);
104197a6f772SChristian Gromm spin_lock_init(&mdev->channel_lock[i]);
104297a6f772SChristian Gromm }
104397a6f772SChristian Gromm dev_dbg(dev, "claimed gadget: Vendor=%4.4x ProdID=%4.4x Bus=%02x Device=%02x\n",
104497a6f772SChristian Gromm le16_to_cpu(usb_dev->descriptor.idVendor),
104597a6f772SChristian Gromm le16_to_cpu(usb_dev->descriptor.idProduct),
104697a6f772SChristian Gromm usb_dev->bus->busnum,
104797a6f772SChristian Gromm usb_dev->devnum);
104897a6f772SChristian Gromm
104997a6f772SChristian Gromm dev_dbg(dev, "device path: /sys/bus/usb/devices/%d-%s:%d.%d\n",
105097a6f772SChristian Gromm usb_dev->bus->busnum,
105197a6f772SChristian Gromm usb_dev->devpath,
105297a6f772SChristian Gromm usb_dev->config->desc.bConfigurationValue,
105397a6f772SChristian Gromm usb_iface_desc->desc.bInterfaceNumber);
105497a6f772SChristian Gromm
105597a6f772SChristian Gromm ret = most_register_interface(&mdev->iface);
105697a6f772SChristian Gromm if (ret)
105797a6f772SChristian Gromm goto err_free_busy_urbs;
105897a6f772SChristian Gromm
105997a6f772SChristian Gromm mutex_lock(&mdev->io_mutex);
106097a6f772SChristian Gromm if (le16_to_cpu(usb_dev->descriptor.idProduct) == USB_DEV_ID_OS81118 ||
106197a6f772SChristian Gromm le16_to_cpu(usb_dev->descriptor.idProduct) == USB_DEV_ID_OS81119 ||
106297a6f772SChristian Gromm le16_to_cpu(usb_dev->descriptor.idProduct) == USB_DEV_ID_OS81210) {
106397a6f772SChristian Gromm mdev->dci = kzalloc(sizeof(*mdev->dci), GFP_KERNEL);
106497a6f772SChristian Gromm if (!mdev->dci) {
106597a6f772SChristian Gromm mutex_unlock(&mdev->io_mutex);
106697a6f772SChristian Gromm most_deregister_interface(&mdev->iface);
106797a6f772SChristian Gromm ret = -ENOMEM;
106897a6f772SChristian Gromm goto err_free_busy_urbs;
106997a6f772SChristian Gromm }
107097a6f772SChristian Gromm
107197a6f772SChristian Gromm mdev->dci->dev.init_name = "dci";
107297a6f772SChristian Gromm mdev->dci->dev.parent = get_device(mdev->iface.dev);
107397a6f772SChristian Gromm mdev->dci->dev.groups = dci_groups;
107497a6f772SChristian Gromm mdev->dci->dev.release = release_dci;
107597a6f772SChristian Gromm if (device_register(&mdev->dci->dev)) {
107697a6f772SChristian Gromm mutex_unlock(&mdev->io_mutex);
107797a6f772SChristian Gromm most_deregister_interface(&mdev->iface);
107897a6f772SChristian Gromm ret = -ENOMEM;
107997a6f772SChristian Gromm goto err_free_dci;
108097a6f772SChristian Gromm }
108197a6f772SChristian Gromm mdev->dci->usb_device = mdev->usb_device;
108297a6f772SChristian Gromm }
108397a6f772SChristian Gromm mutex_unlock(&mdev->io_mutex);
108497a6f772SChristian Gromm return 0;
108597a6f772SChristian Gromm err_free_dci:
108697a6f772SChristian Gromm put_device(&mdev->dci->dev);
108797a6f772SChristian Gromm err_free_busy_urbs:
108897a6f772SChristian Gromm kfree(mdev->busy_urbs);
108997a6f772SChristian Gromm err_free_ep_address:
109097a6f772SChristian Gromm kfree(mdev->ep_address);
109197a6f772SChristian Gromm err_free_cap:
109297a6f772SChristian Gromm kfree(mdev->cap);
109397a6f772SChristian Gromm err_free_conf:
109497a6f772SChristian Gromm kfree(mdev->conf);
109597a6f772SChristian Gromm err_free_mdev:
109697a6f772SChristian Gromm put_device(&mdev->dev);
109797a6f772SChristian Gromm return ret;
109897a6f772SChristian Gromm }
109997a6f772SChristian Gromm
110097a6f772SChristian Gromm /**
110197a6f772SChristian Gromm * hdm_disconnect - disconnect function of USB device driver
110297a6f772SChristian Gromm * @interface: Interface of the attached USB device
110397a6f772SChristian Gromm *
110497a6f772SChristian Gromm * This deregisters the interface with the core, removes the kernel timer
110597a6f772SChristian Gromm * and frees resources.
110697a6f772SChristian Gromm *
110797a6f772SChristian Gromm * Context: hub kernel thread
110897a6f772SChristian Gromm */
hdm_disconnect(struct usb_interface * interface)110997a6f772SChristian Gromm static void hdm_disconnect(struct usb_interface *interface)
111097a6f772SChristian Gromm {
111197a6f772SChristian Gromm struct most_dev *mdev = usb_get_intfdata(interface);
111297a6f772SChristian Gromm
111397a6f772SChristian Gromm mutex_lock(&mdev->io_mutex);
111497a6f772SChristian Gromm usb_set_intfdata(interface, NULL);
111597a6f772SChristian Gromm mdev->usb_device = NULL;
111697a6f772SChristian Gromm mutex_unlock(&mdev->io_mutex);
111797a6f772SChristian Gromm
111897a6f772SChristian Gromm del_timer_sync(&mdev->link_stat_timer);
111997a6f772SChristian Gromm cancel_work_sync(&mdev->poll_work_obj);
112097a6f772SChristian Gromm
112197a6f772SChristian Gromm if (mdev->dci)
112297a6f772SChristian Gromm device_unregister(&mdev->dci->dev);
112397a6f772SChristian Gromm most_deregister_interface(&mdev->iface);
112497a6f772SChristian Gromm
112597a6f772SChristian Gromm kfree(mdev->busy_urbs);
112697a6f772SChristian Gromm kfree(mdev->cap);
112797a6f772SChristian Gromm kfree(mdev->conf);
112897a6f772SChristian Gromm kfree(mdev->ep_address);
112997a6f772SChristian Gromm put_device(&mdev->dci->dev);
113097a6f772SChristian Gromm put_device(&mdev->dev);
113197a6f772SChristian Gromm }
113297a6f772SChristian Gromm
hdm_suspend(struct usb_interface * interface,pm_message_t message)113397a6f772SChristian Gromm static int hdm_suspend(struct usb_interface *interface, pm_message_t message)
113497a6f772SChristian Gromm {
113597a6f772SChristian Gromm struct most_dev *mdev = usb_get_intfdata(interface);
113697a6f772SChristian Gromm int i;
113797a6f772SChristian Gromm
113897a6f772SChristian Gromm mutex_lock(&mdev->io_mutex);
113997a6f772SChristian Gromm for (i = 0; i < mdev->iface.num_channels; i++) {
114097a6f772SChristian Gromm most_stop_enqueue(&mdev->iface, i);
114197a6f772SChristian Gromm usb_kill_anchored_urbs(&mdev->busy_urbs[i]);
114297a6f772SChristian Gromm }
114397a6f772SChristian Gromm mutex_unlock(&mdev->io_mutex);
114497a6f772SChristian Gromm return 0;
114597a6f772SChristian Gromm }
114697a6f772SChristian Gromm
hdm_resume(struct usb_interface * interface)114797a6f772SChristian Gromm static int hdm_resume(struct usb_interface *interface)
114897a6f772SChristian Gromm {
114997a6f772SChristian Gromm struct most_dev *mdev = usb_get_intfdata(interface);
115097a6f772SChristian Gromm int i;
115197a6f772SChristian Gromm
115297a6f772SChristian Gromm mutex_lock(&mdev->io_mutex);
115397a6f772SChristian Gromm for (i = 0; i < mdev->iface.num_channels; i++)
115497a6f772SChristian Gromm most_resume_enqueue(&mdev->iface, i);
115597a6f772SChristian Gromm mutex_unlock(&mdev->io_mutex);
115697a6f772SChristian Gromm return 0;
115797a6f772SChristian Gromm }
115897a6f772SChristian Gromm
115997a6f772SChristian Gromm static struct usb_driver hdm_usb = {
116097a6f772SChristian Gromm .name = "hdm_usb",
116197a6f772SChristian Gromm .id_table = usbid,
116297a6f772SChristian Gromm .probe = hdm_probe,
116397a6f772SChristian Gromm .disconnect = hdm_disconnect,
116497a6f772SChristian Gromm .resume = hdm_resume,
116597a6f772SChristian Gromm .suspend = hdm_suspend,
116697a6f772SChristian Gromm };
116797a6f772SChristian Gromm
116897a6f772SChristian Gromm module_usb_driver(hdm_usb);
116997a6f772SChristian Gromm MODULE_LICENSE("GPL");
117097a6f772SChristian Gromm MODULE_AUTHOR("Christian Gromm <christian.gromm@microchip.com>");
117197a6f772SChristian Gromm MODULE_DESCRIPTION("HDM_4_USB");
1172