/** * hdm_add_padding - add padding bytes * @mdev: most device * @channel: channel ID * @mbo: buffer object * * This inserts the INIC hardware specific padding bytes into a streaming * channel's buffer */ static int hdm_add_padding(struct most_dev *mdev, int channel, struct mbo *mbo) { struct most_channel_config *conf = &mdev->conf[channel]; unsigned int frame_size = get_stream_frame_size(conf); unsigned int j, num_frames; u16 rd_addr, wr_addr; if (!frame_size) return -EIO; num_frames = mbo->buffer_length / frame_size; if (num_frames < 1) { dev_err(&mdev->usb_device->dev, "Missed minimal transfer unit.\n"); return -EIO; } for (j = 1; j < num_frames; j++) { wr_addr = (num_frames - j) * USB_MTU; rd_addr = (num_frames - j) * frame_size; memmove(mbo->virt_address + wr_addr, mbo->virt_address + rd_addr, frame_size); } mbo->buffer_length = num_frames * USB_MTU; return 0; }
/** * hdm_remove_padding - remove padding bytes * @mdev: most device * @channel: channel ID * @mbo: buffer object * * This takes the INIC hardware specific padding bytes off a streaming * channel's buffer. */ static int hdm_remove_padding(struct most_dev *mdev, int channel, struct mbo *mbo) { struct most_channel_config *const conf = &mdev->conf[channel]; unsigned int frame_size = get_stream_frame_size(conf); unsigned int j, num_frames; if (!frame_size) return -EIO; num_frames = mbo->processed_length / USB_MTU; for (j = 1; j < num_frames; j++) memmove(mbo->virt_address + frame_size * j, mbo->virt_address + USB_MTU * j, frame_size); mbo->processed_length = frame_size * num_frames; return 0; }
/** * hdm_configure_channel - receive channel configuration from core * @iface: interface * @channel: channel ID * @conf: structure that holds the configuration information */ static int hdm_configure_channel(struct most_interface *iface, int channel, struct most_channel_config *conf) { unsigned int num_frames; unsigned int frame_size; unsigned int temp_size; unsigned int tail_space; struct most_dev *mdev = to_mdev(iface); struct device *dev = &mdev->usb_device->dev; mdev->is_channel_healthy[channel] = true; mdev->clear_work[channel].channel = channel; mdev->clear_work[channel].mdev = mdev; INIT_WORK(&mdev->clear_work[channel].ws, wq_clear_halt); if (unlikely(!iface || !conf)) { dev_err(dev, "Bad interface or config pointer.\n"); return -EINVAL; } if (unlikely(channel < 0 || channel >= iface->num_channels)) { dev_err(dev, "Channel ID out of range.\n"); return -EINVAL; } if (!conf->num_buffers || !conf->buffer_size) { dev_err(dev, "Misconfig: buffer size or #buffers zero.\n"); return -EINVAL; } if (conf->data_type != MOST_CH_SYNC && !(conf->data_type == MOST_CH_ISOC && conf->packets_per_xact != 0xFF)) { mdev->padding_active[channel] = false; goto exit; } mdev->padding_active[channel] = true; temp_size = conf->buffer_size; frame_size = get_stream_frame_size(conf); if (frame_size == 0 || frame_size > USB_MTU) { dev_warn(dev, "Misconfig: frame size wrong\n"); return -EINVAL; } if (conf->buffer_size % frame_size) { u16 tmp_val; tmp_val = conf->buffer_size / frame_size; conf->buffer_size = tmp_val * frame_size; dev_notice(dev, "Channel %d - rounding buffer size to %d bytes, channel config says %d bytes\n", channel, conf->buffer_size, temp_size); } num_frames = conf->buffer_size / frame_size; tail_space = num_frames * (USB_MTU - frame_size); temp_size += tail_space; /* calculate extra length to comply w/ HW padding */ conf->extra_len = (DIV_ROUND_UP(temp_size, USB_MTU) * USB_MTU) - conf->buffer_size; exit: mdev->conf[channel] = *conf; return 0; }
/** * hdm_configure_channel - receive channel configuration from core * @iface: interface * @channel: channel ID * @conf: structure that holds the configuration information * * The attached network interface controller (NIC) supports a padding mode * to avoid short packets on USB, hence increasing the performance due to a * lower interrupt load. This mode is default for synchronous data and can * be switched on for isochronous data. In case padding is active the * driver needs to know the frame size of the payload in order to calculate * the number of bytes it needs to pad when transmitting or to cut off when * receiving data. * */ static int hdm_configure_channel(struct most_interface *iface, int channel, struct most_channel_config *conf) { unsigned int num_frames; unsigned int frame_size; struct most_dev *mdev = to_mdev(iface); struct device *dev = &mdev->usb_device->dev; mdev->is_channel_healthy[channel] = true; mdev->clear_work[channel].channel = channel; mdev->clear_work[channel].mdev = mdev; INIT_WORK(&mdev->clear_work[channel].ws, wq_clear_halt); if (unlikely(!iface || !conf)) { dev_err(dev, "Bad interface or config pointer.\n"); return -EINVAL; } if (unlikely(channel < 0 || channel >= iface->num_channels)) { dev_err(dev, "Channel ID out of range.\n"); return -EINVAL; } if (!conf->num_buffers || !conf->buffer_size) { dev_err(dev, "Misconfig: buffer size or #buffers zero.\n"); return -EINVAL; } if (conf->data_type != MOST_CH_SYNC && !(conf->data_type == MOST_CH_ISOC && conf->packets_per_xact != 0xFF)) { mdev->padding_active[channel] = false; /* * Since the NIC's padding mode is not going to be * used, we can skip the frame size calculations and * move directly on to exit. */ goto exit; } mdev->padding_active[channel] = true; frame_size = get_stream_frame_size(conf); if (frame_size == 0 || frame_size > USB_MTU) { dev_warn(dev, "Misconfig: frame size wrong\n"); return -EINVAL; } num_frames = conf->buffer_size / frame_size; if (conf->buffer_size % frame_size) { u16 old_size = conf->buffer_size; conf->buffer_size = num_frames * frame_size; dev_warn(dev, "%s: fixed buffer size (%d -> %d)\n", mdev->suffix[channel], old_size, conf->buffer_size); } /* calculate extra length to comply w/ HW padding */ conf->extra_len = num_frames * (USB_MTU - frame_size); exit: mdev->conf[channel] = *conf; if (conf->data_type == MOST_CH_ASYNC) { u16 ep = mdev->ep_address[channel]; if (start_sync_ep(mdev->usb_device, ep) < 0) dev_warn(dev, "sync for ep%02x failed", ep); } return 0; }