Merge tag 'usb-for-v4.13' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-testing
Felipe writes: usb: changes for v4.13 merge window This time around we have a total of 57 non-merge commits. A list of most important changes follows: - Improvements to dwc3 tracing interface - Initial dual-role support for dwc3 - Improvements to how we handle DMA resources in dwc3 - A new f_uac1 implementation which much more flexible - Removal of AVR32 bits - Improvements to f_mass_storage driver
This commit is contained in:
@@ -41,7 +41,7 @@ menuconfig USB_GADGET
|
||||
don't have this kind of hardware (except maybe inside Linux PDAs).
|
||||
|
||||
For more information, see <http://www.linux-usb.org/gadget> and
|
||||
the kernel DocBook documentation for this API.
|
||||
the kernel documentation for this API.
|
||||
|
||||
if USB_GADGET
|
||||
|
||||
@@ -158,6 +158,9 @@ config USB_U_SERIAL
|
||||
config USB_U_ETHER
|
||||
tristate
|
||||
|
||||
config USB_U_AUDIO
|
||||
tristate
|
||||
|
||||
config USB_F_SERIAL
|
||||
tristate
|
||||
|
||||
@@ -191,6 +194,9 @@ config USB_F_FS
|
||||
config USB_F_UAC1
|
||||
tristate
|
||||
|
||||
config USB_F_UAC1_LEGACY
|
||||
tristate
|
||||
|
||||
config USB_F_UAC2
|
||||
tristate
|
||||
|
||||
@@ -368,12 +374,30 @@ config USB_CONFIGFS_F_UAC1
|
||||
depends on SND
|
||||
select USB_LIBCOMPOSITE
|
||||
select SND_PCM
|
||||
select USB_U_AUDIO
|
||||
select USB_F_UAC1
|
||||
help
|
||||
This Audio function implements 1 AudioControl interface,
|
||||
1 AudioStreaming Interface each for USB-OUT and USB-IN.
|
||||
This driver requires a real Audio codec to be present
|
||||
on the device.
|
||||
This driver doesn't expect any real Audio codec to be present
|
||||
on the device - the audio streams are simply sinked to and
|
||||
sourced from a virtual ALSA sound card created. The user-space
|
||||
application may choose to do whatever it wants with the data
|
||||
received from the USB Host and choose to provide whatever it
|
||||
wants as audio data to the USB Host.
|
||||
|
||||
config USB_CONFIGFS_F_UAC1_LEGACY
|
||||
bool "Audio Class 1.0 (legacy implementation)"
|
||||
depends on USB_CONFIGFS
|
||||
depends on SND
|
||||
select USB_LIBCOMPOSITE
|
||||
select SND_PCM
|
||||
select USB_F_UAC1_LEGACY
|
||||
help
|
||||
This Audio function implements 1 AudioControl interface,
|
||||
1 AudioStreaming Interface each for USB-OUT and USB-IN.
|
||||
This is a legacy driver and requires a real Audio codec
|
||||
to be present on the device.
|
||||
|
||||
config USB_CONFIGFS_F_UAC2
|
||||
bool "Audio Class 2.0"
|
||||
@@ -381,6 +405,7 @@ config USB_CONFIGFS_F_UAC2
|
||||
depends on SND
|
||||
select USB_LIBCOMPOSITE
|
||||
select SND_PCM
|
||||
select USB_U_AUDIO
|
||||
select USB_F_UAC2
|
||||
help
|
||||
This Audio function is compatible with USB Audio Class
|
||||
|
@@ -610,7 +610,6 @@ static int count_configs(struct usb_composite_dev *cdev, unsigned type)
|
||||
static int bos_desc(struct usb_composite_dev *cdev)
|
||||
{
|
||||
struct usb_ext_cap_descriptor *usb_ext;
|
||||
struct usb_ss_cap_descriptor *ss_cap;
|
||||
struct usb_dcd_config_params dcd_config_params;
|
||||
struct usb_bos_descriptor *bos = cdev->req->buf;
|
||||
|
||||
@@ -636,29 +635,35 @@ static int bos_desc(struct usb_composite_dev *cdev)
|
||||
* The Superspeed USB Capability descriptor shall be implemented by all
|
||||
* SuperSpeed devices.
|
||||
*/
|
||||
ss_cap = cdev->req->buf + le16_to_cpu(bos->wTotalLength);
|
||||
bos->bNumDeviceCaps++;
|
||||
le16_add_cpu(&bos->wTotalLength, USB_DT_USB_SS_CAP_SIZE);
|
||||
ss_cap->bLength = USB_DT_USB_SS_CAP_SIZE;
|
||||
ss_cap->bDescriptorType = USB_DT_DEVICE_CAPABILITY;
|
||||
ss_cap->bDevCapabilityType = USB_SS_CAP_TYPE;
|
||||
ss_cap->bmAttributes = 0; /* LTM is not supported yet */
|
||||
ss_cap->wSpeedSupported = cpu_to_le16(USB_LOW_SPEED_OPERATION |
|
||||
USB_FULL_SPEED_OPERATION |
|
||||
USB_HIGH_SPEED_OPERATION |
|
||||
USB_5GBPS_OPERATION);
|
||||
ss_cap->bFunctionalitySupport = USB_LOW_SPEED_OPERATION;
|
||||
if (gadget_is_superspeed(cdev->gadget)) {
|
||||
struct usb_ss_cap_descriptor *ss_cap;
|
||||
|
||||
/* Get Controller configuration */
|
||||
if (cdev->gadget->ops->get_config_params)
|
||||
cdev->gadget->ops->get_config_params(&dcd_config_params);
|
||||
else {
|
||||
dcd_config_params.bU1devExitLat = USB_DEFAULT_U1_DEV_EXIT_LAT;
|
||||
dcd_config_params.bU2DevExitLat =
|
||||
cpu_to_le16(USB_DEFAULT_U2_DEV_EXIT_LAT);
|
||||
ss_cap = cdev->req->buf + le16_to_cpu(bos->wTotalLength);
|
||||
bos->bNumDeviceCaps++;
|
||||
le16_add_cpu(&bos->wTotalLength, USB_DT_USB_SS_CAP_SIZE);
|
||||
ss_cap->bLength = USB_DT_USB_SS_CAP_SIZE;
|
||||
ss_cap->bDescriptorType = USB_DT_DEVICE_CAPABILITY;
|
||||
ss_cap->bDevCapabilityType = USB_SS_CAP_TYPE;
|
||||
ss_cap->bmAttributes = 0; /* LTM is not supported yet */
|
||||
ss_cap->wSpeedSupported = cpu_to_le16(USB_LOW_SPEED_OPERATION |
|
||||
USB_FULL_SPEED_OPERATION |
|
||||
USB_HIGH_SPEED_OPERATION |
|
||||
USB_5GBPS_OPERATION);
|
||||
ss_cap->bFunctionalitySupport = USB_LOW_SPEED_OPERATION;
|
||||
|
||||
/* Get Controller configuration */
|
||||
if (cdev->gadget->ops->get_config_params) {
|
||||
cdev->gadget->ops->get_config_params(
|
||||
&dcd_config_params);
|
||||
} else {
|
||||
dcd_config_params.bU1devExitLat =
|
||||
USB_DEFAULT_U1_DEV_EXIT_LAT;
|
||||
dcd_config_params.bU2DevExitLat =
|
||||
cpu_to_le16(USB_DEFAULT_U2_DEV_EXIT_LAT);
|
||||
}
|
||||
ss_cap->bU1devExitLat = dcd_config_params.bU1devExitLat;
|
||||
ss_cap->bU2DevExitLat = dcd_config_params.bU2DevExitLat;
|
||||
}
|
||||
ss_cap->bU1devExitLat = dcd_config_params.bU1devExitLat;
|
||||
ss_cap->bU2DevExitLat = dcd_config_params.bU2DevExitLat;
|
||||
|
||||
/* The SuperSpeedPlus USB Device Capability descriptor */
|
||||
if (gadget_is_superspeed_plus(cdev->gadget)) {
|
||||
@@ -1602,7 +1607,10 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
|
||||
cdev->desc.bcdUSB = cpu_to_le16(0x0210);
|
||||
}
|
||||
} else {
|
||||
cdev->desc.bcdUSB = cpu_to_le16(0x0200);
|
||||
if (gadget->lpm_capable)
|
||||
cdev->desc.bcdUSB = cpu_to_le16(0x0201);
|
||||
else
|
||||
cdev->desc.bcdUSB = cpu_to_le16(0x0200);
|
||||
}
|
||||
|
||||
value = min(w_length, (u16) sizeof cdev->desc);
|
||||
@@ -1633,7 +1641,8 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
|
||||
value = min(w_length, (u16) value);
|
||||
break;
|
||||
case USB_DT_BOS:
|
||||
if (gadget_is_superspeed(gadget)) {
|
||||
if (gadget_is_superspeed(gadget) ||
|
||||
gadget->lpm_capable) {
|
||||
value = bos_desc(cdev);
|
||||
value = min(w_length, (u16) value);
|
||||
}
|
||||
|
@@ -738,7 +738,7 @@ static inline struct gadget_info *os_desc_item_to_gadget_info(
|
||||
|
||||
static ssize_t os_desc_use_show(struct config_item *item, char *page)
|
||||
{
|
||||
return sprintf(page, "%d",
|
||||
return sprintf(page, "%d\n",
|
||||
os_desc_item_to_gadget_info(item)->use_os_desc);
|
||||
}
|
||||
|
||||
@@ -762,7 +762,7 @@ static ssize_t os_desc_use_store(struct config_item *item, const char *page,
|
||||
|
||||
static ssize_t os_desc_b_vendor_code_show(struct config_item *item, char *page)
|
||||
{
|
||||
return sprintf(page, "%d",
|
||||
return sprintf(page, "0x%02x\n",
|
||||
os_desc_item_to_gadget_info(item)->b_vendor_code);
|
||||
}
|
||||
|
||||
@@ -787,9 +787,13 @@ static ssize_t os_desc_b_vendor_code_store(struct config_item *item,
|
||||
static ssize_t os_desc_qw_sign_show(struct config_item *item, char *page)
|
||||
{
|
||||
struct gadget_info *gi = os_desc_item_to_gadget_info(item);
|
||||
int res;
|
||||
|
||||
memcpy(page, gi->qw_sign, OS_STRING_QW_SIGN_LEN);
|
||||
return OS_STRING_QW_SIGN_LEN;
|
||||
res = utf16s_to_utf8s((wchar_t *) gi->qw_sign, OS_STRING_QW_SIGN_LEN,
|
||||
UTF16_LITTLE_ENDIAN, page, PAGE_SIZE - 1);
|
||||
page[res++] = '\n';
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static ssize_t os_desc_qw_sign_store(struct config_item *item, const char *page,
|
||||
@@ -900,7 +904,7 @@ static inline struct usb_os_desc_ext_prop
|
||||
|
||||
static ssize_t ext_prop_type_show(struct config_item *item, char *page)
|
||||
{
|
||||
return sprintf(page, "%d", to_usb_os_desc_ext_prop(item)->type);
|
||||
return sprintf(page, "%d\n", to_usb_os_desc_ext_prop(item)->type);
|
||||
}
|
||||
|
||||
static ssize_t ext_prop_type_store(struct config_item *item,
|
||||
|
@@ -32,8 +32,11 @@ usb_f_mass_storage-y := f_mass_storage.o storage_common.o
|
||||
obj-$(CONFIG_USB_F_MASS_STORAGE)+= usb_f_mass_storage.o
|
||||
usb_f_fs-y := f_fs.o
|
||||
obj-$(CONFIG_USB_F_FS) += usb_f_fs.o
|
||||
usb_f_uac1-y := f_uac1.o u_uac1.o
|
||||
obj-$(CONFIG_USB_U_AUDIO) += u_audio.o
|
||||
usb_f_uac1-y := f_uac1.o
|
||||
obj-$(CONFIG_USB_F_UAC1) += usb_f_uac1.o
|
||||
usb_f_uac1_legacy-y := f_uac1_legacy.o u_uac1_legacy.o
|
||||
obj-$(CONFIG_USB_F_UAC1_LEGACY) += usb_f_uac1_legacy.o
|
||||
usb_f_uac2-y := f_uac2.o
|
||||
obj-$(CONFIG_USB_F_UAC2) += usb_f_uac2.o
|
||||
usb_f_uvc-y := f_uvc.o uvc_queue.o uvc_v4l2.o uvc_video.o uvc_configfs.o
|
||||
|
@@ -127,7 +127,6 @@ struct ffs_ep {
|
||||
struct ffs_epfile {
|
||||
/* Protects ep->ep and ep->req. */
|
||||
struct mutex mutex;
|
||||
wait_queue_head_t wait;
|
||||
|
||||
struct ffs_data *ffs;
|
||||
struct ffs_ep *ep; /* P: ffs->eps_lock */
|
||||
@@ -889,7 +888,8 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
|
||||
if (file->f_flags & O_NONBLOCK)
|
||||
return -EAGAIN;
|
||||
|
||||
ret = wait_event_interruptible(epfile->wait, (ep = epfile->ep));
|
||||
ret = wait_event_interruptible(
|
||||
epfile->ffs->wait, (ep = epfile->ep));
|
||||
if (ret)
|
||||
return -EINTR;
|
||||
}
|
||||
@@ -1189,6 +1189,7 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code,
|
||||
unsigned long value)
|
||||
{
|
||||
struct ffs_epfile *epfile = file->private_data;
|
||||
struct ffs_ep *ep;
|
||||
int ret;
|
||||
|
||||
ENTER();
|
||||
@@ -1196,50 +1197,65 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code,
|
||||
if (WARN_ON(epfile->ffs->state != FFS_ACTIVE))
|
||||
return -ENODEV;
|
||||
|
||||
/* Wait for endpoint to be enabled */
|
||||
ep = epfile->ep;
|
||||
if (!ep) {
|
||||
if (file->f_flags & O_NONBLOCK)
|
||||
return -EAGAIN;
|
||||
|
||||
ret = wait_event_interruptible(
|
||||
epfile->ffs->wait, (ep = epfile->ep));
|
||||
if (ret)
|
||||
return -EINTR;
|
||||
}
|
||||
|
||||
spin_lock_irq(&epfile->ffs->eps_lock);
|
||||
if (likely(epfile->ep)) {
|
||||
switch (code) {
|
||||
case FUNCTIONFS_FIFO_STATUS:
|
||||
ret = usb_ep_fifo_status(epfile->ep->ep);
|
||||
break;
|
||||
case FUNCTIONFS_FIFO_FLUSH:
|
||||
usb_ep_fifo_flush(epfile->ep->ep);
|
||||
ret = 0;
|
||||
break;
|
||||
case FUNCTIONFS_CLEAR_HALT:
|
||||
ret = usb_ep_clear_halt(epfile->ep->ep);
|
||||
break;
|
||||
case FUNCTIONFS_ENDPOINT_REVMAP:
|
||||
ret = epfile->ep->num;
|
||||
break;
|
||||
case FUNCTIONFS_ENDPOINT_DESC:
|
||||
{
|
||||
int desc_idx;
|
||||
struct usb_endpoint_descriptor *desc;
|
||||
|
||||
switch (epfile->ffs->gadget->speed) {
|
||||
case USB_SPEED_SUPER:
|
||||
desc_idx = 2;
|
||||
break;
|
||||
case USB_SPEED_HIGH:
|
||||
desc_idx = 1;
|
||||
break;
|
||||
default:
|
||||
desc_idx = 0;
|
||||
}
|
||||
desc = epfile->ep->descs[desc_idx];
|
||||
/* In the meantime, endpoint got disabled or changed. */
|
||||
if (epfile->ep != ep) {
|
||||
spin_unlock_irq(&epfile->ffs->eps_lock);
|
||||
return -ESHUTDOWN;
|
||||
}
|
||||
|
||||
spin_unlock_irq(&epfile->ffs->eps_lock);
|
||||
ret = copy_to_user((void *)value, desc, desc->bLength);
|
||||
if (ret)
|
||||
ret = -EFAULT;
|
||||
return ret;
|
||||
}
|
||||
switch (code) {
|
||||
case FUNCTIONFS_FIFO_STATUS:
|
||||
ret = usb_ep_fifo_status(epfile->ep->ep);
|
||||
break;
|
||||
case FUNCTIONFS_FIFO_FLUSH:
|
||||
usb_ep_fifo_flush(epfile->ep->ep);
|
||||
ret = 0;
|
||||
break;
|
||||
case FUNCTIONFS_CLEAR_HALT:
|
||||
ret = usb_ep_clear_halt(epfile->ep->ep);
|
||||
break;
|
||||
case FUNCTIONFS_ENDPOINT_REVMAP:
|
||||
ret = epfile->ep->num;
|
||||
break;
|
||||
case FUNCTIONFS_ENDPOINT_DESC:
|
||||
{
|
||||
int desc_idx;
|
||||
struct usb_endpoint_descriptor *desc;
|
||||
|
||||
switch (epfile->ffs->gadget->speed) {
|
||||
case USB_SPEED_SUPER:
|
||||
desc_idx = 2;
|
||||
break;
|
||||
case USB_SPEED_HIGH:
|
||||
desc_idx = 1;
|
||||
break;
|
||||
default:
|
||||
ret = -ENOTTY;
|
||||
desc_idx = 0;
|
||||
}
|
||||
} else {
|
||||
ret = -ENODEV;
|
||||
desc = epfile->ep->descs[desc_idx];
|
||||
|
||||
spin_unlock_irq(&epfile->ffs->eps_lock);
|
||||
ret = copy_to_user((void *)value, desc, desc->bLength);
|
||||
if (ret)
|
||||
ret = -EFAULT;
|
||||
return ret;
|
||||
}
|
||||
default:
|
||||
ret = -ENOTTY;
|
||||
}
|
||||
spin_unlock_irq(&epfile->ffs->eps_lock);
|
||||
|
||||
@@ -1593,7 +1609,8 @@ static void ffs_data_put(struct ffs_data *ffs)
|
||||
pr_info("%s(): freeing\n", __func__);
|
||||
ffs_data_clear(ffs);
|
||||
BUG_ON(waitqueue_active(&ffs->ev.waitq) ||
|
||||
waitqueue_active(&ffs->ep0req_completion.wait));
|
||||
waitqueue_active(&ffs->ep0req_completion.wait) ||
|
||||
waitqueue_active(&ffs->wait));
|
||||
kfree(ffs->dev_name);
|
||||
kfree(ffs);
|
||||
}
|
||||
@@ -1640,6 +1657,7 @@ static struct ffs_data *ffs_data_new(void)
|
||||
mutex_init(&ffs->mutex);
|
||||
spin_lock_init(&ffs->eps_lock);
|
||||
init_waitqueue_head(&ffs->ev.waitq);
|
||||
init_waitqueue_head(&ffs->wait);
|
||||
init_completion(&ffs->ep0req_completion);
|
||||
|
||||
/* XXX REVISIT need to update it in some places, or do we? */
|
||||
@@ -1761,7 +1779,6 @@ static int ffs_epfiles_create(struct ffs_data *ffs)
|
||||
for (i = 1; i <= count; ++i, ++epfile) {
|
||||
epfile->ffs = ffs;
|
||||
mutex_init(&epfile->mutex);
|
||||
init_waitqueue_head(&epfile->wait);
|
||||
if (ffs->user_flags & FUNCTIONFS_VIRTUAL_ADDR)
|
||||
sprintf(epfile->name, "ep%02x", ffs->eps_addrmap[i]);
|
||||
else
|
||||
@@ -1786,8 +1803,7 @@ static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count)
|
||||
ENTER();
|
||||
|
||||
for (; count; --count, ++epfile) {
|
||||
BUG_ON(mutex_is_locked(&epfile->mutex) ||
|
||||
waitqueue_active(&epfile->wait));
|
||||
BUG_ON(mutex_is_locked(&epfile->mutex));
|
||||
if (epfile->dentry) {
|
||||
d_delete(epfile->dentry);
|
||||
dput(epfile->dentry);
|
||||
@@ -1874,11 +1890,11 @@ static int ffs_func_eps_enable(struct ffs_function *func)
|
||||
break;
|
||||
}
|
||||
|
||||
wake_up(&epfile->wait);
|
||||
|
||||
++ep;
|
||||
++epfile;
|
||||
}
|
||||
|
||||
wake_up_interruptible(&ffs->wait);
|
||||
spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
|
||||
|
||||
return ret;
|
||||
|
@@ -260,12 +260,13 @@ struct fsg_common {
|
||||
struct usb_gadget *gadget;
|
||||
struct usb_composite_dev *cdev;
|
||||
struct fsg_dev *fsg, *new_fsg;
|
||||
wait_queue_head_t io_wait;
|
||||
wait_queue_head_t fsg_wait;
|
||||
|
||||
/* filesem protects: backing files in use */
|
||||
struct rw_semaphore filesem;
|
||||
|
||||
/* lock protects: state, all the req_busy's */
|
||||
/* lock protects: state and thread_task */
|
||||
spinlock_t lock;
|
||||
|
||||
struct usb_ep *ep0; /* Copy of gadget->ep0 */
|
||||
@@ -303,7 +304,6 @@ struct fsg_common {
|
||||
unsigned int running:1;
|
||||
unsigned int sysfs:1;
|
||||
|
||||
int thread_wakeup_needed;
|
||||
struct completion thread_notifier;
|
||||
struct task_struct *thread_task;
|
||||
|
||||
@@ -355,7 +355,7 @@ typedef void (*fsg_routine_t)(struct fsg_dev *);
|
||||
|
||||
static int exception_in_progress(struct fsg_common *common)
|
||||
{
|
||||
return common->state > FSG_STATE_IDLE;
|
||||
return common->state > FSG_STATE_NORMAL;
|
||||
}
|
||||
|
||||
/* Make bulk-out requests be divisible by the maxpacket size */
|
||||
@@ -393,20 +393,6 @@ static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep)
|
||||
|
||||
/* These routines may be called in process context or in_irq */
|
||||
|
||||
/* Caller must hold fsg->lock */
|
||||
static void wakeup_thread(struct fsg_common *common)
|
||||
{
|
||||
/*
|
||||
* Ensure the reading of thread_wakeup_needed
|
||||
* and the writing of bh->state are completed
|
||||
*/
|
||||
smp_mb();
|
||||
/* Tell the main thread that something has happened */
|
||||
common->thread_wakeup_needed = 1;
|
||||
if (common->thread_task)
|
||||
wake_up_process(common->thread_task);
|
||||
}
|
||||
|
||||
static void raise_exception(struct fsg_common *common, enum fsg_state new_state)
|
||||
{
|
||||
unsigned long flags;
|
||||
@@ -460,13 +446,9 @@ static void bulk_in_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
if (req->status == -ECONNRESET) /* Request was cancelled */
|
||||
usb_ep_fifo_flush(ep);
|
||||
|
||||
/* Hold the lock while we update the request and buffer states */
|
||||
smp_wmb();
|
||||
spin_lock(&common->lock);
|
||||
bh->inreq_busy = 0;
|
||||
bh->state = BUF_STATE_EMPTY;
|
||||
wakeup_thread(common);
|
||||
spin_unlock(&common->lock);
|
||||
/* Synchronize with the smp_load_acquire() in sleep_thread() */
|
||||
smp_store_release(&bh->state, BUF_STATE_EMPTY);
|
||||
wake_up(&common->io_wait);
|
||||
}
|
||||
|
||||
static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
@@ -481,13 +463,9 @@ static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
if (req->status == -ECONNRESET) /* Request was cancelled */
|
||||
usb_ep_fifo_flush(ep);
|
||||
|
||||
/* Hold the lock while we update the request and buffer states */
|
||||
smp_wmb();
|
||||
spin_lock(&common->lock);
|
||||
bh->outreq_busy = 0;
|
||||
bh->state = BUF_STATE_FULL;
|
||||
wakeup_thread(common);
|
||||
spin_unlock(&common->lock);
|
||||
/* Synchronize with the smp_load_acquire() in sleep_thread() */
|
||||
smp_store_release(&bh->state, BUF_STATE_FULL);
|
||||
wake_up(&common->io_wait);
|
||||
}
|
||||
|
||||
static int _fsg_common_get_max_lun(struct fsg_common *common)
|
||||
@@ -532,7 +510,7 @@ static int fsg_setup(struct usb_function *f,
|
||||
* and reinitialize our state.
|
||||
*/
|
||||
DBG(fsg, "bulk reset request\n");
|
||||
raise_exception(fsg->common, FSG_STATE_RESET);
|
||||
raise_exception(fsg->common, FSG_STATE_PROTOCOL_RESET);
|
||||
return USB_GADGET_DELAYED_STATUS;
|
||||
|
||||
case US_BULK_GET_MAX_LUN:
|
||||
@@ -563,43 +541,39 @@ static int fsg_setup(struct usb_function *f,
|
||||
/* All the following routines run in process context */
|
||||
|
||||
/* Use this for bulk or interrupt transfers, not ep0 */
|
||||
static void start_transfer(struct fsg_dev *fsg, struct usb_ep *ep,
|
||||
struct usb_request *req, int *pbusy,
|
||||
enum fsg_buffer_state *state)
|
||||
static int start_transfer(struct fsg_dev *fsg, struct usb_ep *ep,
|
||||
struct usb_request *req)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (ep == fsg->bulk_in)
|
||||
dump_msg(fsg, "bulk-in", req->buf, req->length);
|
||||
|
||||
spin_lock_irq(&fsg->common->lock);
|
||||
*pbusy = 1;
|
||||
*state = BUF_STATE_BUSY;
|
||||
spin_unlock_irq(&fsg->common->lock);
|
||||
|
||||
rc = usb_ep_queue(ep, req, GFP_KERNEL);
|
||||
if (rc == 0)
|
||||
return; /* All good, we're done */
|
||||
if (rc) {
|
||||
|
||||
*pbusy = 0;
|
||||
*state = BUF_STATE_EMPTY;
|
||||
/* We can't do much more than wait for a reset */
|
||||
req->status = rc;
|
||||
|
||||
/* We can't do much more than wait for a reset */
|
||||
|
||||
/*
|
||||
* Note: currently the net2280 driver fails zero-length
|
||||
* submissions if DMA is enabled.
|
||||
*/
|
||||
if (rc != -ESHUTDOWN && !(rc == -EOPNOTSUPP && req->length == 0))
|
||||
WARNING(fsg, "error in submission: %s --> %d\n", ep->name, rc);
|
||||
/*
|
||||
* Note: currently the net2280 driver fails zero-length
|
||||
* submissions if DMA is enabled.
|
||||
*/
|
||||
if (rc != -ESHUTDOWN &&
|
||||
!(rc == -EOPNOTSUPP && req->length == 0))
|
||||
WARNING(fsg, "error in submission: %s --> %d\n",
|
||||
ep->name, rc);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static bool start_in_transfer(struct fsg_common *common, struct fsg_buffhd *bh)
|
||||
{
|
||||
if (!fsg_is_set(common))
|
||||
return false;
|
||||
start_transfer(common->fsg, common->fsg->bulk_in,
|
||||
bh->inreq, &bh->inreq_busy, &bh->state);
|
||||
bh->state = BUF_STATE_SENDING;
|
||||
if (start_transfer(common->fsg, common->fsg->bulk_in, bh->inreq))
|
||||
bh->state = BUF_STATE_EMPTY;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -607,37 +581,31 @@ static bool start_out_transfer(struct fsg_common *common, struct fsg_buffhd *bh)
|
||||
{
|
||||
if (!fsg_is_set(common))
|
||||
return false;
|
||||
start_transfer(common->fsg, common->fsg->bulk_out,
|
||||
bh->outreq, &bh->outreq_busy, &bh->state);
|
||||
bh->state = BUF_STATE_RECEIVING;
|
||||
if (start_transfer(common->fsg, common->fsg->bulk_out, bh->outreq))
|
||||
bh->state = BUF_STATE_FULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int sleep_thread(struct fsg_common *common, bool can_freeze)
|
||||
static int sleep_thread(struct fsg_common *common, bool can_freeze,
|
||||
struct fsg_buffhd *bh)
|
||||
{
|
||||
int rc = 0;
|
||||
int rc;
|
||||
|
||||
/* Wait until a signal arrives or we are woken up */
|
||||
for (;;) {
|
||||
if (can_freeze)
|
||||
try_to_freeze();
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
if (signal_pending(current)) {
|
||||
rc = -EINTR;
|
||||
break;
|
||||
}
|
||||
if (common->thread_wakeup_needed)
|
||||
break;
|
||||
schedule();
|
||||
}
|
||||
__set_current_state(TASK_RUNNING);
|
||||
common->thread_wakeup_needed = 0;
|
||||
|
||||
/*
|
||||
* Ensure the writing of thread_wakeup_needed
|
||||
* and the reading of bh->state are completed
|
||||
*/
|
||||
smp_mb();
|
||||
return rc;
|
||||
/* Wait until a signal arrives or bh is no longer busy */
|
||||
if (can_freeze)
|
||||
/*
|
||||
* synchronize with the smp_store_release(&bh->state) in
|
||||
* bulk_in_complete() or bulk_out_complete()
|
||||
*/
|
||||
rc = wait_event_freezable(common->io_wait,
|
||||
bh && smp_load_acquire(&bh->state) >=
|
||||
BUF_STATE_EMPTY);
|
||||
else
|
||||
rc = wait_event_interruptible(common->io_wait,
|
||||
bh && smp_load_acquire(&bh->state) >=
|
||||
BUF_STATE_EMPTY);
|
||||
return rc ? -EINTR : 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -697,11 +665,9 @@ static int do_read(struct fsg_common *common)
|
||||
|
||||
/* Wait for the next buffer to become available */
|
||||
bh = common->next_buffhd_to_fill;
|
||||
while (bh->state != BUF_STATE_EMPTY) {
|
||||
rc = sleep_thread(common, false);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
rc = sleep_thread(common, false, bh);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/*
|
||||
* If we were asked to read past the end of file,
|
||||
@@ -878,84 +844,80 @@ static int do_write(struct fsg_common *common)
|
||||
bh = common->next_buffhd_to_drain;
|
||||
if (bh->state == BUF_STATE_EMPTY && !get_some_more)
|
||||
break; /* We stopped early */
|
||||
if (bh->state == BUF_STATE_FULL) {
|
||||
smp_rmb();
|
||||
common->next_buffhd_to_drain = bh->next;
|
||||
bh->state = BUF_STATE_EMPTY;
|
||||
|
||||
/* Did something go wrong with the transfer? */
|
||||
if (bh->outreq->status != 0) {
|
||||
curlun->sense_data = SS_COMMUNICATION_FAILURE;
|
||||
curlun->sense_data_info =
|
||||
file_offset >> curlun->blkbits;
|
||||
curlun->info_valid = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
amount = bh->outreq->actual;
|
||||
if (curlun->file_length - file_offset < amount) {
|
||||
LERROR(curlun,
|
||||
"write %u @ %llu beyond end %llu\n",
|
||||
amount, (unsigned long long)file_offset,
|
||||
(unsigned long long)curlun->file_length);
|
||||
amount = curlun->file_length - file_offset;
|
||||
}
|
||||
|
||||
/* Don't accept excess data. The spec doesn't say
|
||||
* what to do in this case. We'll ignore the error.
|
||||
*/
|
||||
amount = min(amount, bh->bulk_out_intended_length);
|
||||
|
||||
/* Don't write a partial block */
|
||||
amount = round_down(amount, curlun->blksize);
|
||||
if (amount == 0)
|
||||
goto empty_write;
|
||||
|
||||
/* Perform the write */
|
||||
file_offset_tmp = file_offset;
|
||||
nwritten = vfs_write(curlun->filp,
|
||||
(char __user *)bh->buf,
|
||||
amount, &file_offset_tmp);
|
||||
VLDBG(curlun, "file write %u @ %llu -> %d\n", amount,
|
||||
(unsigned long long)file_offset, (int)nwritten);
|
||||
if (signal_pending(current))
|
||||
return -EINTR; /* Interrupted! */
|
||||
|
||||
if (nwritten < 0) {
|
||||
LDBG(curlun, "error in file write: %d\n",
|
||||
(int)nwritten);
|
||||
nwritten = 0;
|
||||
} else if (nwritten < amount) {
|
||||
LDBG(curlun, "partial file write: %d/%u\n",
|
||||
(int)nwritten, amount);
|
||||
nwritten = round_down(nwritten, curlun->blksize);
|
||||
}
|
||||
file_offset += nwritten;
|
||||
amount_left_to_write -= nwritten;
|
||||
common->residue -= nwritten;
|
||||
|
||||
/* If an error occurred, report it and its position */
|
||||
if (nwritten < amount) {
|
||||
curlun->sense_data = SS_WRITE_ERROR;
|
||||
curlun->sense_data_info =
|
||||
file_offset >> curlun->blkbits;
|
||||
curlun->info_valid = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
empty_write:
|
||||
/* Did the host decide to stop early? */
|
||||
if (bh->outreq->actual < bh->bulk_out_intended_length) {
|
||||
common->short_packet_received = 1;
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Wait for something to happen */
|
||||
rc = sleep_thread(common, false);
|
||||
/* Wait for the data to be received */
|
||||
rc = sleep_thread(common, false, bh);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
common->next_buffhd_to_drain = bh->next;
|
||||
bh->state = BUF_STATE_EMPTY;
|
||||
|
||||
/* Did something go wrong with the transfer? */
|
||||
if (bh->outreq->status != 0) {
|
||||
curlun->sense_data = SS_COMMUNICATION_FAILURE;
|
||||
curlun->sense_data_info =
|
||||
file_offset >> curlun->blkbits;
|
||||
curlun->info_valid = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
amount = bh->outreq->actual;
|
||||
if (curlun->file_length - file_offset < amount) {
|
||||
LERROR(curlun, "write %u @ %llu beyond end %llu\n",
|
||||
amount, (unsigned long long)file_offset,
|
||||
(unsigned long long)curlun->file_length);
|
||||
amount = curlun->file_length - file_offset;
|
||||
}
|
||||
|
||||
/*
|
||||
* Don't accept excess data. The spec doesn't say
|
||||
* what to do in this case. We'll ignore the error.
|
||||
*/
|
||||
amount = min(amount, bh->bulk_out_intended_length);
|
||||
|
||||
/* Don't write a partial block */
|
||||
amount = round_down(amount, curlun->blksize);
|
||||
if (amount == 0)
|
||||
goto empty_write;
|
||||
|
||||
/* Perform the write */
|
||||
file_offset_tmp = file_offset;
|
||||
nwritten = vfs_write(curlun->filp, (char __user *)bh->buf,
|
||||
amount, &file_offset_tmp);
|
||||
VLDBG(curlun, "file write %u @ %llu -> %d\n", amount,
|
||||
(unsigned long long)file_offset, (int)nwritten);
|
||||
if (signal_pending(current))
|
||||
return -EINTR; /* Interrupted! */
|
||||
|
||||
if (nwritten < 0) {
|
||||
LDBG(curlun, "error in file write: %d\n",
|
||||
(int) nwritten);
|
||||
nwritten = 0;
|
||||
} else if (nwritten < amount) {
|
||||
LDBG(curlun, "partial file write: %d/%u\n",
|
||||
(int) nwritten, amount);
|
||||
nwritten = round_down(nwritten, curlun->blksize);
|
||||
}
|
||||
file_offset += nwritten;
|
||||
amount_left_to_write -= nwritten;
|
||||
common->residue -= nwritten;
|
||||
|
||||
/* If an error occurred, report it and its position */
|
||||
if (nwritten < amount) {
|
||||
curlun->sense_data = SS_WRITE_ERROR;
|
||||
curlun->sense_data_info =
|
||||
file_offset >> curlun->blkbits;
|
||||
curlun->info_valid = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
empty_write:
|
||||
/* Did the host decide to stop early? */
|
||||
if (bh->outreq->actual < bh->bulk_out_intended_length) {
|
||||
common->short_packet_received = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return -EIO; /* No default reply */
|
||||
@@ -1480,7 +1442,7 @@ static int wedge_bulk_in_endpoint(struct fsg_dev *fsg)
|
||||
|
||||
static int throw_away_data(struct fsg_common *common)
|
||||
{
|
||||
struct fsg_buffhd *bh;
|
||||
struct fsg_buffhd *bh, *bh2;
|
||||
u32 amount;
|
||||
int rc;
|
||||
|
||||
@@ -1488,26 +1450,10 @@ static int throw_away_data(struct fsg_common *common)
|
||||
bh->state != BUF_STATE_EMPTY || common->usb_amount_left > 0;
|
||||
bh = common->next_buffhd_to_drain) {
|
||||
|
||||
/* Throw away the data in a filled buffer */
|
||||
if (bh->state == BUF_STATE_FULL) {
|
||||
smp_rmb();
|
||||
bh->state = BUF_STATE_EMPTY;
|
||||
common->next_buffhd_to_drain = bh->next;
|
||||
|
||||
/* A short packet or an error ends everything */
|
||||
if (bh->outreq->actual < bh->bulk_out_intended_length ||
|
||||
bh->outreq->status != 0) {
|
||||
raise_exception(common,
|
||||
FSG_STATE_ABORT_BULK_OUT);
|
||||
return -EINTR;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Try to submit another request if we need one */
|
||||
bh = common->next_buffhd_to_fill;
|
||||
if (bh->state == BUF_STATE_EMPTY
|
||||
&& common->usb_amount_left > 0) {
|
||||
bh2 = common->next_buffhd_to_fill;
|
||||
if (bh2->state == BUF_STATE_EMPTY &&
|
||||
common->usb_amount_left > 0) {
|
||||
amount = min(common->usb_amount_left, FSG_BUFLEN);
|
||||
|
||||
/*
|
||||
@@ -1515,19 +1461,30 @@ static int throw_away_data(struct fsg_common *common)
|
||||
* equal to the buffer size, which is divisible by
|
||||
* the bulk-out maxpacket size.
|
||||
*/
|
||||
set_bulk_out_req_length(common, bh, amount);
|
||||
if (!start_out_transfer(common, bh))
|
||||
set_bulk_out_req_length(common, bh2, amount);
|
||||
if (!start_out_transfer(common, bh2))
|
||||
/* Dunno what to do if common->fsg is NULL */
|
||||
return -EIO;
|
||||
common->next_buffhd_to_fill = bh->next;
|
||||
common->next_buffhd_to_fill = bh2->next;
|
||||
common->usb_amount_left -= amount;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Otherwise wait for something to happen */
|
||||
rc = sleep_thread(common, true);
|
||||
/* Wait for the data to be received */
|
||||
rc = sleep_thread(common, false, bh);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Throw away the data in a filled buffer */
|
||||
bh->state = BUF_STATE_EMPTY;
|
||||
common->next_buffhd_to_drain = bh->next;
|
||||
|
||||
/* A short packet or an error ends everything */
|
||||
if (bh->outreq->actual < bh->bulk_out_intended_length ||
|
||||
bh->outreq->status != 0) {
|
||||
raise_exception(common, FSG_STATE_ABORT_BULK_OUT);
|
||||
return -EINTR;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -1634,7 +1591,7 @@ static int finish_reply(struct fsg_common *common)
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int send_status(struct fsg_common *common)
|
||||
static void send_status(struct fsg_common *common)
|
||||
{
|
||||
struct fsg_lun *curlun = common->curlun;
|
||||
struct fsg_buffhd *bh;
|
||||
@@ -1645,11 +1602,9 @@ static int send_status(struct fsg_common *common)
|
||||
|
||||
/* Wait for the next buffer to become available */
|
||||
bh = common->next_buffhd_to_fill;
|
||||
while (bh->state != BUF_STATE_EMPTY) {
|
||||
rc = sleep_thread(common, true);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
rc = sleep_thread(common, false, bh);
|
||||
if (rc)
|
||||
return;
|
||||
|
||||
if (curlun) {
|
||||
sd = curlun->sense_data;
|
||||
@@ -1683,10 +1638,10 @@ static int send_status(struct fsg_common *common)
|
||||
bh->inreq->zero = 0;
|
||||
if (!start_in_transfer(common, bh))
|
||||
/* Don't know what to do if common->fsg is NULL */
|
||||
return -EIO;
|
||||
return;
|
||||
|
||||
common->next_buffhd_to_fill = bh->next;
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1848,11 +1803,10 @@ static int do_scsi_command(struct fsg_common *common)
|
||||
/* Wait for the next buffer to become available for data or status */
|
||||
bh = common->next_buffhd_to_fill;
|
||||
common->next_buffhd_to_drain = bh;
|
||||
while (bh->state != BUF_STATE_EMPTY) {
|
||||
rc = sleep_thread(common, true);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
rc = sleep_thread(common, false, bh);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
common->phase_error = 0;
|
||||
common->short_packet_received = 0;
|
||||
|
||||
@@ -2195,11 +2149,9 @@ static int get_next_command(struct fsg_common *common)
|
||||
|
||||
/* Wait for the next buffer to become available */
|
||||
bh = common->next_buffhd_to_fill;
|
||||
while (bh->state != BUF_STATE_EMPTY) {
|
||||
rc = sleep_thread(common, true);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
rc = sleep_thread(common, true, bh);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Queue a request to read a Bulk-only CBW */
|
||||
set_bulk_out_req_length(common, bh, US_BULK_CB_WRAP_LEN);
|
||||
@@ -2214,12 +2166,10 @@ static int get_next_command(struct fsg_common *common)
|
||||
*/
|
||||
|
||||
/* Wait for the CBW to arrive */
|
||||
while (bh->state != BUF_STATE_FULL) {
|
||||
rc = sleep_thread(common, true);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
smp_rmb();
|
||||
rc = sleep_thread(common, true, bh);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = fsg_is_set(common) ? received_cbw(common->fsg, bh) : -EIO;
|
||||
bh->state = BUF_STATE_EMPTY;
|
||||
|
||||
@@ -2371,9 +2321,11 @@ static void handle_exception(struct fsg_common *common)
|
||||
if (!sig)
|
||||
break;
|
||||
if (sig != SIGUSR1) {
|
||||
spin_lock_irq(&common->lock);
|
||||
if (common->state < FSG_STATE_EXIT)
|
||||
DBG(common, "Main thread exiting on signal\n");
|
||||
raise_exception(common, FSG_STATE_EXIT);
|
||||
common->state = FSG_STATE_EXIT;
|
||||
spin_unlock_irq(&common->lock);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2381,23 +2333,14 @@ static void handle_exception(struct fsg_common *common)
|
||||
if (likely(common->fsg)) {
|
||||
for (i = 0; i < common->fsg_num_buffers; ++i) {
|
||||
bh = &common->buffhds[i];
|
||||
if (bh->inreq_busy)
|
||||
if (bh->state == BUF_STATE_SENDING)
|
||||
usb_ep_dequeue(common->fsg->bulk_in, bh->inreq);
|
||||
if (bh->outreq_busy)
|
||||
if (bh->state == BUF_STATE_RECEIVING)
|
||||
usb_ep_dequeue(common->fsg->bulk_out,
|
||||
bh->outreq);
|
||||
}
|
||||
|
||||
/* Wait until everything is idle */
|
||||
for (;;) {
|
||||
int num_active = 0;
|
||||
for (i = 0; i < common->fsg_num_buffers; ++i) {
|
||||
bh = &common->buffhds[i];
|
||||
num_active += bh->inreq_busy + bh->outreq_busy;
|
||||
}
|
||||
if (num_active == 0)
|
||||
break;
|
||||
if (sleep_thread(common, true))
|
||||
/* Wait for a transfer to become idle */
|
||||
if (sleep_thread(common, false, bh))
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2422,10 +2365,9 @@ static void handle_exception(struct fsg_common *common)
|
||||
common->next_buffhd_to_drain = &common->buffhds[0];
|
||||
exception_req_tag = common->exception_req_tag;
|
||||
old_state = common->state;
|
||||
common->state = FSG_STATE_NORMAL;
|
||||
|
||||
if (old_state == FSG_STATE_ABORT_BULK_OUT)
|
||||
common->state = FSG_STATE_STATUS_PHASE;
|
||||
else {
|
||||
if (old_state != FSG_STATE_ABORT_BULK_OUT) {
|
||||
for (i = 0; i < ARRAY_SIZE(common->luns); ++i) {
|
||||
curlun = common->luns[i];
|
||||
if (!curlun)
|
||||
@@ -2436,21 +2378,19 @@ static void handle_exception(struct fsg_common *common)
|
||||
curlun->sense_data_info = 0;
|
||||
curlun->info_valid = 0;
|
||||
}
|
||||
common->state = FSG_STATE_IDLE;
|
||||
}
|
||||
spin_unlock_irq(&common->lock);
|
||||
|
||||
/* Carry out any extra actions required for the exception */
|
||||
switch (old_state) {
|
||||
case FSG_STATE_ABORT_BULK_OUT:
|
||||
send_status(common);
|
||||
spin_lock_irq(&common->lock);
|
||||
if (common->state == FSG_STATE_STATUS_PHASE)
|
||||
common->state = FSG_STATE_IDLE;
|
||||
spin_unlock_irq(&common->lock);
|
||||
case FSG_STATE_NORMAL:
|
||||
break;
|
||||
|
||||
case FSG_STATE_RESET:
|
||||
case FSG_STATE_ABORT_BULK_OUT:
|
||||
send_status(common);
|
||||
break;
|
||||
|
||||
case FSG_STATE_PROTOCOL_RESET:
|
||||
/*
|
||||
* In case we were forced against our will to halt a
|
||||
* bulk endpoint, clear the halt now. (The SuperH UDC
|
||||
@@ -2483,19 +2423,13 @@ static void handle_exception(struct fsg_common *common)
|
||||
break;
|
||||
|
||||
case FSG_STATE_EXIT:
|
||||
case FSG_STATE_TERMINATED:
|
||||
do_set_interface(common, NULL); /* Free resources */
|
||||
spin_lock_irq(&common->lock);
|
||||
common->state = FSG_STATE_TERMINATED; /* Stop the thread */
|
||||
spin_unlock_irq(&common->lock);
|
||||
break;
|
||||
|
||||
case FSG_STATE_INTERFACE_CHANGE:
|
||||
case FSG_STATE_DISCONNECT:
|
||||
case FSG_STATE_COMMAND_PHASE:
|
||||
case FSG_STATE_DATA_PHASE:
|
||||
case FSG_STATE_STATUS_PHASE:
|
||||
case FSG_STATE_IDLE:
|
||||
case FSG_STATE_TERMINATED:
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -2534,33 +2468,17 @@ static int fsg_main_thread(void *common_)
|
||||
}
|
||||
|
||||
if (!common->running) {
|
||||
sleep_thread(common, true);
|
||||
sleep_thread(common, true, NULL);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (get_next_command(common))
|
||||
if (get_next_command(common) || exception_in_progress(common))
|
||||
continue;
|
||||
|
||||
spin_lock_irq(&common->lock);
|
||||
if (!exception_in_progress(common))
|
||||
common->state = FSG_STATE_DATA_PHASE;
|
||||
spin_unlock_irq(&common->lock);
|
||||
|
||||
if (do_scsi_command(common) || finish_reply(common))
|
||||
if (do_scsi_command(common) || exception_in_progress(common))
|
||||
continue;
|
||||
|
||||
spin_lock_irq(&common->lock);
|
||||
if (!exception_in_progress(common))
|
||||
common->state = FSG_STATE_STATUS_PHASE;
|
||||
spin_unlock_irq(&common->lock);
|
||||
|
||||
if (send_status(common))
|
||||
if (finish_reply(common) || exception_in_progress(common))
|
||||
continue;
|
||||
|
||||
spin_lock_irq(&common->lock);
|
||||
if (!exception_in_progress(common))
|
||||
common->state = FSG_STATE_IDLE;
|
||||
spin_unlock_irq(&common->lock);
|
||||
send_status(common);
|
||||
}
|
||||
|
||||
spin_lock_irq(&common->lock);
|
||||
@@ -2680,6 +2598,7 @@ static struct fsg_common *fsg_common_setup(struct fsg_common *common)
|
||||
spin_lock_init(&common->lock);
|
||||
kref_init(&common->ref);
|
||||
init_completion(&common->thread_notifier);
|
||||
init_waitqueue_head(&common->io_wait);
|
||||
init_waitqueue_head(&common->fsg_wait);
|
||||
common->state = FSG_STATE_TERMINATED;
|
||||
memset(common->luns, 0, sizeof(common->luns));
|
||||
@@ -2981,7 +2900,6 @@ static void fsg_common_release(struct kref *ref)
|
||||
if (common->state != FSG_STATE_TERMINATED) {
|
||||
raise_exception(common, FSG_STATE_EXIT);
|
||||
wait_for_completion(&common->thread_notifier);
|
||||
common->thread_task = NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(common->luns); ++i) {
|
||||
@@ -3030,11 +2948,11 @@ static int fsg_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
}
|
||||
|
||||
if (!common->thread_task) {
|
||||
common->state = FSG_STATE_IDLE;
|
||||
common->state = FSG_STATE_NORMAL;
|
||||
common->thread_task =
|
||||
kthread_create(fsg_main_thread, common, "file-storage");
|
||||
if (IS_ERR(common->thread_task)) {
|
||||
int ret = PTR_ERR(common->thread_task);
|
||||
ret = PTR_ERR(common->thread_task);
|
||||
common->thread_task = NULL;
|
||||
common->state = FSG_STATE_TERMINATED;
|
||||
return ret;
|
||||
|
تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
Diff را بارگزاری کن
1021
drivers/usb/gadget/function/f_uac1_legacy.c
Normal file
1021
drivers/usb/gadget/function/f_uac1_legacy.c
Normal file
تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
Diff را بارگزاری کن
تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
Diff را بارگزاری کن
@@ -133,9 +133,10 @@ static inline bool fsg_lun_is_open(struct fsg_lun *curlun)
|
||||
#define FSG_MAX_LUNS 16
|
||||
|
||||
enum fsg_buffer_state {
|
||||
BUF_STATE_SENDING = -2,
|
||||
BUF_STATE_RECEIVING,
|
||||
BUF_STATE_EMPTY = 0,
|
||||
BUF_STATE_FULL,
|
||||
BUF_STATE_BUSY
|
||||
BUF_STATE_FULL
|
||||
};
|
||||
|
||||
struct fsg_buffhd {
|
||||
@@ -151,23 +152,14 @@ struct fsg_buffhd {
|
||||
unsigned int bulk_out_intended_length;
|
||||
|
||||
struct usb_request *inreq;
|
||||
int inreq_busy;
|
||||
struct usb_request *outreq;
|
||||
int outreq_busy;
|
||||
};
|
||||
|
||||
enum fsg_state {
|
||||
/* This one isn't used anywhere */
|
||||
FSG_STATE_COMMAND_PHASE = -10,
|
||||
FSG_STATE_DATA_PHASE,
|
||||
FSG_STATE_STATUS_PHASE,
|
||||
|
||||
FSG_STATE_IDLE = 0,
|
||||
FSG_STATE_NORMAL,
|
||||
FSG_STATE_ABORT_BULK_OUT,
|
||||
FSG_STATE_RESET,
|
||||
FSG_STATE_INTERFACE_CHANGE,
|
||||
FSG_STATE_PROTOCOL_RESET,
|
||||
FSG_STATE_CONFIG_CHANGE,
|
||||
FSG_STATE_DISCONNECT,
|
||||
FSG_STATE_EXIT,
|
||||
FSG_STATE_TERMINATED
|
||||
};
|
||||
|
662
drivers/usb/gadget/function/u_audio.c
Normal file
662
drivers/usb/gadget/function/u_audio.c
Normal file
@@ -0,0 +1,662 @@
|
||||
/*
|
||||
* u_audio.c -- interface to USB gadget "ALSA sound card" utilities
|
||||
*
|
||||
* Copyright (C) 2016
|
||||
* Author: Ruslan Bilovol <ruslan.bilovol@gmail.com>
|
||||
*
|
||||
* Sound card implementation was cut-and-pasted with changes
|
||||
* from f_uac2.c and has:
|
||||
* Copyright (C) 2011
|
||||
* Yadwinder Singh (yadi.brar01@gmail.com)
|
||||
* Jaswinder Singh (jaswinder.singh@linaro.org)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
|
||||
#include "u_audio.h"
|
||||
|
||||
#define BUFF_SIZE_MAX (PAGE_SIZE * 16)
|
||||
#define PRD_SIZE_MAX PAGE_SIZE
|
||||
#define MIN_PERIODS 4
|
||||
|
||||
struct uac_req {
|
||||
struct uac_rtd_params *pp; /* parent param */
|
||||
struct usb_request *req;
|
||||
};
|
||||
|
||||
/* Runtime data params for one stream */
|
||||
struct uac_rtd_params {
|
||||
struct snd_uac_chip *uac; /* parent chip */
|
||||
bool ep_enabled; /* if the ep is enabled */
|
||||
/* Size of the ring buffer */
|
||||
size_t dma_bytes;
|
||||
unsigned char *dma_area;
|
||||
|
||||
struct snd_pcm_substream *ss;
|
||||
|
||||
/* Ring buffer */
|
||||
ssize_t hw_ptr;
|
||||
|
||||
void *rbuf;
|
||||
|
||||
size_t period_size;
|
||||
|
||||
unsigned max_psize; /* MaxPacketSize of endpoint */
|
||||
struct uac_req *ureq;
|
||||
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
struct snd_uac_chip {
|
||||
struct g_audio *audio_dev;
|
||||
|
||||
struct uac_rtd_params p_prm;
|
||||
struct uac_rtd_params c_prm;
|
||||
|
||||
struct snd_card *card;
|
||||
struct snd_pcm *pcm;
|
||||
|
||||
/* timekeeping for the playback endpoint */
|
||||
unsigned int p_interval;
|
||||
unsigned int p_residue;
|
||||
|
||||
/* pre-calculated values for playback iso completion */
|
||||
unsigned int p_pktsize;
|
||||
unsigned int p_pktsize_residue;
|
||||
unsigned int p_framesize;
|
||||
};
|
||||
|
||||
static struct snd_pcm_hardware uac_pcm_hardware = {
|
||||
.info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER
|
||||
| SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID
|
||||
| SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
|
||||
.rates = SNDRV_PCM_RATE_CONTINUOUS,
|
||||
.periods_max = BUFF_SIZE_MAX / PRD_SIZE_MAX,
|
||||
.buffer_bytes_max = BUFF_SIZE_MAX,
|
||||
.period_bytes_max = PRD_SIZE_MAX,
|
||||
.periods_min = MIN_PERIODS,
|
||||
};
|
||||
|
||||
static void u_audio_iso_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
unsigned pending;
|
||||
unsigned long flags;
|
||||
unsigned int hw_ptr;
|
||||
bool update_alsa = false;
|
||||
int status = req->status;
|
||||
struct uac_req *ur = req->context;
|
||||
struct snd_pcm_substream *substream;
|
||||
struct uac_rtd_params *prm = ur->pp;
|
||||
struct snd_uac_chip *uac = prm->uac;
|
||||
|
||||
/* i/f shutting down */
|
||||
if (!prm->ep_enabled || req->status == -ESHUTDOWN)
|
||||
return;
|
||||
|
||||
/*
|
||||
* We can't really do much about bad xfers.
|
||||
* Afterall, the ISOCH xfers could fail legitimately.
|
||||
*/
|
||||
if (status)
|
||||
pr_debug("%s: iso_complete status(%d) %d/%d\n",
|
||||
__func__, status, req->actual, req->length);
|
||||
|
||||
substream = prm->ss;
|
||||
|
||||
/* Do nothing if ALSA isn't active */
|
||||
if (!substream)
|
||||
goto exit;
|
||||
|
||||
spin_lock_irqsave(&prm->lock, flags);
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
/*
|
||||
* For each IN packet, take the quotient of the current data
|
||||
* rate and the endpoint's interval as the base packet size.
|
||||
* If there is a residue from this division, add it to the
|
||||
* residue accumulator.
|
||||
*/
|
||||
req->length = uac->p_pktsize;
|
||||
uac->p_residue += uac->p_pktsize_residue;
|
||||
|
||||
/*
|
||||
* Whenever there are more bytes in the accumulator than we
|
||||
* need to add one more sample frame, increase this packet's
|
||||
* size and decrease the accumulator.
|
||||
*/
|
||||
if (uac->p_residue / uac->p_interval >= uac->p_framesize) {
|
||||
req->length += uac->p_framesize;
|
||||
uac->p_residue -= uac->p_framesize *
|
||||
uac->p_interval;
|
||||
}
|
||||
|
||||
req->actual = req->length;
|
||||
}
|
||||
|
||||
pending = prm->hw_ptr % prm->period_size;
|
||||
pending += req->actual;
|
||||
if (pending >= prm->period_size)
|
||||
update_alsa = true;
|
||||
|
||||
hw_ptr = prm->hw_ptr;
|
||||
prm->hw_ptr = (prm->hw_ptr + req->actual) % prm->dma_bytes;
|
||||
|
||||
spin_unlock_irqrestore(&prm->lock, flags);
|
||||
|
||||
/* Pack USB load in ALSA ring buffer */
|
||||
pending = prm->dma_bytes - hw_ptr;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
if (unlikely(pending < req->actual)) {
|
||||
memcpy(req->buf, prm->dma_area + hw_ptr, pending);
|
||||
memcpy(req->buf + pending, prm->dma_area,
|
||||
req->actual - pending);
|
||||
} else {
|
||||
memcpy(req->buf, prm->dma_area + hw_ptr, req->actual);
|
||||
}
|
||||
} else {
|
||||
if (unlikely(pending < req->actual)) {
|
||||
memcpy(prm->dma_area + hw_ptr, req->buf, pending);
|
||||
memcpy(prm->dma_area, req->buf + pending,
|
||||
req->actual - pending);
|
||||
} else {
|
||||
memcpy(prm->dma_area + hw_ptr, req->buf, req->actual);
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
if (usb_ep_queue(ep, req, GFP_ATOMIC))
|
||||
dev_err(uac->card->dev, "%d Error!\n", __LINE__);
|
||||
|
||||
if (update_alsa)
|
||||
snd_pcm_period_elapsed(substream);
|
||||
}
|
||||
|
||||
static int uac_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
struct snd_uac_chip *uac = snd_pcm_substream_chip(substream);
|
||||
struct uac_rtd_params *prm;
|
||||
struct g_audio *audio_dev;
|
||||
struct uac_params *params;
|
||||
unsigned long flags;
|
||||
int err = 0;
|
||||
|
||||
audio_dev = uac->audio_dev;
|
||||
params = &audio_dev->params;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
prm = &uac->p_prm;
|
||||
else
|
||||
prm = &uac->c_prm;
|
||||
|
||||
spin_lock_irqsave(&prm->lock, flags);
|
||||
|
||||
/* Reset */
|
||||
prm->hw_ptr = 0;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
prm->ss = substream;
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
prm->ss = NULL;
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&prm->lock, flags);
|
||||
|
||||
/* Clear buffer after Play stops */
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && !prm->ss)
|
||||
memset(prm->rbuf, 0, prm->max_psize * params->req_number);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t uac_pcm_pointer(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_uac_chip *uac = snd_pcm_substream_chip(substream);
|
||||
struct uac_rtd_params *prm;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
prm = &uac->p_prm;
|
||||
else
|
||||
prm = &uac->c_prm;
|
||||
|
||||
return bytes_to_frames(substream->runtime, prm->hw_ptr);
|
||||
}
|
||||
|
||||
static int uac_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
struct snd_uac_chip *uac = snd_pcm_substream_chip(substream);
|
||||
struct uac_rtd_params *prm;
|
||||
int err;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
prm = &uac->p_prm;
|
||||
else
|
||||
prm = &uac->c_prm;
|
||||
|
||||
err = snd_pcm_lib_malloc_pages(substream,
|
||||
params_buffer_bytes(hw_params));
|
||||
if (err >= 0) {
|
||||
prm->dma_bytes = substream->runtime->dma_bytes;
|
||||
prm->dma_area = substream->runtime->dma_area;
|
||||
prm->period_size = params_period_bytes(hw_params);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int uac_pcm_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_uac_chip *uac = snd_pcm_substream_chip(substream);
|
||||
struct uac_rtd_params *prm;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
prm = &uac->p_prm;
|
||||
else
|
||||
prm = &uac->c_prm;
|
||||
|
||||
prm->dma_area = NULL;
|
||||
prm->dma_bytes = 0;
|
||||
prm->period_size = 0;
|
||||
|
||||
return snd_pcm_lib_free_pages(substream);
|
||||
}
|
||||
|
||||
static int uac_pcm_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_uac_chip *uac = snd_pcm_substream_chip(substream);
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct g_audio *audio_dev;
|
||||
struct uac_params *params;
|
||||
int p_ssize, c_ssize;
|
||||
int p_srate, c_srate;
|
||||
int p_chmask, c_chmask;
|
||||
|
||||
audio_dev = uac->audio_dev;
|
||||
params = &audio_dev->params;
|
||||
p_ssize = params->p_ssize;
|
||||
c_ssize = params->c_ssize;
|
||||
p_srate = params->p_srate;
|
||||
c_srate = params->c_srate;
|
||||
p_chmask = params->p_chmask;
|
||||
c_chmask = params->c_chmask;
|
||||
uac->p_residue = 0;
|
||||
|
||||
runtime->hw = uac_pcm_hardware;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
spin_lock_init(&uac->p_prm.lock);
|
||||
runtime->hw.rate_min = p_srate;
|
||||
switch (p_ssize) {
|
||||
case 3:
|
||||
runtime->hw.formats = SNDRV_PCM_FMTBIT_S24_3LE;
|
||||
break;
|
||||
case 4:
|
||||
runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE;
|
||||
break;
|
||||
default:
|
||||
runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE;
|
||||
break;
|
||||
}
|
||||
runtime->hw.channels_min = num_channels(p_chmask);
|
||||
runtime->hw.period_bytes_min = 2 * uac->p_prm.max_psize
|
||||
/ runtime->hw.periods_min;
|
||||
} else {
|
||||
spin_lock_init(&uac->c_prm.lock);
|
||||
runtime->hw.rate_min = c_srate;
|
||||
switch (c_ssize) {
|
||||
case 3:
|
||||
runtime->hw.formats = SNDRV_PCM_FMTBIT_S24_3LE;
|
||||
break;
|
||||
case 4:
|
||||
runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE;
|
||||
break;
|
||||
default:
|
||||
runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE;
|
||||
break;
|
||||
}
|
||||
runtime->hw.channels_min = num_channels(c_chmask);
|
||||
runtime->hw.period_bytes_min = 2 * uac->c_prm.max_psize
|
||||
/ runtime->hw.periods_min;
|
||||
}
|
||||
|
||||
runtime->hw.rate_max = runtime->hw.rate_min;
|
||||
runtime->hw.channels_max = runtime->hw.channels_min;
|
||||
|
||||
snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ALSA cries without these function pointers */
|
||||
static int uac_pcm_null(struct snd_pcm_substream *substream)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_pcm_ops uac_pcm_ops = {
|
||||
.open = uac_pcm_open,
|
||||
.close = uac_pcm_null,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = uac_pcm_hw_params,
|
||||
.hw_free = uac_pcm_hw_free,
|
||||
.trigger = uac_pcm_trigger,
|
||||
.pointer = uac_pcm_pointer,
|
||||
.prepare = uac_pcm_null,
|
||||
};
|
||||
|
||||
static inline void free_ep(struct uac_rtd_params *prm, struct usb_ep *ep)
|
||||
{
|
||||
struct snd_uac_chip *uac = prm->uac;
|
||||
struct g_audio *audio_dev;
|
||||
struct uac_params *params;
|
||||
int i;
|
||||
|
||||
if (!prm->ep_enabled)
|
||||
return;
|
||||
|
||||
prm->ep_enabled = false;
|
||||
|
||||
audio_dev = uac->audio_dev;
|
||||
params = &audio_dev->params;
|
||||
|
||||
for (i = 0; i < params->req_number; i++) {
|
||||
if (prm->ureq[i].req) {
|
||||
usb_ep_dequeue(ep, prm->ureq[i].req);
|
||||
usb_ep_free_request(ep, prm->ureq[i].req);
|
||||
prm->ureq[i].req = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (usb_ep_disable(ep))
|
||||
dev_err(uac->card->dev, "%s:%d Error!\n", __func__, __LINE__);
|
||||
}
|
||||
|
||||
|
||||
int u_audio_start_capture(struct g_audio *audio_dev)
|
||||
{
|
||||
struct snd_uac_chip *uac = audio_dev->uac;
|
||||
struct usb_gadget *gadget = audio_dev->gadget;
|
||||
struct device *dev = &gadget->dev;
|
||||
struct usb_request *req;
|
||||
struct usb_ep *ep;
|
||||
struct uac_rtd_params *prm;
|
||||
struct uac_params *params = &audio_dev->params;
|
||||
int req_len, i;
|
||||
|
||||
ep = audio_dev->out_ep;
|
||||
prm = &uac->c_prm;
|
||||
config_ep_by_speed(gadget, &audio_dev->func, ep);
|
||||
req_len = prm->max_psize;
|
||||
|
||||
prm->ep_enabled = true;
|
||||
usb_ep_enable(ep);
|
||||
|
||||
for (i = 0; i < params->req_number; i++) {
|
||||
if (!prm->ureq[i].req) {
|
||||
req = usb_ep_alloc_request(ep, GFP_ATOMIC);
|
||||
if (req == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
prm->ureq[i].req = req;
|
||||
prm->ureq[i].pp = prm;
|
||||
|
||||
req->zero = 0;
|
||||
req->context = &prm->ureq[i];
|
||||
req->length = req_len;
|
||||
req->complete = u_audio_iso_complete;
|
||||
req->buf = prm->rbuf + i * prm->max_psize;
|
||||
}
|
||||
|
||||
if (usb_ep_queue(ep, prm->ureq[i].req, GFP_ATOMIC))
|
||||
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(u_audio_start_capture);
|
||||
|
||||
void u_audio_stop_capture(struct g_audio *audio_dev)
|
||||
{
|
||||
struct snd_uac_chip *uac = audio_dev->uac;
|
||||
|
||||
free_ep(&uac->c_prm, audio_dev->out_ep);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(u_audio_stop_capture);
|
||||
|
||||
int u_audio_start_playback(struct g_audio *audio_dev)
|
||||
{
|
||||
struct snd_uac_chip *uac = audio_dev->uac;
|
||||
struct usb_gadget *gadget = audio_dev->gadget;
|
||||
struct device *dev = &gadget->dev;
|
||||
struct usb_request *req;
|
||||
struct usb_ep *ep;
|
||||
struct uac_rtd_params *prm;
|
||||
struct uac_params *params = &audio_dev->params;
|
||||
unsigned int factor, rate;
|
||||
const struct usb_endpoint_descriptor *ep_desc;
|
||||
int req_len, i;
|
||||
|
||||
ep = audio_dev->in_ep;
|
||||
prm = &uac->p_prm;
|
||||
config_ep_by_speed(gadget, &audio_dev->func, ep);
|
||||
|
||||
ep_desc = ep->desc;
|
||||
|
||||
/* pre-calculate the playback endpoint's interval */
|
||||
if (gadget->speed == USB_SPEED_FULL)
|
||||
factor = 1000;
|
||||
else
|
||||
factor = 8000;
|
||||
|
||||
/* pre-compute some values for iso_complete() */
|
||||
uac->p_framesize = params->p_ssize *
|
||||
num_channels(params->p_chmask);
|
||||
rate = params->p_srate * uac->p_framesize;
|
||||
uac->p_interval = factor / (1 << (ep_desc->bInterval - 1));
|
||||
uac->p_pktsize = min_t(unsigned int, rate / uac->p_interval,
|
||||
prm->max_psize);
|
||||
|
||||
if (uac->p_pktsize < prm->max_psize)
|
||||
uac->p_pktsize_residue = rate % uac->p_interval;
|
||||
else
|
||||
uac->p_pktsize_residue = 0;
|
||||
|
||||
req_len = uac->p_pktsize;
|
||||
uac->p_residue = 0;
|
||||
|
||||
prm->ep_enabled = true;
|
||||
usb_ep_enable(ep);
|
||||
|
||||
for (i = 0; i < params->req_number; i++) {
|
||||
if (!prm->ureq[i].req) {
|
||||
req = usb_ep_alloc_request(ep, GFP_ATOMIC);
|
||||
if (req == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
prm->ureq[i].req = req;
|
||||
prm->ureq[i].pp = prm;
|
||||
|
||||
req->zero = 0;
|
||||
req->context = &prm->ureq[i];
|
||||
req->length = req_len;
|
||||
req->complete = u_audio_iso_complete;
|
||||
req->buf = prm->rbuf + i * prm->max_psize;
|
||||
}
|
||||
|
||||
if (usb_ep_queue(ep, prm->ureq[i].req, GFP_ATOMIC))
|
||||
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(u_audio_start_playback);
|
||||
|
||||
void u_audio_stop_playback(struct g_audio *audio_dev)
|
||||
{
|
||||
struct snd_uac_chip *uac = audio_dev->uac;
|
||||
|
||||
free_ep(&uac->p_prm, audio_dev->in_ep);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(u_audio_stop_playback);
|
||||
|
||||
int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
|
||||
const char *card_name)
|
||||
{
|
||||
struct snd_uac_chip *uac;
|
||||
struct snd_card *card;
|
||||
struct snd_pcm *pcm;
|
||||
struct uac_params *params;
|
||||
int p_chmask, c_chmask;
|
||||
int err;
|
||||
|
||||
if (!g_audio)
|
||||
return -EINVAL;
|
||||
|
||||
uac = kzalloc(sizeof(*uac), GFP_KERNEL);
|
||||
if (!uac)
|
||||
return -ENOMEM;
|
||||
g_audio->uac = uac;
|
||||
uac->audio_dev = g_audio;
|
||||
|
||||
params = &g_audio->params;
|
||||
p_chmask = params->p_chmask;
|
||||
c_chmask = params->c_chmask;
|
||||
|
||||
if (c_chmask) {
|
||||
struct uac_rtd_params *prm = &uac->c_prm;
|
||||
|
||||
uac->c_prm.uac = uac;
|
||||
prm->max_psize = g_audio->out_ep_maxpsize;
|
||||
|
||||
prm->ureq = kcalloc(params->req_number, sizeof(struct uac_req),
|
||||
GFP_KERNEL);
|
||||
if (!prm->ureq) {
|
||||
err = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
prm->rbuf = kcalloc(params->req_number, prm->max_psize,
|
||||
GFP_KERNEL);
|
||||
if (!prm->rbuf) {
|
||||
prm->max_psize = 0;
|
||||
err = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (p_chmask) {
|
||||
struct uac_rtd_params *prm = &uac->p_prm;
|
||||
|
||||
uac->p_prm.uac = uac;
|
||||
prm->max_psize = g_audio->in_ep_maxpsize;
|
||||
|
||||
prm->ureq = kcalloc(params->req_number, sizeof(struct uac_req),
|
||||
GFP_KERNEL);
|
||||
if (!prm->ureq) {
|
||||
err = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
prm->rbuf = kcalloc(params->req_number, prm->max_psize,
|
||||
GFP_KERNEL);
|
||||
if (!prm->rbuf) {
|
||||
prm->max_psize = 0;
|
||||
err = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* Choose any slot, with no id */
|
||||
err = snd_card_new(&g_audio->gadget->dev,
|
||||
-1, NULL, THIS_MODULE, 0, &card);
|
||||
if (err < 0)
|
||||
goto fail;
|
||||
|
||||
uac->card = card;
|
||||
|
||||
/*
|
||||
* Create first PCM device
|
||||
* Create a substream only for non-zero channel streams
|
||||
*/
|
||||
err = snd_pcm_new(uac->card, pcm_name, 0,
|
||||
p_chmask ? 1 : 0, c_chmask ? 1 : 0, &pcm);
|
||||
if (err < 0)
|
||||
goto snd_fail;
|
||||
|
||||
strcpy(pcm->name, pcm_name);
|
||||
pcm->private_data = uac;
|
||||
uac->pcm = pcm;
|
||||
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &uac_pcm_ops);
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &uac_pcm_ops);
|
||||
|
||||
strcpy(card->driver, card_name);
|
||||
strcpy(card->shortname, card_name);
|
||||
sprintf(card->longname, "%s %i", card_name, card->dev->id);
|
||||
|
||||
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
|
||||
snd_dma_continuous_data(GFP_KERNEL), 0, BUFF_SIZE_MAX);
|
||||
|
||||
err = snd_card_register(card);
|
||||
|
||||
if (!err)
|
||||
return 0;
|
||||
|
||||
snd_fail:
|
||||
snd_card_free(card);
|
||||
fail:
|
||||
kfree(uac->p_prm.ureq);
|
||||
kfree(uac->c_prm.ureq);
|
||||
kfree(uac->p_prm.rbuf);
|
||||
kfree(uac->c_prm.rbuf);
|
||||
kfree(uac);
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(g_audio_setup);
|
||||
|
||||
void g_audio_cleanup(struct g_audio *g_audio)
|
||||
{
|
||||
struct snd_uac_chip *uac;
|
||||
struct snd_card *card;
|
||||
|
||||
if (!g_audio || !g_audio->uac)
|
||||
return;
|
||||
|
||||
uac = g_audio->uac;
|
||||
card = uac->card;
|
||||
if (card)
|
||||
snd_card_free(card);
|
||||
|
||||
kfree(uac->p_prm.ureq);
|
||||
kfree(uac->c_prm.ureq);
|
||||
kfree(uac->p_prm.rbuf);
|
||||
kfree(uac->c_prm.rbuf);
|
||||
kfree(uac);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(g_audio_cleanup);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("USB gadget \"ALSA sound card\" utilities");
|
||||
MODULE_AUTHOR("Ruslan Bilovol");
|
95
drivers/usb/gadget/function/u_audio.h
Normal file
95
drivers/usb/gadget/function/u_audio.h
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* u_audio.h -- interface to USB gadget "ALSA sound card" utilities
|
||||
*
|
||||
* Copyright (C) 2016
|
||||
* Author: Ruslan Bilovol <ruslan.bilovol@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __U_AUDIO_H
|
||||
#define __U_AUDIO_H
|
||||
|
||||
#include <linux/usb/composite.h>
|
||||
|
||||
struct uac_params {
|
||||
/* playback */
|
||||
int p_chmask; /* channel mask */
|
||||
int p_srate; /* rate in Hz */
|
||||
int p_ssize; /* sample size */
|
||||
|
||||
/* capture */
|
||||
int c_chmask; /* channel mask */
|
||||
int c_srate; /* rate in Hz */
|
||||
int c_ssize; /* sample size */
|
||||
|
||||
int req_number; /* number of preallocated requests */
|
||||
};
|
||||
|
||||
struct g_audio {
|
||||
struct usb_function func;
|
||||
struct usb_gadget *gadget;
|
||||
|
||||
struct usb_ep *in_ep;
|
||||
struct usb_ep *out_ep;
|
||||
|
||||
/* Max packet size for all in_ep possible speeds */
|
||||
unsigned int in_ep_maxpsize;
|
||||
/* Max packet size for all out_ep possible speeds */
|
||||
unsigned int out_ep_maxpsize;
|
||||
|
||||
/* The ALSA Sound Card it represents on the USB-Client side */
|
||||
struct snd_uac_chip *uac;
|
||||
|
||||
struct uac_params params;
|
||||
};
|
||||
|
||||
static inline struct g_audio *func_to_g_audio(struct usb_function *f)
|
||||
{
|
||||
return container_of(f, struct g_audio, func);
|
||||
}
|
||||
|
||||
static inline uint num_channels(uint chanmask)
|
||||
{
|
||||
uint num = 0;
|
||||
|
||||
while (chanmask) {
|
||||
num += (chanmask & 1);
|
||||
chanmask >>= 1;
|
||||
}
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
/*
|
||||
* g_audio_setup - initialize one virtual ALSA sound card
|
||||
* @g_audio: struct with filled params, in_ep_maxpsize, out_ep_maxpsize
|
||||
* @pcm_name: the id string for a PCM instance of this sound card
|
||||
* @card_name: name of this soundcard
|
||||
*
|
||||
* This sets up the single virtual ALSA sound card that may be exported by a
|
||||
* gadget driver using this framework.
|
||||
*
|
||||
* Context: may sleep
|
||||
*
|
||||
* Returns zero on success, or a negative error on failure.
|
||||
*/
|
||||
int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
|
||||
const char *card_name);
|
||||
void g_audio_cleanup(struct g_audio *g_audio);
|
||||
|
||||
int u_audio_start_capture(struct g_audio *g_audio);
|
||||
void u_audio_stop_capture(struct g_audio *g_audio);
|
||||
int u_audio_start_playback(struct g_audio *g_audio);
|
||||
void u_audio_stop_playback(struct g_audio *g_audio);
|
||||
|
||||
#endif /* __U_AUDIO_H */
|
@@ -216,6 +216,9 @@ struct ffs_data {
|
||||
#define FFS_FL_CALL_CLOSED_CALLBACK 0
|
||||
#define FFS_FL_BOUND 1
|
||||
|
||||
/* For waking up blocked threads when function is enabled. */
|
||||
wait_queue_head_t wait;
|
||||
|
||||
/* Active function */
|
||||
struct ffs_function *func;
|
||||
|
||||
|
@@ -1,82 +1,41 @@
|
||||
/*
|
||||
* u_uac1.h -- interface to USB gadget "ALSA AUDIO" utilities
|
||||
* u_uac1.h - Utility definitions for UAC1 function
|
||||
*
|
||||
* Copyright (C) 2008 Bryan Wu <cooloney@kernel.org>
|
||||
* Copyright (C) 2008 Analog Devices, Inc
|
||||
* Copyright (C) 2016 Ruslan Bilovol <ruslan.bilovol@gmail.com>
|
||||
*
|
||||
* Enter bugs at http://blackfin.uclinux.org/
|
||||
*
|
||||
* Licensed under the GPL-2 or later.
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef __U_AUDIO_H
|
||||
#define __U_AUDIO_H
|
||||
#ifndef __U_UAC1_H
|
||||
#define __U_UAC1_H
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/usb/audio.h>
|
||||
#include <linux/usb/composite.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
|
||||
#define FILE_PCM_PLAYBACK "/dev/snd/pcmC0D0p"
|
||||
#define FILE_PCM_CAPTURE "/dev/snd/pcmC0D0c"
|
||||
#define FILE_CONTROL "/dev/snd/controlC0"
|
||||
|
||||
#define UAC1_OUT_EP_MAX_PACKET_SIZE 200
|
||||
#define UAC1_REQ_COUNT 256
|
||||
#define UAC1_AUDIO_BUF_SIZE 48000
|
||||
#define UAC1_DEF_CCHMASK 0x3
|
||||
#define UAC1_DEF_CSRATE 48000
|
||||
#define UAC1_DEF_CSSIZE 2
|
||||
#define UAC1_DEF_PCHMASK 0x3
|
||||
#define UAC1_DEF_PSRATE 48000
|
||||
#define UAC1_DEF_PSSIZE 2
|
||||
#define UAC1_DEF_REQ_NUM 2
|
||||
|
||||
/*
|
||||
* This represents the USB side of an audio card device, managed by a USB
|
||||
* function which provides control and stream interfaces.
|
||||
*/
|
||||
|
||||
struct gaudio_snd_dev {
|
||||
struct gaudio *card;
|
||||
struct file *filp;
|
||||
struct snd_pcm_substream *substream;
|
||||
int access;
|
||||
int format;
|
||||
int channels;
|
||||
int rate;
|
||||
};
|
||||
|
||||
struct gaudio {
|
||||
struct usb_function func;
|
||||
struct usb_gadget *gadget;
|
||||
|
||||
/* ALSA sound device interfaces */
|
||||
struct gaudio_snd_dev control;
|
||||
struct gaudio_snd_dev playback;
|
||||
struct gaudio_snd_dev capture;
|
||||
|
||||
/* TODO */
|
||||
};
|
||||
|
||||
struct f_uac1_opts {
|
||||
struct usb_function_instance func_inst;
|
||||
int req_buf_size;
|
||||
int req_count;
|
||||
int audio_buf_size;
|
||||
char *fn_play;
|
||||
char *fn_cap;
|
||||
char *fn_cntl;
|
||||
int c_chmask;
|
||||
int c_srate;
|
||||
int c_ssize;
|
||||
int p_chmask;
|
||||
int p_srate;
|
||||
int p_ssize;
|
||||
int req_number;
|
||||
unsigned bound:1;
|
||||
unsigned fn_play_alloc:1;
|
||||
unsigned fn_cap_alloc:1;
|
||||
unsigned fn_cntl_alloc:1;
|
||||
|
||||
struct mutex lock;
|
||||
int refcnt;
|
||||
};
|
||||
|
||||
int gaudio_setup(struct gaudio *card);
|
||||
void gaudio_cleanup(struct gaudio *the_card);
|
||||
|
||||
size_t u_audio_playback(struct gaudio *card, void *buf, size_t count);
|
||||
int u_audio_get_playback_channels(struct gaudio *card);
|
||||
int u_audio_get_playback_rate(struct gaudio *card);
|
||||
|
||||
#endif /* __U_AUDIO_H */
|
||||
#endif /* __U_UAC1_H */
|
||||
|
@@ -18,7 +18,7 @@
|
||||
#include <linux/random.h>
|
||||
#include <linux/syscalls.h>
|
||||
|
||||
#include "u_uac1.h"
|
||||
#include "u_uac1_legacy.h"
|
||||
|
||||
/*
|
||||
* This component encapsulates the ALSA devices for USB audio gadget
|
||||
@@ -205,10 +205,11 @@ static int gaudio_open_snd_dev(struct gaudio *card)
|
||||
{
|
||||
struct snd_pcm_file *pcm_file;
|
||||
struct gaudio_snd_dev *snd;
|
||||
struct f_uac1_opts *opts;
|
||||
struct f_uac1_legacy_opts *opts;
|
||||
char *fn_play, *fn_cap, *fn_cntl;
|
||||
|
||||
opts = container_of(card->func.fi, struct f_uac1_opts, func_inst);
|
||||
opts = container_of(card->func.fi, struct f_uac1_legacy_opts,
|
||||
func_inst);
|
||||
fn_play = opts->fn_play;
|
||||
fn_cap = opts->fn_cap;
|
||||
fn_cntl = opts->fn_cntl;
|
82
drivers/usb/gadget/function/u_uac1_legacy.h
Normal file
82
drivers/usb/gadget/function/u_uac1_legacy.h
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* u_uac1.h -- interface to USB gadget "ALSA AUDIO" utilities
|
||||
*
|
||||
* Copyright (C) 2008 Bryan Wu <cooloney@kernel.org>
|
||||
* Copyright (C) 2008 Analog Devices, Inc
|
||||
*
|
||||
* Enter bugs at http://blackfin.uclinux.org/
|
||||
*
|
||||
* Licensed under the GPL-2 or later.
|
||||
*/
|
||||
|
||||
#ifndef __U_UAC1_LEGACY_H
|
||||
#define __U_UAC1_LEGACY_H
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/usb/audio.h>
|
||||
#include <linux/usb/composite.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
|
||||
#define FILE_PCM_PLAYBACK "/dev/snd/pcmC0D0p"
|
||||
#define FILE_PCM_CAPTURE "/dev/snd/pcmC0D0c"
|
||||
#define FILE_CONTROL "/dev/snd/controlC0"
|
||||
|
||||
#define UAC1_OUT_EP_MAX_PACKET_SIZE 200
|
||||
#define UAC1_REQ_COUNT 256
|
||||
#define UAC1_AUDIO_BUF_SIZE 48000
|
||||
|
||||
/*
|
||||
* This represents the USB side of an audio card device, managed by a USB
|
||||
* function which provides control and stream interfaces.
|
||||
*/
|
||||
|
||||
struct gaudio_snd_dev {
|
||||
struct gaudio *card;
|
||||
struct file *filp;
|
||||
struct snd_pcm_substream *substream;
|
||||
int access;
|
||||
int format;
|
||||
int channels;
|
||||
int rate;
|
||||
};
|
||||
|
||||
struct gaudio {
|
||||
struct usb_function func;
|
||||
struct usb_gadget *gadget;
|
||||
|
||||
/* ALSA sound device interfaces */
|
||||
struct gaudio_snd_dev control;
|
||||
struct gaudio_snd_dev playback;
|
||||
struct gaudio_snd_dev capture;
|
||||
|
||||
/* TODO */
|
||||
};
|
||||
|
||||
struct f_uac1_legacy_opts {
|
||||
struct usb_function_instance func_inst;
|
||||
int req_buf_size;
|
||||
int req_count;
|
||||
int audio_buf_size;
|
||||
char *fn_play;
|
||||
char *fn_cap;
|
||||
char *fn_cntl;
|
||||
unsigned bound:1;
|
||||
unsigned fn_play_alloc:1;
|
||||
unsigned fn_cap_alloc:1;
|
||||
unsigned fn_cntl_alloc:1;
|
||||
struct mutex lock;
|
||||
int refcnt;
|
||||
};
|
||||
|
||||
int gaudio_setup(struct gaudio *card);
|
||||
void gaudio_cleanup(struct gaudio *the_card);
|
||||
|
||||
size_t u_audio_playback(struct gaudio *card, void *buf, size_t count);
|
||||
int u_audio_get_playback_channels(struct gaudio *card);
|
||||
int u_audio_get_playback_rate(struct gaudio *card);
|
||||
|
||||
#endif /* __U_UAC1_LEGACY_H */
|
@@ -54,8 +54,10 @@ config USB_AUDIO
|
||||
depends on SND
|
||||
select USB_LIBCOMPOSITE
|
||||
select SND_PCM
|
||||
select USB_F_UAC1 if GADGET_UAC1
|
||||
select USB_F_UAC1 if (GADGET_UAC1 && !GADGET_UAC1_LEGACY)
|
||||
select USB_F_UAC1_LEGACY if (GADGET_UAC1 && GADGET_UAC1_LEGACY)
|
||||
select USB_F_UAC2 if !GADGET_UAC1
|
||||
select USB_U_AUDIO if (USB_F_UAC2 || USB_F_UAC1)
|
||||
help
|
||||
This Gadget Audio driver is compatible with USB Audio Class
|
||||
specification 2.0. It implements 1 AudioControl interface,
|
||||
@@ -73,10 +75,17 @@ config USB_AUDIO
|
||||
dynamically linked module called "g_audio".
|
||||
|
||||
config GADGET_UAC1
|
||||
bool "UAC 1.0 (Legacy)"
|
||||
bool "UAC 1.0"
|
||||
depends on USB_AUDIO
|
||||
help
|
||||
If you instead want older UAC Spec-1.0 driver that also has audio
|
||||
If you instead want older USB Audio Class specification 1.0 support
|
||||
with similar driver capabilities.
|
||||
|
||||
config GADGET_UAC1_LEGACY
|
||||
bool "UAC 1.0 (Legacy)"
|
||||
depends on GADGET_UAC1
|
||||
help
|
||||
If you instead want legacy UAC Spec-1.0 driver that also has audio
|
||||
paths hardwired to the Audio codec chip on-board and doesn't work
|
||||
without one.
|
||||
|
||||
|
@@ -53,8 +53,41 @@ static int c_ssize = UAC2_DEF_CSSIZE;
|
||||
module_param(c_ssize, uint, S_IRUGO);
|
||||
MODULE_PARM_DESC(c_ssize, "Capture Sample Size(bytes)");
|
||||
#else
|
||||
#ifndef CONFIG_GADGET_UAC1_LEGACY
|
||||
#include "u_uac1.h"
|
||||
|
||||
/* Playback(USB-IN) Default Stereo - Fl/Fr */
|
||||
static int p_chmask = UAC1_DEF_PCHMASK;
|
||||
module_param(p_chmask, uint, S_IRUGO);
|
||||
MODULE_PARM_DESC(p_chmask, "Playback Channel Mask");
|
||||
|
||||
/* Playback Default 48 KHz */
|
||||
static int p_srate = UAC1_DEF_PSRATE;
|
||||
module_param(p_srate, uint, S_IRUGO);
|
||||
MODULE_PARM_DESC(p_srate, "Playback Sampling Rate");
|
||||
|
||||
/* Playback Default 16bits/sample */
|
||||
static int p_ssize = UAC1_DEF_PSSIZE;
|
||||
module_param(p_ssize, uint, S_IRUGO);
|
||||
MODULE_PARM_DESC(p_ssize, "Playback Sample Size(bytes)");
|
||||
|
||||
/* Capture(USB-OUT) Default Stereo - Fl/Fr */
|
||||
static int c_chmask = UAC1_DEF_CCHMASK;
|
||||
module_param(c_chmask, uint, S_IRUGO);
|
||||
MODULE_PARM_DESC(c_chmask, "Capture Channel Mask");
|
||||
|
||||
/* Capture Default 48 KHz */
|
||||
static int c_srate = UAC1_DEF_CSRATE;
|
||||
module_param(c_srate, uint, S_IRUGO);
|
||||
MODULE_PARM_DESC(c_srate, "Capture Sampling Rate");
|
||||
|
||||
/* Capture Default 16bits/sample */
|
||||
static int c_ssize = UAC1_DEF_CSSIZE;
|
||||
module_param(c_ssize, uint, S_IRUGO);
|
||||
MODULE_PARM_DESC(c_ssize, "Capture Sample Size(bytes)");
|
||||
#else /* CONFIG_GADGET_UAC1_LEGACY */
|
||||
#include "u_uac1_legacy.h"
|
||||
|
||||
static char *fn_play = FILE_PCM_PLAYBACK;
|
||||
module_param(fn_play, charp, S_IRUGO);
|
||||
MODULE_PARM_DESC(fn_play, "Playback PCM device file name");
|
||||
@@ -78,6 +111,7 @@ MODULE_PARM_DESC(req_count, "ISO OUT endpoint request count");
|
||||
static int audio_buf_size = UAC1_AUDIO_BUF_SIZE;
|
||||
module_param(audio_buf_size, int, S_IRUGO);
|
||||
MODULE_PARM_DESC(audio_buf_size, "Audio buffer size");
|
||||
#endif /* CONFIG_GADGET_UAC1_LEGACY */
|
||||
#endif
|
||||
|
||||
/* string IDs are assigned dynamically */
|
||||
@@ -125,7 +159,7 @@ static struct usb_device_descriptor device_desc = {
|
||||
|
||||
/* .bcdUSB = DYNAMIC */
|
||||
|
||||
#ifdef CONFIG_GADGET_UAC1
|
||||
#ifdef CONFIG_GADGET_UAC1_LEGACY
|
||||
.bDeviceClass = USB_CLASS_PER_INTERFACE,
|
||||
.bDeviceSubClass = 0,
|
||||
.bDeviceProtocol = 0,
|
||||
@@ -207,7 +241,11 @@ static int audio_bind(struct usb_composite_dev *cdev)
|
||||
#ifndef CONFIG_GADGET_UAC1
|
||||
struct f_uac2_opts *uac2_opts;
|
||||
#else
|
||||
#ifndef CONFIG_GADGET_UAC1_LEGACY
|
||||
struct f_uac1_opts *uac1_opts;
|
||||
#else
|
||||
struct f_uac1_legacy_opts *uac1_opts;
|
||||
#endif
|
||||
#endif
|
||||
int status;
|
||||
|
||||
@@ -216,7 +254,11 @@ static int audio_bind(struct usb_composite_dev *cdev)
|
||||
if (IS_ERR(fi_uac2))
|
||||
return PTR_ERR(fi_uac2);
|
||||
#else
|
||||
#ifndef CONFIG_GADGET_UAC1_LEGACY
|
||||
fi_uac1 = usb_get_function_instance("uac1");
|
||||
#else
|
||||
fi_uac1 = usb_get_function_instance("uac1_legacy");
|
||||
#endif
|
||||
if (IS_ERR(fi_uac1))
|
||||
return PTR_ERR(fi_uac1);
|
||||
#endif
|
||||
@@ -231,13 +273,24 @@ static int audio_bind(struct usb_composite_dev *cdev)
|
||||
uac2_opts->c_ssize = c_ssize;
|
||||
uac2_opts->req_number = UAC2_DEF_REQ_NUM;
|
||||
#else
|
||||
#ifndef CONFIG_GADGET_UAC1_LEGACY
|
||||
uac1_opts = container_of(fi_uac1, struct f_uac1_opts, func_inst);
|
||||
uac1_opts->p_chmask = p_chmask;
|
||||
uac1_opts->p_srate = p_srate;
|
||||
uac1_opts->p_ssize = p_ssize;
|
||||
uac1_opts->c_chmask = c_chmask;
|
||||
uac1_opts->c_srate = c_srate;
|
||||
uac1_opts->c_ssize = c_ssize;
|
||||
uac1_opts->req_number = UAC1_DEF_REQ_NUM;
|
||||
#else /* CONFIG_GADGET_UAC1_LEGACY */
|
||||
uac1_opts = container_of(fi_uac1, struct f_uac1_legacy_opts, func_inst);
|
||||
uac1_opts->fn_play = fn_play;
|
||||
uac1_opts->fn_cap = fn_cap;
|
||||
uac1_opts->fn_cntl = fn_cntl;
|
||||
uac1_opts->req_buf_size = req_buf_size;
|
||||
uac1_opts->req_count = req_count;
|
||||
uac1_opts->audio_buf_size = audio_buf_size;
|
||||
#endif /* CONFIG_GADGET_UAC1_LEGACY */
|
||||
#endif
|
||||
|
||||
status = usb_string_ids_tab(cdev, strings_dev);
|
||||
|
@@ -210,7 +210,6 @@ static int msg_bind(struct usb_composite_dev *cdev)
|
||||
usb_composite_overwrite_options(cdev, &coverwrite);
|
||||
dev_info(&cdev->gadget->dev,
|
||||
DRIVER_DESC ", version: " DRIVER_VERSION "\n");
|
||||
set_bit(0, &msg_registered);
|
||||
return 0;
|
||||
|
||||
fail_otg_desc:
|
||||
@@ -257,7 +256,12 @@ MODULE_LICENSE("GPL");
|
||||
|
||||
static int __init msg_init(void)
|
||||
{
|
||||
return usb_composite_probe(&msg_driver);
|
||||
int ret;
|
||||
|
||||
ret = usb_composite_probe(&msg_driver);
|
||||
set_bit(0, &msg_registered);
|
||||
|
||||
return ret;
|
||||
}
|
||||
module_init(msg_init);
|
||||
|
||||
|
@@ -55,7 +55,7 @@ config USB_LPC32XX
|
||||
|
||||
config USB_ATMEL_USBA
|
||||
tristate "Atmel USBA"
|
||||
depends on ((AVR32 && !OF) || ARCH_AT91)
|
||||
depends on ARCH_AT91
|
||||
help
|
||||
USBA is the integrated high-speed USB Device controller on
|
||||
the AT32AP700x, some AT91SAM9 and AT91CAP9 processors from Atmel.
|
||||
@@ -256,7 +256,7 @@ config USB_MV_U3D
|
||||
controller, which support super speed USB peripheral.
|
||||
|
||||
config USB_SNP_CORE
|
||||
depends on USB_AMD5536UDC
|
||||
depends on (USB_AMD5536UDC || USB_SNP_UDC_PLAT)
|
||||
tristate
|
||||
help
|
||||
This enables core driver support for Synopsys USB 2.0 Device
|
||||
@@ -269,6 +269,20 @@ config USB_SNP_CORE
|
||||
This IP is different to the High Speed OTG IP that can be enabled
|
||||
by selecting USB_DWC2 or USB_DWC3 options.
|
||||
|
||||
config USB_SNP_UDC_PLAT
|
||||
tristate "Synopsys USB 2.0 Device controller"
|
||||
depends on (USB_GADGET && OF)
|
||||
select USB_GADGET_DUALSPEED
|
||||
select USB_SNP_CORE
|
||||
default ARCH_BCM_IPROC
|
||||
help
|
||||
This adds Platform Device support for Synopsys Designware core
|
||||
AHB subsystem USB2.0 Device Controller (UDC).
|
||||
|
||||
This driver works with UDCs integrated into Broadcom's Northstar2
|
||||
and Cygnus SoCs.
|
||||
|
||||
If unsure, say N.
|
||||
#
|
||||
# Controllers available in both integrated and discrete versions
|
||||
#
|
||||
|
@@ -10,7 +10,7 @@ obj-$(CONFIG_USB_GADGET) += udc-core.o
|
||||
obj-$(CONFIG_USB_DUMMY_HCD) += dummy_hcd.o
|
||||
obj-$(CONFIG_USB_NET2272) += net2272.o
|
||||
obj-$(CONFIG_USB_NET2280) += net2280.o
|
||||
obj-$(CONFIG_USB_SNP_CORE) += amd5536udc.o
|
||||
obj-$(CONFIG_USB_SNP_CORE) += snps_udc_core.o
|
||||
obj-$(CONFIG_USB_AMD5536UDC) += amd5536udc_pci.o
|
||||
obj-$(CONFIG_USB_PXA25X) += pxa25x_udc.o
|
||||
obj-$(CONFIG_USB_PXA27X) += pxa27x_udc.o
|
||||
@@ -37,4 +37,5 @@ obj-$(CONFIG_USB_FOTG210_UDC) += fotg210-udc.o
|
||||
obj-$(CONFIG_USB_MV_U3D) += mv_u3d_core.o
|
||||
obj-$(CONFIG_USB_GR_UDC) += gr_udc.o
|
||||
obj-$(CONFIG_USB_GADGET_XILINX) += udc-xilinx.o
|
||||
obj-$(CONFIG_USB_SNP_UDC_PLAT) += snps_udc_plat.o
|
||||
obj-$(CONFIG_USB_BDC_UDC) += bdc/
|
||||
|
@@ -16,6 +16,7 @@
|
||||
/* debug control */
|
||||
/* #define UDC_VERBOSE */
|
||||
|
||||
#include <linux/extcon.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
|
||||
@@ -28,6 +29,9 @@
|
||||
#define UDC_HSA0_REV 1
|
||||
#define UDC_HSB1_REV 2
|
||||
|
||||
/* Broadcom chip rev. */
|
||||
#define UDC_BCM_REV 10
|
||||
|
||||
/*
|
||||
* SETUP usb commands
|
||||
* needed, because some SETUP's are handled in hw, but must be passed to
|
||||
@@ -112,6 +116,7 @@
|
||||
#define UDC_DEVCTL_BRLEN_MASK 0x00ff0000
|
||||
#define UDC_DEVCTL_BRLEN_OFS 16
|
||||
|
||||
#define UDC_DEVCTL_SRX_FLUSH 14
|
||||
#define UDC_DEVCTL_CSR_DONE 13
|
||||
#define UDC_DEVCTL_DEVNAK 12
|
||||
#define UDC_DEVCTL_SD 10
|
||||
@@ -563,6 +568,16 @@ struct udc {
|
||||
u16 cur_config;
|
||||
u16 cur_intf;
|
||||
u16 cur_alt;
|
||||
|
||||
/* for platform device and extcon support */
|
||||
struct device *dev;
|
||||
struct phy *udc_phy;
|
||||
struct extcon_dev *edev;
|
||||
struct extcon_specific_cable_nb extcon_nb;
|
||||
struct notifier_block nb;
|
||||
struct delayed_work drd_work;
|
||||
struct workqueue_struct *drd_wq;
|
||||
u32 conn_type;
|
||||
};
|
||||
|
||||
#define to_amd5536_udc(g) (container_of((g), struct udc, gadget))
|
||||
@@ -578,6 +593,7 @@ int udc_enable_dev_setup_interrupts(struct udc *dev);
|
||||
int udc_mask_unused_interrupts(struct udc *dev);
|
||||
irqreturn_t udc_irq(int irq, void *pdev);
|
||||
void gadget_release(struct device *pdev);
|
||||
void empty_req_queue(struct udc_ep *ep);
|
||||
void udc_basic_init(struct udc *dev);
|
||||
void free_dma_pools(struct udc *dev);
|
||||
int init_dma_pools(struct udc *dev);
|
||||
@@ -639,7 +655,7 @@ MODULE_PARM_DESC(use_fullspeed, "true for fullspeed only");
|
||||
|
||||
/* debug macros ------------------------------------------------------------*/
|
||||
|
||||
#define DBG(udc , args...) dev_dbg(&(udc)->pdev->dev, args)
|
||||
#define DBG(udc , args...) dev_dbg(udc->dev, args)
|
||||
|
||||
#ifdef UDC_VERBOSE
|
||||
#define VDBG DBG
|
||||
|
@@ -168,6 +168,7 @@ static int udc_pci_probe(
|
||||
dev->phys_addr = resource;
|
||||
dev->irq = pdev->irq;
|
||||
dev->pdev = pdev;
|
||||
dev->dev = &pdev->dev;
|
||||
|
||||
/* general probing */
|
||||
if (udc_probe(dev)) {
|
||||
|
@@ -152,7 +152,7 @@ static int regs_dbg_open(struct inode *inode, struct file *file)
|
||||
|
||||
spin_lock_irq(&udc->lock);
|
||||
for (i = 0; i < inode->i_size / 4; i++)
|
||||
data[i] = usba_io_readl(udc->regs + i * 4);
|
||||
data[i] = readl_relaxed(udc->regs + i * 4);
|
||||
spin_unlock_irq(&udc->lock);
|
||||
|
||||
file->private_data = data;
|
||||
@@ -1369,7 +1369,7 @@ static int handle_ep0_setup(struct usba_udc *udc, struct usba_ep *ep,
|
||||
if (crq->wLength != cpu_to_le16(sizeof(status)))
|
||||
goto stall;
|
||||
ep->state = DATA_STAGE_IN;
|
||||
usba_io_writew(status, ep->fifo);
|
||||
writew_relaxed(status, ep->fifo);
|
||||
usba_ep_writel(ep, SET_STA, USBA_TX_PK_RDY);
|
||||
break;
|
||||
}
|
||||
|
@@ -43,13 +43,8 @@
|
||||
#define USBA_REMOTE_WAKE_UP (1 << 10)
|
||||
#define USBA_PULLD_DIS (1 << 11)
|
||||
|
||||
#if defined(CONFIG_AVR32)
|
||||
#define USBA_ENABLE_MASK USBA_EN_USBA
|
||||
#define USBA_DISABLE_MASK 0
|
||||
#elif defined(CONFIG_ARCH_AT91)
|
||||
#define USBA_ENABLE_MASK (USBA_EN_USBA | USBA_PULLD_DIS)
|
||||
#define USBA_DISABLE_MASK USBA_DETACH
|
||||
#endif /* CONFIG_ARCH_AT91 */
|
||||
|
||||
/* Bitfields in FNUM */
|
||||
#define USBA_MICRO_FRAME_NUM_OFFSET 0
|
||||
@@ -191,28 +186,18 @@
|
||||
| USBA_BF(name, value))
|
||||
|
||||
/* Register access macros */
|
||||
#ifdef CONFIG_AVR32
|
||||
#define usba_io_readl __raw_readl
|
||||
#define usba_io_writel __raw_writel
|
||||
#define usba_io_writew __raw_writew
|
||||
#else
|
||||
#define usba_io_readl readl_relaxed
|
||||
#define usba_io_writel writel_relaxed
|
||||
#define usba_io_writew writew_relaxed
|
||||
#endif
|
||||
|
||||
#define usba_readl(udc, reg) \
|
||||
usba_io_readl((udc)->regs + USBA_##reg)
|
||||
readl_relaxed((udc)->regs + USBA_##reg)
|
||||
#define usba_writel(udc, reg, value) \
|
||||
usba_io_writel((value), (udc)->regs + USBA_##reg)
|
||||
writel_relaxed((value), (udc)->regs + USBA_##reg)
|
||||
#define usba_ep_readl(ep, reg) \
|
||||
usba_io_readl((ep)->ep_regs + USBA_EPT_##reg)
|
||||
readl_relaxed((ep)->ep_regs + USBA_EPT_##reg)
|
||||
#define usba_ep_writel(ep, reg, value) \
|
||||
usba_io_writel((value), (ep)->ep_regs + USBA_EPT_##reg)
|
||||
writel_relaxed((value), (ep)->ep_regs + USBA_EPT_##reg)
|
||||
#define usba_dma_readl(ep, reg) \
|
||||
usba_io_readl((ep)->dma_regs + USBA_DMA_##reg)
|
||||
readl_relaxed((ep)->dma_regs + USBA_DMA_##reg)
|
||||
#define usba_dma_writel(ep, reg, value) \
|
||||
usba_io_writel((value), (ep)->dma_regs + USBA_DMA_##reg)
|
||||
writel_relaxed((value), (ep)->dma_regs + USBA_DMA_##reg)
|
||||
|
||||
/* Calculate base address for a given endpoint or DMA controller */
|
||||
#define USBA_EPT_BASE(x) (0x100 + (x) * 0x20)
|
||||
|
@@ -475,7 +475,7 @@ static int bdc_probe(struct platform_device *pdev)
|
||||
bdc->dev = dev;
|
||||
dev_dbg(bdc->dev, "bdc->regs: %p irq=%d\n", bdc->regs, bdc->irq);
|
||||
|
||||
temp = bdc_readl(bdc->regs, BDC_BDCSC);
|
||||
temp = bdc_readl(bdc->regs, BDC_BDCCAP1);
|
||||
if ((temp & BDC_P64) &&
|
||||
!dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64))) {
|
||||
dev_dbg(bdc->dev, "Using 64-bit address\n");
|
||||
|
@@ -140,10 +140,8 @@ int usb_ep_disable(struct usb_ep *ep)
|
||||
goto out;
|
||||
|
||||
ret = ep->ops->disable(ep);
|
||||
if (ret) {
|
||||
ret = ret;
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
ep->enabled = false;
|
||||
|
||||
@@ -1066,6 +1064,23 @@ static inline void usb_gadget_udc_stop(struct usb_udc *udc)
|
||||
udc->gadget->ops->udc_stop(udc->gadget);
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_gadget_udc_set_speed - tells usb device controller speed supported by
|
||||
* current driver
|
||||
* @udc: The device we want to set maximum speed
|
||||
* @speed: The maximum speed to allowed to run
|
||||
*
|
||||
* This call is issued by the UDC Class driver before calling
|
||||
* usb_gadget_udc_start() in order to make sure that we don't try to
|
||||
* connect on speeds the gadget driver doesn't support.
|
||||
*/
|
||||
static inline void usb_gadget_udc_set_speed(struct usb_udc *udc,
|
||||
enum usb_device_speed speed)
|
||||
{
|
||||
if (udc->gadget->ops->udc_set_speed)
|
||||
udc->gadget->ops->udc_set_speed(udc->gadget, speed);
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_udc_release - release the usb_udc struct
|
||||
* @dev: the dev member within usb_udc
|
||||
@@ -1299,6 +1314,9 @@ static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *dri
|
||||
udc->dev.driver = &driver->driver;
|
||||
udc->gadget->dev.driver = &driver->driver;
|
||||
|
||||
if (driver->max_speed < udc->gadget->max_speed)
|
||||
usb_gadget_udc_set_speed(udc, driver->max_speed);
|
||||
|
||||
ret = driver->bind(udc->gadget, driver);
|
||||
if (ret)
|
||||
goto err1;
|
||||
@@ -1451,6 +1469,18 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr,
|
||||
}
|
||||
static DEVICE_ATTR_RO(state);
|
||||
|
||||
static ssize_t function_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct usb_udc *udc = container_of(dev, struct usb_udc, dev);
|
||||
struct usb_gadget_driver *drv = udc->driver;
|
||||
|
||||
if (!drv || !drv->function)
|
||||
return 0;
|
||||
return scnprintf(buf, PAGE_SIZE, "%s\n", drv->function);
|
||||
}
|
||||
static DEVICE_ATTR_RO(function);
|
||||
|
||||
#define USB_UDC_SPEED_ATTR(name, param) \
|
||||
ssize_t name##_show(struct device *dev, \
|
||||
struct device_attribute *attr, char *buf) \
|
||||
@@ -1486,6 +1516,7 @@ static struct attribute *usb_udc_attrs[] = {
|
||||
&dev_attr_srp.attr,
|
||||
&dev_attr_soft_connect.attr,
|
||||
&dev_attr_state.attr,
|
||||
&dev_attr_function.attr,
|
||||
&dev_attr_current_speed.attr,
|
||||
&dev_attr_maximum_speed.attr,
|
||||
|
||||
|
@@ -881,22 +881,6 @@ static int dummy_pullup(struct usb_gadget *_gadget, int value)
|
||||
unsigned long flags;
|
||||
|
||||
dum = gadget_dev_to_dummy(&_gadget->dev);
|
||||
|
||||
if (value && dum->driver) {
|
||||
if (mod_data.is_super_speed)
|
||||
dum->gadget.speed = dum->driver->max_speed;
|
||||
else if (mod_data.is_high_speed)
|
||||
dum->gadget.speed = min_t(u8, USB_SPEED_HIGH,
|
||||
dum->driver->max_speed);
|
||||
else
|
||||
dum->gadget.speed = USB_SPEED_FULL;
|
||||
dummy_udc_update_ep0(dum);
|
||||
|
||||
if (dum->gadget.speed < dum->driver->max_speed)
|
||||
dev_dbg(udc_dev(dum), "This device can perform faster"
|
||||
" if you connect it to a %s port...\n",
|
||||
usb_speed_string(dum->driver->max_speed));
|
||||
}
|
||||
dum_hcd = gadget_to_dummy_hcd(_gadget);
|
||||
|
||||
spin_lock_irqsave(&dum->lock, flags);
|
||||
@@ -908,6 +892,28 @@ static int dummy_pullup(struct usb_gadget *_gadget, int value)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dummy_udc_set_speed(struct usb_gadget *_gadget,
|
||||
enum usb_device_speed speed)
|
||||
{
|
||||
struct dummy *dum;
|
||||
|
||||
dum = gadget_dev_to_dummy(&_gadget->dev);
|
||||
|
||||
if (mod_data.is_super_speed)
|
||||
dum->gadget.speed = min_t(u8, USB_SPEED_SUPER, speed);
|
||||
else if (mod_data.is_high_speed)
|
||||
dum->gadget.speed = min_t(u8, USB_SPEED_HIGH, speed);
|
||||
else
|
||||
dum->gadget.speed = USB_SPEED_FULL;
|
||||
|
||||
dummy_udc_update_ep0(dum);
|
||||
|
||||
if (dum->gadget.speed < speed)
|
||||
dev_dbg(udc_dev(dum), "This device can perform faster"
|
||||
" if you connect it to a %s port...\n",
|
||||
usb_speed_string(speed));
|
||||
}
|
||||
|
||||
static int dummy_udc_start(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver);
|
||||
static int dummy_udc_stop(struct usb_gadget *g);
|
||||
@@ -919,6 +925,7 @@ static const struct usb_gadget_ops dummy_ops = {
|
||||
.pullup = dummy_pullup,
|
||||
.udc_start = dummy_udc_start,
|
||||
.udc_stop = dummy_udc_stop,
|
||||
.udc_set_speed = dummy_udc_set_speed,
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
@@ -960,9 +960,9 @@ static const struct usb_ep_ops mv_ep_ops = {
|
||||
.fifo_flush = mv_ep_fifo_flush, /* flush fifo */
|
||||
};
|
||||
|
||||
static void udc_clock_enable(struct mv_udc *udc)
|
||||
static int udc_clock_enable(struct mv_udc *udc)
|
||||
{
|
||||
clk_prepare_enable(udc->clk);
|
||||
return clk_prepare_enable(udc->clk);
|
||||
}
|
||||
|
||||
static void udc_clock_disable(struct mv_udc *udc)
|
||||
@@ -1070,7 +1070,10 @@ static int mv_udc_enable_internal(struct mv_udc *udc)
|
||||
return 0;
|
||||
|
||||
dev_dbg(&udc->dev->dev, "enable udc\n");
|
||||
udc_clock_enable(udc);
|
||||
retval = udc_clock_enable(udc);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
if (udc->pdata->phy_init) {
|
||||
retval = udc->pdata->phy_init(udc->phy_regs);
|
||||
if (retval) {
|
||||
|
@@ -3566,7 +3566,6 @@ static void net2280_remove(struct pci_dev *pdev)
|
||||
BUG_ON(dev->driver);
|
||||
|
||||
/* then clean up the resources we allocated during probe() */
|
||||
net2280_led_shutdown(dev);
|
||||
if (dev->requests) {
|
||||
int i;
|
||||
for (i = 1; i < 5; i++) {
|
||||
@@ -3581,8 +3580,10 @@ static void net2280_remove(struct pci_dev *pdev)
|
||||
free_irq(pdev->irq, dev);
|
||||
if (dev->quirks & PLX_PCIE)
|
||||
pci_disable_msi(pdev);
|
||||
if (dev->regs)
|
||||
if (dev->regs) {
|
||||
net2280_led_shutdown(dev);
|
||||
iounmap(dev->regs);
|
||||
}
|
||||
if (dev->region)
|
||||
release_mem_region(pci_resource_start(pdev, 0),
|
||||
pci_resource_len(pdev, 0));
|
||||
|
@@ -9,6 +9,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/extcon.h>
|
||||
#include <linux/interrupt.h>
|
||||
@@ -27,6 +28,8 @@
|
||||
#define USB3_AXI_INT_ENA 0x00c
|
||||
#define USB3_DMA_INT_STA 0x010
|
||||
#define USB3_DMA_INT_ENA 0x014
|
||||
#define USB3_DMA_CH0_CON(n) (0x030 + ((n) - 1) * 0x10) /* n = 1 to 4 */
|
||||
#define USB3_DMA_CH0_PRD_ADR(n) (0x034 + ((n) - 1) * 0x10) /* n = 1 to 4 */
|
||||
#define USB3_USB_COM_CON 0x200
|
||||
#define USB3_USB20_CON 0x204
|
||||
#define USB3_USB30_CON 0x208
|
||||
@@ -64,6 +67,22 @@
|
||||
/* AXI_INT_ENA and AXI_INT_STA */
|
||||
#define AXI_INT_DMAINT BIT(31)
|
||||
#define AXI_INT_EPCINT BIT(30)
|
||||
/* PRD's n = from 1 to 4 */
|
||||
#define AXI_INT_PRDEN_CLR_STA_SHIFT(n) (16 + (n) - 1)
|
||||
#define AXI_INT_PRDERR_STA_SHIFT(n) (0 + (n) - 1)
|
||||
#define AXI_INT_PRDEN_CLR_STA(n) (1 << AXI_INT_PRDEN_CLR_STA_SHIFT(n))
|
||||
#define AXI_INT_PRDERR_STA(n) (1 << AXI_INT_PRDERR_STA_SHIFT(n))
|
||||
|
||||
/* DMA_INT_ENA and DMA_INT_STA */
|
||||
#define DMA_INT(n) BIT(n)
|
||||
|
||||
/* DMA_CH0_CONn */
|
||||
#define DMA_CON_PIPE_DIR BIT(15) /* 1: In Transfer */
|
||||
#define DMA_CON_PIPE_NO_SHIFT 8
|
||||
#define DMA_CON_PIPE_NO_MASK GENMASK(12, DMA_CON_PIPE_NO_SHIFT)
|
||||
#define DMA_COM_PIPE_NO(n) (((n) << DMA_CON_PIPE_NO_SHIFT) & \
|
||||
DMA_CON_PIPE_NO_MASK)
|
||||
#define DMA_CON_PRD_EN BIT(0)
|
||||
|
||||
/* LCLKSEL */
|
||||
#define LCLKSEL_LSEL BIT(18)
|
||||
@@ -231,8 +250,50 @@
|
||||
#define USB3_EP0_BUF_SIZE 8
|
||||
#define USB3_MAX_NUM_PIPES 30
|
||||
#define USB3_WAIT_US 3
|
||||
#define USB3_DMA_NUM_SETTING_AREA 4
|
||||
/*
|
||||
* To avoid double-meaning of "0" (xferred 65536 bytes or received zlp if
|
||||
* buffer size is 65536), this driver uses the maximum size per a entry is
|
||||
* 32768 bytes.
|
||||
*/
|
||||
#define USB3_DMA_MAX_XFER_SIZE 32768
|
||||
#define USB3_DMA_PRD_SIZE 4096
|
||||
|
||||
struct renesas_usb3;
|
||||
|
||||
/* Physical Region Descriptor Table */
|
||||
struct renesas_usb3_prd {
|
||||
u32 word1;
|
||||
#define USB3_PRD1_E BIT(30) /* the end of chain */
|
||||
#define USB3_PRD1_U BIT(29) /* completion of transfer */
|
||||
#define USB3_PRD1_D BIT(28) /* Error occurred */
|
||||
#define USB3_PRD1_INT BIT(27) /* Interrupt occurred */
|
||||
#define USB3_PRD1_LST BIT(26) /* Last Packet */
|
||||
#define USB3_PRD1_B_INC BIT(24)
|
||||
#define USB3_PRD1_MPS_8 0
|
||||
#define USB3_PRD1_MPS_16 BIT(21)
|
||||
#define USB3_PRD1_MPS_32 BIT(22)
|
||||
#define USB3_PRD1_MPS_64 (BIT(22) | BIT(21))
|
||||
#define USB3_PRD1_MPS_512 BIT(23)
|
||||
#define USB3_PRD1_MPS_1024 (BIT(23) | BIT(21))
|
||||
#define USB3_PRD1_MPS_RESERVED (BIT(23) | BIT(22) | BIT(21))
|
||||
#define USB3_PRD1_SIZE_MASK GENMASK(15, 0)
|
||||
|
||||
u32 bap;
|
||||
};
|
||||
#define USB3_DMA_NUM_PRD_ENTRIES (USB3_DMA_PRD_SIZE / \
|
||||
sizeof(struct renesas_usb3_prd))
|
||||
#define USB3_DMA_MAX_XFER_SIZE_ALL_PRDS (USB3_DMA_PRD_SIZE / \
|
||||
sizeof(struct renesas_usb3_prd) * \
|
||||
USB3_DMA_MAX_XFER_SIZE)
|
||||
|
||||
struct renesas_usb3_dma {
|
||||
struct renesas_usb3_prd *prd;
|
||||
dma_addr_t prd_dma;
|
||||
int num; /* Setting area number (from 1 to 4) */
|
||||
bool used;
|
||||
};
|
||||
|
||||
struct renesas_usb3_request {
|
||||
struct usb_request req;
|
||||
struct list_head queue;
|
||||
@@ -242,6 +303,7 @@ struct renesas_usb3_request {
|
||||
struct renesas_usb3_ep {
|
||||
struct usb_ep ep;
|
||||
struct renesas_usb3 *usb3;
|
||||
struct renesas_usb3_dma *dma;
|
||||
int num;
|
||||
char ep_name[USB3_EP_NAME_SIZE];
|
||||
struct list_head queue;
|
||||
@@ -270,6 +332,8 @@ struct renesas_usb3 {
|
||||
struct renesas_usb3_ep *usb3_ep;
|
||||
int num_usb3_eps;
|
||||
|
||||
struct renesas_usb3_dma dma[USB3_DMA_NUM_SETTING_AREA];
|
||||
|
||||
spinlock_t lock;
|
||||
int disabled_count;
|
||||
|
||||
@@ -298,8 +362,18 @@ struct renesas_usb3 {
|
||||
(i) < (usb3)->num_usb3_eps; \
|
||||
(i)++, usb3_ep = usb3_get_ep(usb3, (i)))
|
||||
|
||||
#define usb3_get_dma(usb3, i) (&(usb3)->dma[i])
|
||||
#define usb3_for_each_dma(usb3, dma, i) \
|
||||
for ((i) = 0, dma = usb3_get_dma((usb3), (i)); \
|
||||
(i) < USB3_DMA_NUM_SETTING_AREA; \
|
||||
(i)++, dma = usb3_get_dma((usb3), (i)))
|
||||
|
||||
static const char udc_name[] = "renesas_usb3";
|
||||
|
||||
static bool use_dma = 1;
|
||||
module_param(use_dma, bool, 0644);
|
||||
MODULE_PARM_DESC(use_dma, "use dedicated DMAC");
|
||||
|
||||
static void usb3_write(struct renesas_usb3 *usb3, u32 data, u32 offs)
|
||||
{
|
||||
iowrite32(data, usb3->reg + offs);
|
||||
@@ -1059,6 +1133,273 @@ static void usb3_start_pipe0(struct renesas_usb3_ep *usb3_ep,
|
||||
usb3_p0_xfer(usb3_ep, usb3_req);
|
||||
}
|
||||
|
||||
static void usb3_enable_dma_pipen(struct renesas_usb3 *usb3)
|
||||
{
|
||||
usb3_set_bit(usb3, PN_CON_DATAIF_EN, USB3_PN_CON);
|
||||
}
|
||||
|
||||
static void usb3_disable_dma_pipen(struct renesas_usb3 *usb3)
|
||||
{
|
||||
usb3_clear_bit(usb3, PN_CON_DATAIF_EN, USB3_PN_CON);
|
||||
}
|
||||
|
||||
static void usb3_enable_dma_irq(struct renesas_usb3 *usb3, int num)
|
||||
{
|
||||
usb3_set_bit(usb3, DMA_INT(num), USB3_DMA_INT_ENA);
|
||||
}
|
||||
|
||||
static void usb3_disable_dma_irq(struct renesas_usb3 *usb3, int num)
|
||||
{
|
||||
usb3_clear_bit(usb3, DMA_INT(num), USB3_DMA_INT_ENA);
|
||||
}
|
||||
|
||||
static u32 usb3_dma_mps_to_prd_word1(struct renesas_usb3_ep *usb3_ep)
|
||||
{
|
||||
switch (usb3_ep->ep.maxpacket) {
|
||||
case 8:
|
||||
return USB3_PRD1_MPS_8;
|
||||
case 16:
|
||||
return USB3_PRD1_MPS_16;
|
||||
case 32:
|
||||
return USB3_PRD1_MPS_32;
|
||||
case 64:
|
||||
return USB3_PRD1_MPS_64;
|
||||
case 512:
|
||||
return USB3_PRD1_MPS_512;
|
||||
case 1024:
|
||||
return USB3_PRD1_MPS_1024;
|
||||
default:
|
||||
return USB3_PRD1_MPS_RESERVED;
|
||||
}
|
||||
}
|
||||
|
||||
static bool usb3_dma_get_setting_area(struct renesas_usb3_ep *usb3_ep,
|
||||
struct renesas_usb3_request *usb3_req)
|
||||
{
|
||||
struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep);
|
||||
struct renesas_usb3_dma *dma;
|
||||
int i;
|
||||
bool ret = false;
|
||||
|
||||
if (usb3_req->req.length > USB3_DMA_MAX_XFER_SIZE_ALL_PRDS) {
|
||||
dev_dbg(usb3_to_dev(usb3), "%s: the length is too big (%d)\n",
|
||||
__func__, usb3_req->req.length);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* The driver doesn't handle zero-length packet via dmac */
|
||||
if (!usb3_req->req.length)
|
||||
return false;
|
||||
|
||||
if (usb3_dma_mps_to_prd_word1(usb3_ep) == USB3_PRD1_MPS_RESERVED)
|
||||
return false;
|
||||
|
||||
usb3_for_each_dma(usb3, dma, i) {
|
||||
if (dma->used)
|
||||
continue;
|
||||
|
||||
if (usb_gadget_map_request(&usb3->gadget, &usb3_req->req,
|
||||
usb3_ep->dir_in) < 0)
|
||||
break;
|
||||
|
||||
dma->used = true;
|
||||
usb3_ep->dma = dma;
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void usb3_dma_put_setting_area(struct renesas_usb3_ep *usb3_ep,
|
||||
struct renesas_usb3_request *usb3_req)
|
||||
{
|
||||
struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep);
|
||||
int i;
|
||||
struct renesas_usb3_dma *dma;
|
||||
|
||||
usb3_for_each_dma(usb3, dma, i) {
|
||||
if (usb3_ep->dma == dma) {
|
||||
usb_gadget_unmap_request(&usb3->gadget, &usb3_req->req,
|
||||
usb3_ep->dir_in);
|
||||
dma->used = false;
|
||||
usb3_ep->dma = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void usb3_dma_fill_prd(struct renesas_usb3_ep *usb3_ep,
|
||||
struct renesas_usb3_request *usb3_req)
|
||||
{
|
||||
struct renesas_usb3_prd *cur_prd = usb3_ep->dma->prd;
|
||||
u32 remain = usb3_req->req.length;
|
||||
u32 dma = usb3_req->req.dma;
|
||||
u32 len;
|
||||
int i = 0;
|
||||
|
||||
do {
|
||||
len = min_t(u32, remain, USB3_DMA_MAX_XFER_SIZE) &
|
||||
USB3_PRD1_SIZE_MASK;
|
||||
cur_prd->word1 = usb3_dma_mps_to_prd_word1(usb3_ep) |
|
||||
USB3_PRD1_B_INC | len;
|
||||
cur_prd->bap = dma;
|
||||
remain -= len;
|
||||
dma += len;
|
||||
if (!remain || (i + 1) < USB3_DMA_NUM_PRD_ENTRIES)
|
||||
break;
|
||||
|
||||
cur_prd++;
|
||||
i++;
|
||||
} while (1);
|
||||
|
||||
cur_prd->word1 |= USB3_PRD1_E | USB3_PRD1_INT;
|
||||
if (usb3_ep->dir_in)
|
||||
cur_prd->word1 |= USB3_PRD1_LST;
|
||||
}
|
||||
|
||||
static void usb3_dma_kick_prd(struct renesas_usb3_ep *usb3_ep)
|
||||
{
|
||||
struct renesas_usb3_dma *dma = usb3_ep->dma;
|
||||
struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep);
|
||||
u32 dma_con = DMA_COM_PIPE_NO(usb3_ep->num) | DMA_CON_PRD_EN;
|
||||
|
||||
if (usb3_ep->dir_in)
|
||||
dma_con |= DMA_CON_PIPE_DIR;
|
||||
|
||||
wmb(); /* prd entries should be in system memory here */
|
||||
|
||||
usb3_write(usb3, 1 << usb3_ep->num, USB3_DMA_INT_STA);
|
||||
usb3_write(usb3, AXI_INT_PRDEN_CLR_STA(dma->num) |
|
||||
AXI_INT_PRDERR_STA(dma->num), USB3_AXI_INT_STA);
|
||||
|
||||
usb3_write(usb3, dma->prd_dma, USB3_DMA_CH0_PRD_ADR(dma->num));
|
||||
usb3_write(usb3, dma_con, USB3_DMA_CH0_CON(dma->num));
|
||||
usb3_enable_dma_irq(usb3, usb3_ep->num);
|
||||
}
|
||||
|
||||
static void usb3_dma_stop_prd(struct renesas_usb3_ep *usb3_ep)
|
||||
{
|
||||
struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep);
|
||||
struct renesas_usb3_dma *dma = usb3_ep->dma;
|
||||
|
||||
usb3_disable_dma_irq(usb3, usb3_ep->num);
|
||||
usb3_write(usb3, 0, USB3_DMA_CH0_CON(dma->num));
|
||||
}
|
||||
|
||||
static int usb3_dma_update_status(struct renesas_usb3_ep *usb3_ep,
|
||||
struct renesas_usb3_request *usb3_req)
|
||||
{
|
||||
struct renesas_usb3_prd *cur_prd = usb3_ep->dma->prd;
|
||||
struct usb_request *req = &usb3_req->req;
|
||||
u32 remain, len;
|
||||
int i = 0;
|
||||
int status = 0;
|
||||
|
||||
rmb(); /* The controller updated prd entries */
|
||||
|
||||
do {
|
||||
if (cur_prd->word1 & USB3_PRD1_D)
|
||||
status = -EIO;
|
||||
if (cur_prd->word1 & USB3_PRD1_E)
|
||||
len = req->length % USB3_DMA_MAX_XFER_SIZE;
|
||||
else
|
||||
len = USB3_DMA_MAX_XFER_SIZE;
|
||||
remain = cur_prd->word1 & USB3_PRD1_SIZE_MASK;
|
||||
req->actual += len - remain;
|
||||
|
||||
if (cur_prd->word1 & USB3_PRD1_E ||
|
||||
(i + 1) < USB3_DMA_NUM_PRD_ENTRIES)
|
||||
break;
|
||||
|
||||
cur_prd++;
|
||||
i++;
|
||||
} while (1);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static bool usb3_dma_try_start(struct renesas_usb3_ep *usb3_ep,
|
||||
struct renesas_usb3_request *usb3_req)
|
||||
{
|
||||
struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep);
|
||||
|
||||
if (!use_dma)
|
||||
return false;
|
||||
|
||||
if (usb3_dma_get_setting_area(usb3_ep, usb3_req)) {
|
||||
usb3_pn_stop(usb3);
|
||||
usb3_enable_dma_pipen(usb3);
|
||||
usb3_dma_fill_prd(usb3_ep, usb3_req);
|
||||
usb3_dma_kick_prd(usb3_ep);
|
||||
usb3_pn_start(usb3);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int usb3_dma_try_stop(struct renesas_usb3_ep *usb3_ep,
|
||||
struct renesas_usb3_request *usb3_req)
|
||||
{
|
||||
struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep);
|
||||
unsigned long flags;
|
||||
int status = 0;
|
||||
|
||||
spin_lock_irqsave(&usb3->lock, flags);
|
||||
if (!usb3_ep->dma)
|
||||
goto out;
|
||||
|
||||
if (!usb3_pn_change(usb3, usb3_ep->num))
|
||||
usb3_disable_dma_pipen(usb3);
|
||||
usb3_dma_stop_prd(usb3_ep);
|
||||
status = usb3_dma_update_status(usb3_ep, usb3_req);
|
||||
usb3_dma_put_setting_area(usb3_ep, usb3_req);
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&usb3->lock, flags);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int renesas_usb3_dma_free_prd(struct renesas_usb3 *usb3,
|
||||
struct device *dev)
|
||||
{
|
||||
int i;
|
||||
struct renesas_usb3_dma *dma;
|
||||
|
||||
usb3_for_each_dma(usb3, dma, i) {
|
||||
if (dma->prd) {
|
||||
dma_free_coherent(dev, USB3_DMA_MAX_XFER_SIZE,
|
||||
dma->prd, dma->prd_dma);
|
||||
dma->prd = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int renesas_usb3_dma_alloc_prd(struct renesas_usb3 *usb3,
|
||||
struct device *dev)
|
||||
{
|
||||
int i;
|
||||
struct renesas_usb3_dma *dma;
|
||||
|
||||
if (!use_dma)
|
||||
return 0;
|
||||
|
||||
usb3_for_each_dma(usb3, dma, i) {
|
||||
dma->prd = dma_alloc_coherent(dev, USB3_DMA_PRD_SIZE,
|
||||
&dma->prd_dma, GFP_KERNEL);
|
||||
if (!dma->prd) {
|
||||
renesas_usb3_dma_free_prd(usb3, dev);
|
||||
return -ENOMEM;
|
||||
}
|
||||
dma->num = i + 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usb3_start_pipen(struct renesas_usb3_ep *usb3_ep,
|
||||
struct renesas_usb3_request *usb3_req)
|
||||
{
|
||||
@@ -1078,6 +1419,10 @@ static void usb3_start_pipen(struct renesas_usb3_ep *usb3_ep,
|
||||
goto out;
|
||||
|
||||
usb3_ep->started = true;
|
||||
|
||||
if (usb3_dma_try_start(usb3_ep, usb3_req))
|
||||
goto out;
|
||||
|
||||
usb3_pn_start(usb3);
|
||||
|
||||
if (usb3_ep->dir_in) {
|
||||
@@ -1603,12 +1948,49 @@ static void usb3_irq_epc(struct renesas_usb3 *usb3)
|
||||
}
|
||||
}
|
||||
|
||||
static void usb3_irq_dma_int(struct renesas_usb3 *usb3, u32 dma_sta)
|
||||
{
|
||||
struct renesas_usb3_ep *usb3_ep;
|
||||
struct renesas_usb3_request *usb3_req;
|
||||
int i, status;
|
||||
|
||||
for (i = 0; i < usb3->num_usb3_eps; i++) {
|
||||
if (!(dma_sta & DMA_INT(i)))
|
||||
continue;
|
||||
|
||||
usb3_ep = usb3_get_ep(usb3, i);
|
||||
if (!(usb3_read(usb3, USB3_AXI_INT_STA) &
|
||||
AXI_INT_PRDEN_CLR_STA(usb3_ep->dma->num)))
|
||||
continue;
|
||||
|
||||
usb3_req = usb3_get_request(usb3_ep);
|
||||
status = usb3_dma_try_stop(usb3_ep, usb3_req);
|
||||
usb3_request_done_pipen(usb3, usb3_ep, usb3_req, status);
|
||||
}
|
||||
}
|
||||
|
||||
static void usb3_irq_dma(struct renesas_usb3 *usb3)
|
||||
{
|
||||
u32 dma_sta = usb3_read(usb3, USB3_DMA_INT_STA);
|
||||
|
||||
dma_sta &= usb3_read(usb3, USB3_DMA_INT_ENA);
|
||||
if (dma_sta) {
|
||||
usb3_write(usb3, dma_sta, USB3_DMA_INT_STA);
|
||||
usb3_irq_dma_int(usb3, dma_sta);
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t renesas_usb3_irq(int irq, void *_usb3)
|
||||
{
|
||||
struct renesas_usb3 *usb3 = _usb3;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
u32 axi_int_sta = usb3_read(usb3, USB3_AXI_INT_STA);
|
||||
|
||||
if (axi_int_sta & AXI_INT_DMAINT) {
|
||||
usb3_irq_dma(usb3);
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
if (axi_int_sta & AXI_INT_EPCINT) {
|
||||
usb3_irq_epc(usb3);
|
||||
ret = IRQ_HANDLED;
|
||||
@@ -1708,6 +2090,7 @@ static int renesas_usb3_ep_disable(struct usb_ep *_ep)
|
||||
usb3_req = usb3_get_request(usb3_ep);
|
||||
if (!usb3_req)
|
||||
break;
|
||||
usb3_dma_try_stop(usb3_ep, usb3_req);
|
||||
usb3_request_done(usb3_ep, usb3_req, -ESHUTDOWN);
|
||||
} while (1);
|
||||
|
||||
@@ -1755,6 +2138,7 @@ static int renesas_usb3_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
||||
dev_dbg(usb3_to_dev(usb3), "ep_dequeue: ep%2d, %u\n", usb3_ep->num,
|
||||
_req->length);
|
||||
|
||||
usb3_dma_try_stop(usb3_ep, usb3_req);
|
||||
usb3_request_done_pipen(usb3, usb3_ep, usb3_req, -ECONNRESET);
|
||||
|
||||
return 0;
|
||||
@@ -1917,6 +2301,7 @@ static int renesas_usb3_remove(struct platform_device *pdev)
|
||||
device_remove_file(&pdev->dev, &dev_attr_role);
|
||||
|
||||
usb_del_gadget_udc(&usb3->gadget);
|
||||
renesas_usb3_dma_free_prd(usb3, &pdev->dev);
|
||||
|
||||
__renesas_usb3_ep_free_request(usb3->ep0_req);
|
||||
|
||||
@@ -2111,6 +2496,10 @@ static int renesas_usb3_probe(struct platform_device *pdev)
|
||||
if (!usb3->ep0_req)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = renesas_usb3_dma_alloc_prd(usb3, &pdev->dev);
|
||||
if (ret < 0)
|
||||
goto err_alloc_prd;
|
||||
|
||||
ret = usb_add_gadget_udc(&pdev->dev, &usb3->gadget);
|
||||
if (ret < 0)
|
||||
goto err_add_udc;
|
||||
@@ -2129,6 +2518,9 @@ err_dev_create:
|
||||
usb_del_gadget_udc(&usb3->gadget);
|
||||
|
||||
err_add_udc:
|
||||
renesas_usb3_dma_free_prd(usb3, &pdev->dev);
|
||||
|
||||
err_alloc_prd:
|
||||
__renesas_usb3_ep_free_request(usb3->ep0_req);
|
||||
|
||||
return ret;
|
||||
|
@@ -41,7 +41,6 @@
|
||||
#include "amd5536udc.h"
|
||||
|
||||
static void udc_tasklet_disconnect(unsigned long);
|
||||
static void empty_req_queue(struct udc_ep *);
|
||||
static void udc_setup_endpoints(struct udc *dev);
|
||||
static void udc_soft_reset(struct udc *dev);
|
||||
static struct udc_request *udc_alloc_bna_dummy(struct udc_ep *ep);
|
||||
@@ -209,18 +208,18 @@ static void print_regs(struct udc *dev)
|
||||
if (use_dma && use_dma_ppb && !use_dma_ppb_du) {
|
||||
DBG(dev, "DMA mode = PPBNDU (packet per buffer "
|
||||
"WITHOUT desc. update)\n");
|
||||
dev_info(&dev->pdev->dev, "DMA mode (%s)\n", "PPBNDU");
|
||||
dev_info(dev->dev, "DMA mode (%s)\n", "PPBNDU");
|
||||
} else if (use_dma && use_dma_ppb && use_dma_ppb_du) {
|
||||
DBG(dev, "DMA mode = PPBDU (packet per buffer "
|
||||
"WITH desc. update)\n");
|
||||
dev_info(&dev->pdev->dev, "DMA mode (%s)\n", "PPBDU");
|
||||
dev_info(dev->dev, "DMA mode (%s)\n", "PPBDU");
|
||||
}
|
||||
if (use_dma && use_dma_bufferfill_mode) {
|
||||
DBG(dev, "DMA mode = BF (buffer fill mode)\n");
|
||||
dev_info(&dev->pdev->dev, "DMA mode (%s)\n", "BF");
|
||||
dev_info(dev->dev, "DMA mode (%s)\n", "BF");
|
||||
}
|
||||
if (!use_dma)
|
||||
dev_info(&dev->pdev->dev, "FIFO mode\n");
|
||||
dev_info(dev->dev, "FIFO mode\n");
|
||||
DBG(dev, "-------------------------------------------------------\n");
|
||||
}
|
||||
|
||||
@@ -1244,7 +1243,7 @@ finished:
|
||||
}
|
||||
|
||||
/* Empty request queue of an endpoint; caller holds spinlock */
|
||||
static void empty_req_queue(struct udc_ep *ep)
|
||||
void empty_req_queue(struct udc_ep *ep)
|
||||
{
|
||||
struct udc_request *req;
|
||||
|
||||
@@ -1256,6 +1255,7 @@ static void empty_req_queue(struct udc_ep *ep)
|
||||
complete_req(ep, req, -ESHUTDOWN);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(empty_req_queue);
|
||||
|
||||
/* Dequeues a request packet, called by gadget driver */
|
||||
static int udc_dequeue(struct usb_ep *usbep, struct usb_request *usbreq)
|
||||
@@ -1623,8 +1623,11 @@ static void udc_setup_endpoints(struct udc *dev)
|
||||
/* Bringup after Connect event, initial bringup to be ready for ep0 events */
|
||||
static void usb_connect(struct udc *dev)
|
||||
{
|
||||
/* Return if already connected */
|
||||
if (dev->connected)
|
||||
return;
|
||||
|
||||
dev_info(&dev->pdev->dev, "USB Connect\n");
|
||||
dev_info(dev->dev, "USB Connect\n");
|
||||
|
||||
dev->connected = 1;
|
||||
|
||||
@@ -1641,8 +1644,11 @@ static void usb_connect(struct udc *dev)
|
||||
*/
|
||||
static void usb_disconnect(struct udc *dev)
|
||||
{
|
||||
/* Return if already disconnected */
|
||||
if (!dev->connected)
|
||||
return;
|
||||
|
||||
dev_info(&dev->pdev->dev, "USB Disconnect\n");
|
||||
dev_info(dev->dev, "USB Disconnect\n");
|
||||
|
||||
dev->connected = 0;
|
||||
|
||||
@@ -1715,11 +1721,15 @@ static void udc_soft_reset(struct udc *dev)
|
||||
/* device int. status reset */
|
||||
writel(UDC_DEV_MSK_DISABLE, &dev->regs->irqsts);
|
||||
|
||||
spin_lock_irqsave(&udc_irq_spinlock, flags);
|
||||
writel(AMD_BIT(UDC_DEVCFG_SOFTRESET), &dev->regs->cfg);
|
||||
readl(&dev->regs->cfg);
|
||||
spin_unlock_irqrestore(&udc_irq_spinlock, flags);
|
||||
|
||||
/* Don't do this for Broadcom UDC since this is a reserved
|
||||
* bit.
|
||||
*/
|
||||
if (dev->chiprev != UDC_BCM_REV) {
|
||||
spin_lock_irqsave(&udc_irq_spinlock, flags);
|
||||
writel(AMD_BIT(UDC_DEVCFG_SOFTRESET), &dev->regs->cfg);
|
||||
readl(&dev->regs->cfg);
|
||||
spin_unlock_irqrestore(&udc_irq_spinlock, flags);
|
||||
}
|
||||
}
|
||||
|
||||
/* RDE timer callback to set RDE bit */
|
||||
@@ -2106,7 +2116,7 @@ static irqreturn_t udc_data_out_isr(struct udc *dev, int ep_ix)
|
||||
}
|
||||
/* HE event ? */
|
||||
if (tmp & AMD_BIT(UDC_EPSTS_HE)) {
|
||||
dev_err(&dev->pdev->dev, "HE ep%dout occurred\n", ep->num);
|
||||
dev_err(dev->dev, "HE ep%dout occurred\n", ep->num);
|
||||
|
||||
/* clear HE */
|
||||
writel(tmp | AMD_BIT(UDC_EPSTS_HE), &ep->regs->sts);
|
||||
@@ -2305,7 +2315,7 @@ static irqreturn_t udc_data_in_isr(struct udc *dev, int ep_ix)
|
||||
if (use_dma) {
|
||||
/* BNA ? */
|
||||
if (epsts & AMD_BIT(UDC_EPSTS_BNA)) {
|
||||
dev_err(&dev->pdev->dev,
|
||||
dev_err(dev->dev,
|
||||
"BNA ep%din occurred - DESPTR = %08lx\n",
|
||||
ep->num,
|
||||
(unsigned long) readl(&ep->regs->desptr));
|
||||
@@ -2318,7 +2328,7 @@ static irqreturn_t udc_data_in_isr(struct udc *dev, int ep_ix)
|
||||
}
|
||||
/* HE event ? */
|
||||
if (epsts & AMD_BIT(UDC_EPSTS_HE)) {
|
||||
dev_err(&dev->pdev->dev,
|
||||
dev_err(dev->dev,
|
||||
"HE ep%dn occurred - DESPTR = %08lx\n",
|
||||
ep->num, (unsigned long) readl(&ep->regs->desptr));
|
||||
|
||||
@@ -2956,7 +2966,7 @@ __acquires(dev->lock)
|
||||
|
||||
/* link up all endpoints */
|
||||
udc_setup_endpoints(dev);
|
||||
dev_info(&dev->pdev->dev, "Connect: %s\n",
|
||||
dev_info(dev->dev, "Connect: %s\n",
|
||||
usb_speed_string(dev->gadget.speed));
|
||||
|
||||
/* init ep 0 */
|
||||
@@ -3097,7 +3107,7 @@ int init_dma_pools(struct udc *dev)
|
||||
}
|
||||
|
||||
/* DMA setup */
|
||||
dev->data_requests = dma_pool_create("data_requests", NULL,
|
||||
dev->data_requests = dma_pool_create("data_requests", dev->dev,
|
||||
sizeof(struct udc_data_dma), 0, 0);
|
||||
if (!dev->data_requests) {
|
||||
DBG(dev, "can't get request data pool\n");
|
||||
@@ -3108,7 +3118,7 @@ int init_dma_pools(struct udc *dev)
|
||||
dev->ep[UDC_EP0IN_IX].dma = &dev->regs->ctl;
|
||||
|
||||
/* dma desc for setup data */
|
||||
dev->stp_requests = dma_pool_create("setup requests", NULL,
|
||||
dev->stp_requests = dma_pool_create("setup requests", dev->dev,
|
||||
sizeof(struct udc_stp_dma), 0, 0);
|
||||
if (!dev->stp_requests) {
|
||||
DBG(dev, "can't get stp request pool\n");
|
||||
@@ -3168,24 +3178,30 @@ int udc_probe(struct udc *dev)
|
||||
/* init registers, interrupts, ... */
|
||||
startup_registers(dev);
|
||||
|
||||
dev_info(&dev->pdev->dev, "%s\n", mod_desc);
|
||||
dev_info(dev->dev, "%s\n", mod_desc);
|
||||
|
||||
snprintf(tmp, sizeof(tmp), "%d", dev->irq);
|
||||
dev_info(&dev->pdev->dev,
|
||||
"irq %s, pci mem %08lx, chip rev %02x(Geode5536 %s)\n",
|
||||
tmp, dev->phys_addr, dev->chiprev,
|
||||
(dev->chiprev == UDC_HSA0_REV) ? "A0" : "B1");
|
||||
strcpy(tmp, UDC_DRIVER_VERSION_STRING);
|
||||
if (dev->chiprev == UDC_HSA0_REV) {
|
||||
dev_err(&dev->pdev->dev, "chip revision is A0; too old\n");
|
||||
retval = -ENODEV;
|
||||
goto finished;
|
||||
|
||||
/* Print this device info for AMD chips only*/
|
||||
if (dev->chiprev == UDC_HSA0_REV ||
|
||||
dev->chiprev == UDC_HSB1_REV) {
|
||||
dev_info(dev->dev, "irq %s, pci mem %08lx, chip rev %02x(Geode5536 %s)\n",
|
||||
tmp, dev->phys_addr, dev->chiprev,
|
||||
(dev->chiprev == UDC_HSA0_REV) ?
|
||||
"A0" : "B1");
|
||||
strcpy(tmp, UDC_DRIVER_VERSION_STRING);
|
||||
if (dev->chiprev == UDC_HSA0_REV) {
|
||||
dev_err(dev->dev, "chip revision is A0; too old\n");
|
||||
retval = -ENODEV;
|
||||
goto finished;
|
||||
}
|
||||
dev_info(dev->dev,
|
||||
"driver version: %s(for Geode5536 B1)\n", tmp);
|
||||
}
|
||||
dev_info(&dev->pdev->dev,
|
||||
"driver version: %s(for Geode5536 B1)\n", tmp);
|
||||
|
||||
udc = dev;
|
||||
|
||||
retval = usb_add_gadget_udc_release(&udc->pdev->dev, &dev->gadget,
|
||||
retval = usb_add_gadget_udc_release(udc->dev, &dev->gadget,
|
||||
gadget_release);
|
||||
if (retval)
|
||||
goto finished;
|
344
drivers/usb/gadget/udc/snps_udc_plat.c
Normal file
344
drivers/usb/gadget/udc/snps_udc_plat.c
Normal file
@@ -0,0 +1,344 @@
|
||||
/*
|
||||
* snps_udc_plat.c - Synopsys UDC Platform Driver
|
||||
*
|
||||
* Copyright (C) 2016 Broadcom
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/extcon.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/dmapool.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include "amd5536udc.h"
|
||||
|
||||
/* description */
|
||||
#define UDC_MOD_DESCRIPTION "Synopsys UDC platform driver"
|
||||
|
||||
void start_udc(struct udc *udc)
|
||||
{
|
||||
if (udc->driver) {
|
||||
dev_info(udc->dev, "Connecting...\n");
|
||||
udc_enable_dev_setup_interrupts(udc);
|
||||
udc_basic_init(udc);
|
||||
udc->connected = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void stop_udc(struct udc *udc)
|
||||
{
|
||||
int tmp;
|
||||
u32 reg;
|
||||
|
||||
spin_lock(&udc->lock);
|
||||
|
||||
/* Flush the receieve fifo */
|
||||
reg = readl(&udc->regs->ctl);
|
||||
reg |= AMD_BIT(UDC_DEVCTL_SRX_FLUSH);
|
||||
writel(reg, &udc->regs->ctl);
|
||||
|
||||
reg = readl(&udc->regs->ctl);
|
||||
reg &= ~(AMD_BIT(UDC_DEVCTL_SRX_FLUSH));
|
||||
writel(reg, &udc->regs->ctl);
|
||||
dev_dbg(udc->dev, "ep rx queue flushed\n");
|
||||
|
||||
/* Mask interrupts. Required more so when the
|
||||
* UDC is connected to a DRD phy.
|
||||
*/
|
||||
udc_mask_unused_interrupts(udc);
|
||||
|
||||
/* Disconnect gadget driver */
|
||||
if (udc->driver) {
|
||||
spin_unlock(&udc->lock);
|
||||
udc->driver->disconnect(&udc->gadget);
|
||||
spin_lock(&udc->lock);
|
||||
|
||||
/* empty queues */
|
||||
for (tmp = 0; tmp < UDC_EP_NUM; tmp++)
|
||||
empty_req_queue(&udc->ep[tmp]);
|
||||
}
|
||||
udc->connected = 0;
|
||||
|
||||
spin_unlock(&udc->lock);
|
||||
dev_info(udc->dev, "Device disconnected\n");
|
||||
}
|
||||
|
||||
void udc_drd_work(struct work_struct *work)
|
||||
{
|
||||
struct udc *udc;
|
||||
|
||||
udc = container_of(to_delayed_work(work),
|
||||
struct udc, drd_work);
|
||||
|
||||
if (udc->conn_type) {
|
||||
dev_dbg(udc->dev, "idle -> device\n");
|
||||
start_udc(udc);
|
||||
} else {
|
||||
dev_dbg(udc->dev, "device -> idle\n");
|
||||
stop_udc(udc);
|
||||
}
|
||||
}
|
||||
|
||||
static int usbd_connect_notify(struct notifier_block *self,
|
||||
unsigned long event, void *ptr)
|
||||
{
|
||||
struct udc *udc = container_of(self, struct udc, nb);
|
||||
|
||||
dev_dbg(udc->dev, "%s: event: %lu\n", __func__, event);
|
||||
|
||||
udc->conn_type = event;
|
||||
|
||||
schedule_delayed_work(&udc->drd_work, 0);
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static int udc_plat_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *res;
|
||||
struct udc *udc;
|
||||
int ret;
|
||||
|
||||
udc = devm_kzalloc(dev, sizeof(*udc), GFP_KERNEL);
|
||||
if (!udc)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_init(&udc->lock);
|
||||
udc->dev = dev;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
udc->virt_addr = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(udc->regs))
|
||||
return PTR_ERR(udc->regs);
|
||||
|
||||
/* udc csr registers base */
|
||||
udc->csr = udc->virt_addr + UDC_CSR_ADDR;
|
||||
|
||||
/* dev registers base */
|
||||
udc->regs = udc->virt_addr + UDC_DEVCFG_ADDR;
|
||||
|
||||
/* ep registers base */
|
||||
udc->ep_regs = udc->virt_addr + UDC_EPREGS_ADDR;
|
||||
|
||||
/* fifo's base */
|
||||
udc->rxfifo = (u32 __iomem *)(udc->virt_addr + UDC_RXFIFO_ADDR);
|
||||
udc->txfifo = (u32 __iomem *)(udc->virt_addr + UDC_TXFIFO_ADDR);
|
||||
|
||||
udc->phys_addr = (unsigned long)res->start;
|
||||
|
||||
udc->irq = irq_of_parse_and_map(dev->of_node, 0);
|
||||
if (udc->irq <= 0) {
|
||||
dev_err(dev, "Can't parse and map interrupt\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
udc->udc_phy = devm_of_phy_get_by_index(dev, dev->of_node, 0);
|
||||
if (IS_ERR(udc->udc_phy)) {
|
||||
dev_err(dev, "Failed to obtain phy from device tree\n");
|
||||
return PTR_ERR(udc->udc_phy);
|
||||
}
|
||||
|
||||
ret = phy_init(udc->udc_phy);
|
||||
if (ret) {
|
||||
dev_err(dev, "UDC phy init failed");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = phy_power_on(udc->udc_phy);
|
||||
if (ret) {
|
||||
dev_err(dev, "UDC phy power on failed");
|
||||
phy_exit(udc->udc_phy);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Register for extcon if supported */
|
||||
if (of_get_property(dev->of_node, "extcon", NULL)) {
|
||||
udc->edev = extcon_get_edev_by_phandle(dev, 0);
|
||||
if (IS_ERR(udc->edev)) {
|
||||
if (PTR_ERR(udc->edev) == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
dev_err(dev, "Invalid or missing extcon\n");
|
||||
ret = PTR_ERR(udc->edev);
|
||||
goto exit_phy;
|
||||
}
|
||||
|
||||
udc->nb.notifier_call = usbd_connect_notify;
|
||||
ret = extcon_register_notifier(udc->edev, EXTCON_USB,
|
||||
&udc->nb);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Can't register extcon device\n");
|
||||
goto exit_phy;
|
||||
}
|
||||
|
||||
ret = extcon_get_cable_state_(udc->edev, EXTCON_USB);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Can't get cable state\n");
|
||||
goto exit_extcon;
|
||||
} else if (ret) {
|
||||
udc->conn_type = ret;
|
||||
}
|
||||
INIT_DELAYED_WORK(&udc->drd_work, udc_drd_work);
|
||||
}
|
||||
|
||||
/* init dma pools */
|
||||
if (use_dma) {
|
||||
ret = init_dma_pools(udc);
|
||||
if (ret != 0)
|
||||
goto exit_extcon;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(dev, udc->irq, udc_irq, IRQF_SHARED,
|
||||
"snps-udc", udc);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Request irq %d failed for UDC\n", udc->irq);
|
||||
goto exit_dma;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, udc);
|
||||
udc->chiprev = UDC_BCM_REV;
|
||||
|
||||
if (udc_probe(udc)) {
|
||||
ret = -ENODEV;
|
||||
goto exit_dma;
|
||||
}
|
||||
dev_info(dev, "Synopsys UDC platform driver probe successful\n");
|
||||
|
||||
return 0;
|
||||
|
||||
exit_dma:
|
||||
if (use_dma)
|
||||
free_dma_pools(udc);
|
||||
exit_extcon:
|
||||
if (udc->edev)
|
||||
extcon_unregister_notifier(udc->edev, EXTCON_USB, &udc->nb);
|
||||
exit_phy:
|
||||
if (udc->udc_phy) {
|
||||
phy_power_off(udc->udc_phy);
|
||||
phy_exit(udc->udc_phy);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int udc_plat_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct udc *dev;
|
||||
|
||||
dev = platform_get_drvdata(pdev);
|
||||
|
||||
usb_del_gadget_udc(&dev->gadget);
|
||||
/* gadget driver must not be registered */
|
||||
if (WARN_ON(dev->driver))
|
||||
return 0;
|
||||
|
||||
/* dma pool cleanup */
|
||||
free_dma_pools(dev);
|
||||
|
||||
udc_remove(dev);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
if (dev->drd_wq) {
|
||||
flush_workqueue(dev->drd_wq);
|
||||
destroy_workqueue(dev->drd_wq);
|
||||
}
|
||||
|
||||
phy_power_off(dev->udc_phy);
|
||||
phy_exit(dev->udc_phy);
|
||||
extcon_unregister_notifier(dev->edev, EXTCON_USB, &dev->nb);
|
||||
|
||||
dev_info(&pdev->dev, "Synopsys UDC platform driver removed\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int udc_plat_suspend(struct device *dev)
|
||||
{
|
||||
struct udc *udc;
|
||||
|
||||
udc = dev_get_drvdata(dev);
|
||||
stop_udc(udc);
|
||||
|
||||
if (extcon_get_cable_state_(udc->edev, EXTCON_USB) > 0) {
|
||||
dev_dbg(udc->dev, "device -> idle\n");
|
||||
stop_udc(udc);
|
||||
}
|
||||
phy_power_off(udc->udc_phy);
|
||||
phy_exit(udc->udc_phy);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int udc_plat_resume(struct device *dev)
|
||||
{
|
||||
struct udc *udc;
|
||||
int ret;
|
||||
|
||||
udc = dev_get_drvdata(dev);
|
||||
|
||||
ret = phy_init(udc->udc_phy);
|
||||
if (ret) {
|
||||
dev_err(udc->dev, "UDC phy init failure");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = phy_power_on(udc->udc_phy);
|
||||
if (ret) {
|
||||
dev_err(udc->dev, "UDC phy power on failure");
|
||||
phy_exit(udc->udc_phy);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (extcon_get_cable_state_(udc->edev, EXTCON_USB) > 0) {
|
||||
dev_dbg(udc->dev, "idle -> device\n");
|
||||
start_udc(udc);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
static const struct dev_pm_ops udc_plat_pm_ops = {
|
||||
.suspend = udc_plat_suspend,
|
||||
.resume = udc_plat_resume,
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_OF)
|
||||
static const struct of_device_id of_udc_match[] = {
|
||||
{ .compatible = "brcm,ns2-udc", },
|
||||
{ .compatible = "brcm,cygnus-udc", },
|
||||
{ .compatible = "brcm,iproc-udc", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_udc_match);
|
||||
#endif
|
||||
|
||||
static struct platform_driver udc_plat_driver = {
|
||||
.probe = udc_plat_probe,
|
||||
.remove = udc_plat_remove,
|
||||
.driver = {
|
||||
.name = "snps-udc-plat",
|
||||
.of_match_table = of_match_ptr(of_udc_match),
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
.pm = &udc_plat_pm_ops,
|
||||
#endif
|
||||
},
|
||||
};
|
||||
module_platform_driver(udc_plat_driver);
|
||||
|
||||
MODULE_DESCRIPTION(UDC_MOD_DESCRIPTION);
|
||||
MODULE_AUTHOR("Broadcom");
|
||||
MODULE_LICENSE("GPL v2");
|
@@ -1151,7 +1151,7 @@ static int xudc_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
||||
break;
|
||||
}
|
||||
if (&req->usb_req != _req) {
|
||||
spin_unlock_irqrestore(&ep->udc->lock, flags);
|
||||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
xudc_done(ep, req, -ECONNRESET);
|
||||
|
مرجع در شماره جدید
Block a user