summaryrefslogtreecommitdiff
path: root/sound/usb
diff options
context:
space:
mode:
Diffstat (limited to 'sound/usb')
-rw-r--r--sound/usb/6fire/firmware.c5
-rw-r--r--sound/usb/card.c2
-rw-r--r--sound/usb/card.h7
-rw-r--r--sound/usb/endpoint.c39
-rw-r--r--sound/usb/endpoint.h5
-rw-r--r--sound/usb/helper.c5
-rw-r--r--sound/usb/mixer.c7
-rw-r--r--sound/usb/pcm.c126
-rw-r--r--sound/usb/quirks-table.h53
-rw-r--r--sound/usb/quirks.c24
-rw-r--r--sound/usb/quirks.h10
-rw-r--r--sound/usb/usx2y/us122l.c2
-rw-r--r--sound/usb/usx2y/usX2Yhwdep.c2
-rw-r--r--sound/usb/usx2y/usx2yhwdeppcm.c2
14 files changed, 219 insertions, 70 deletions
diff --git a/sound/usb/6fire/firmware.c b/sound/usb/6fire/firmware.c
index 56ad923..a1d9b07 100644
--- a/sound/usb/6fire/firmware.c
+++ b/sound/usb/6fire/firmware.c
@@ -346,11 +346,10 @@ static int usb6fire_fw_check(u8 *version)
if (!memcmp(version, known_fw_versions + i, 4))
return 0;
- snd_printk(KERN_ERR PREFIX "invalid fimware version in device: "
- "%02x %02x %02x %02x. "
+ snd_printk(KERN_ERR PREFIX "invalid fimware version in device: %*ph. "
"please reconnect to power. if this failure "
"still happens, check your firmware installation.",
- version[0], version[1], version[2], version[3]);
+ 4, version);
return -EINVAL;
}
diff --git a/sound/usb/card.c b/sound/usb/card.c
index 4a469f0..561bb74 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -646,6 +646,8 @@ static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message)
list_for_each(p, &chip->pcm_list) {
as = list_entry(p, struct snd_usb_stream, list);
snd_pcm_suspend_all(as->pcm);
+ as->substream[0].need_setup_ep =
+ as->substream[1].need_setup_ep = true;
}
}
} else {
diff --git a/sound/usb/card.h b/sound/usb/card.h
index 2b9ffff..afa4f9e 100644
--- a/sound/usb/card.h
+++ b/sound/usb/card.h
@@ -92,6 +92,8 @@ struct snd_usb_endpoint {
unsigned char silence_value;
unsigned int stride;
int iface, alt_idx;
+ int skip_packets; /* quirks for devices to ignore the first n packets
+ in a stream */
spinlock_t lock;
struct list_head list;
@@ -105,6 +107,8 @@ struct snd_usb_substream {
int interface; /* current interface */
int endpoint; /* assigned endpoint */
struct audioformat *cur_audiofmt; /* current audioformat pointer (for hw_params callback) */
+ snd_pcm_format_t pcm_format; /* current audio format (for hw_params callback) */
+ unsigned int channels; /* current number of channels (for hw_params callback) */
unsigned int cur_rate; /* current rate (for hw_params callback) */
unsigned int period_bytes; /* current period bytes (for hw_params callback) */
unsigned int altset_idx; /* USB data format: index of alternate setting */
@@ -115,14 +119,13 @@ struct snd_usb_substream {
unsigned int hwptr_done; /* processed byte position in the buffer */
unsigned int transfer_done; /* processed frames since last period update */
- unsigned long active_mask; /* bitmask of active urbs */
- unsigned long unlink_mask; /* bitmask of unlinked urbs */
/* data and sync endpoints for this stream */
unsigned int ep_num; /* the endpoint number */
struct snd_usb_endpoint *data_endpoint;
struct snd_usb_endpoint *sync_endpoint;
unsigned long flags;
+ bool need_setup_ep; /* (re)configure EP at prepare? */
u64 formats; /* format bitmasks (all or'ed) */
unsigned int num_formats; /* number of supported audio formats (list) */
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index 060dccb..7f78c6d 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -31,6 +31,7 @@
#include "card.h"
#include "endpoint.h"
#include "pcm.h"
+#include "quirks.h"
#define EP_FLAG_ACTIVATED 0
#define EP_FLAG_RUNNING 1
@@ -170,6 +171,11 @@ static void retire_inbound_urb(struct snd_usb_endpoint *ep,
{
struct urb *urb = urb_ctx->urb;
+ if (unlikely(ep->skip_packets > 0)) {
+ ep->skip_packets--;
+ return;
+ }
+
if (ep->sync_slave)
snd_usb_handle_sync_urb(ep->sync_slave, ep, urb);
@@ -567,20 +573,19 @@ static void release_urbs(struct snd_usb_endpoint *ep, int force)
* configure a data endpoint
*/
static int data_ep_set_params(struct snd_usb_endpoint *ep,
- struct snd_pcm_hw_params *hw_params,
+ snd_pcm_format_t pcm_format,
+ unsigned int channels,
+ unsigned int period_bytes,
struct audioformat *fmt,
struct snd_usb_endpoint *sync_ep)
{
unsigned int maxsize, i, urb_packs, total_packs, packs_per_ms;
- int period_bytes = params_period_bytes(hw_params);
- int format = params_format(hw_params);
int is_playback = usb_pipeout(ep->pipe);
- int frame_bits = snd_pcm_format_physical_width(params_format(hw_params)) *
- params_channels(hw_params);
+ int frame_bits = snd_pcm_format_physical_width(pcm_format) * channels;
ep->datainterval = fmt->datainterval;
ep->stride = frame_bits >> 3;
- ep->silence_value = format == SNDRV_PCM_FORMAT_U8 ? 0x80 : 0;
+ ep->silence_value = pcm_format == SNDRV_PCM_FORMAT_U8 ? 0x80 : 0;
/* calculate max. frequency */
if (ep->maxpacksize) {
@@ -693,7 +698,6 @@ out_of_memory:
* configure a sync endpoint
*/
static int sync_ep_set_params(struct snd_usb_endpoint *ep,
- struct snd_pcm_hw_params *hw_params,
struct audioformat *fmt)
{
int i;
@@ -736,7 +740,10 @@ out_of_memory:
* snd_usb_endpoint_set_params: configure an snd_usb_endpoint
*
* @ep: the snd_usb_endpoint to configure
- * @hw_params: the hardware parameters
+ * @pcm_format: the audio fomat.
+ * @channels: the number of audio channels.
+ * @period_bytes: the number of bytes in one alsa period.
+ * @rate: the frame rate.
* @fmt: the USB audio format information
* @sync_ep: the sync endpoint to use, if any
*
@@ -745,7 +752,10 @@ out_of_memory:
* An endpoint that is already running can not be reconfigured.
*/
int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep,
- struct snd_pcm_hw_params *hw_params,
+ snd_pcm_format_t pcm_format,
+ unsigned int channels,
+ unsigned int period_bytes,
+ unsigned int rate,
struct audioformat *fmt,
struct snd_usb_endpoint *sync_ep)
{
@@ -765,9 +775,9 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep,
ep->fill_max = !!(fmt->attributes & UAC_EP_CS_ATTR_FILL_MAX);
if (snd_usb_get_speed(ep->chip->dev) == USB_SPEED_FULL)
- ep->freqn = get_usb_full_speed_rate(params_rate(hw_params));
+ ep->freqn = get_usb_full_speed_rate(rate);
else
- ep->freqn = get_usb_high_speed_rate(params_rate(hw_params));
+ ep->freqn = get_usb_high_speed_rate(rate);
/* calculate the frequency in 16.16 format */
ep->freqm = ep->freqn;
@@ -777,10 +787,11 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep,
switch (ep->type) {
case SND_USB_ENDPOINT_TYPE_DATA:
- err = data_ep_set_params(ep, hw_params, fmt, sync_ep);
+ err = data_ep_set_params(ep, pcm_format, channels,
+ period_bytes, fmt, sync_ep);
break;
case SND_USB_ENDPOINT_TYPE_SYNC:
- err = sync_ep_set_params(ep, hw_params, fmt);
+ err = sync_ep_set_params(ep, fmt);
break;
default:
err = -EINVAL;
@@ -828,6 +839,8 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep, int can_sleep)
ep->unlink_mask = 0;
ep->phase = 0;
+ snd_usb_endpoint_start_quirk(ep);
+
/*
* If this endpoint has a data endpoint as implicit feedback source,
* don't start the urbs here. Instead, mark them all as available,
diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h
index cbbbdf2..6376ccf 100644
--- a/sound/usb/endpoint.h
+++ b/sound/usb/endpoint.h
@@ -9,7 +9,10 @@ struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip,
int ep_num, int direction, int type);
int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep,
- struct snd_pcm_hw_params *hw_params,
+ snd_pcm_format_t pcm_format,
+ unsigned int channels,
+ unsigned int period_bytes,
+ unsigned int rate,
struct audioformat *fmt,
struct snd_usb_endpoint *sync_ep);
diff --git a/sound/usb/helper.c b/sound/usb/helper.c
index 9eed8f4..c1db28f 100644
--- a/sound/usb/helper.c
+++ b/sound/usb/helper.c
@@ -21,6 +21,7 @@
#include "usbaudio.h"
#include "helper.h"
+#include "quirks.h"
/*
* combine bytes and get an integer value
@@ -97,6 +98,10 @@ int snd_usb_ctl_msg(struct usb_device *dev, unsigned int pipe, __u8 request,
memcpy(data, buf, size);
kfree(buf);
}
+
+ snd_usb_ctl_msg_quirk(dev, pipe, request, requesttype,
+ value, index, data, size);
+
return err;
}
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 4f40ba8..fe56c9d 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -1267,6 +1267,13 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, void
/* disable non-functional volume control */
master_bits &= ~UAC_CONTROL_BIT(UAC_FU_VOLUME);
break;
+ case USB_ID(0x1130, 0xf211):
+ snd_printk(KERN_INFO
+ "usbmixer: volume control quirk for Tenx TP6911 Audio Headset\n");
+ /* disable non-functional volume control */
+ channels = 0;
+ break;
+
}
if (channels > 0)
first_ch_bits = snd_usb_combine_bytes(bmaControls + csize, csize);
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index f782ce1..55e19e1 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -82,8 +82,7 @@ static snd_pcm_uframes_t snd_usb_pcm_pointer(struct snd_pcm_substream *substream
/*
* find a matching audio format
*/
-static struct audioformat *find_format(struct snd_usb_substream *subs, unsigned int format,
- unsigned int rate, unsigned int channels)
+static struct audioformat *find_format(struct snd_usb_substream *subs)
{
struct list_head *p;
struct audioformat *found = NULL;
@@ -92,16 +91,17 @@ static struct audioformat *find_format(struct snd_usb_substream *subs, unsigned
list_for_each(p, &subs->fmt_list) {
struct audioformat *fp;
fp = list_entry(p, struct audioformat, list);
- if (!(fp->formats & (1uLL << format)))
+ if (!(fp->formats & (1uLL << subs->pcm_format)))
continue;
- if (fp->channels != channels)
+ if (fp->channels != subs->channels)
continue;
- if (rate < fp->rate_min || rate > fp->rate_max)
+ if (subs->cur_rate < fp->rate_min ||
+ subs->cur_rate > fp->rate_max)
continue;
if (! (fp->rates & SNDRV_PCM_RATE_CONTINUOUS)) {
unsigned int i;
for (i = 0; i < fp->nr_rates; i++)
- if (fp->rate_table[i] == rate)
+ if (fp->rate_table[i] == subs->cur_rate)
break;
if (i >= fp->nr_rates)
continue;
@@ -436,6 +436,42 @@ add_sync_ep:
}
/*
+ * configure endpoint params
+ *
+ * called during initial setup and upon resume
+ */
+static int configure_endpoint(struct snd_usb_substream *subs)
+{
+ int ret;
+
+ mutex_lock(&subs->stream->chip->shutdown_mutex);
+ /* format changed */
+ stop_endpoints(subs, 0, 0, 0);
+ ret = snd_usb_endpoint_set_params(subs->data_endpoint,
+ subs->pcm_format,
+ subs->channels,
+ subs->period_bytes,
+ subs->cur_rate,
+ subs->cur_audiofmt,
+ subs->sync_endpoint);
+ if (ret < 0)
+ goto unlock;
+
+ if (subs->sync_endpoint)
+ ret = snd_usb_endpoint_set_params(subs->data_endpoint,
+ subs->pcm_format,
+ subs->channels,
+ subs->period_bytes,
+ subs->cur_rate,
+ subs->cur_audiofmt,
+ NULL);
+
+unlock:
+ mutex_unlock(&subs->stream->chip->shutdown_mutex);
+ return ret;
+}
+
+/*
* hw_params callback
*
* allocate a buffer and set the given audio format.
@@ -450,63 +486,33 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
{
struct snd_usb_substream *subs = substream->runtime->private_data;
struct audioformat *fmt;
- unsigned int channels, rate, format;
- int ret, changed;
+ int ret;
ret = snd_pcm_lib_alloc_vmalloc_buffer(substream,
params_buffer_bytes(hw_params));
if (ret < 0)
return ret;
- format = params_format(hw_params);
- rate = params_rate(hw_params);
- channels = params_channels(hw_params);
- fmt = find_format(subs, format, rate, channels);
+ subs->pcm_format = params_format(hw_params);
+ subs->period_bytes = params_period_bytes(hw_params);
+ subs->channels = params_channels(hw_params);
+ subs->cur_rate = params_rate(hw_params);
+
+ fmt = find_format(subs);
if (!fmt) {
snd_printd(KERN_DEBUG "cannot set format: format = %#x, rate = %d, channels = %d\n",
- format, rate, channels);
+ subs->pcm_format, subs->cur_rate, subs->channels);
return -EINVAL;
}
- changed = subs->cur_audiofmt != fmt ||
- subs->period_bytes != params_period_bytes(hw_params) ||
- subs->cur_rate != rate;
if ((ret = set_format(subs, fmt)) < 0)
return ret;
- if (subs->cur_rate != rate) {
- struct usb_host_interface *alts;
- struct usb_interface *iface;
- iface = usb_ifnum_to_if(subs->dev, fmt->iface);
- alts = &iface->altsetting[fmt->altset_idx];
- ret = snd_usb_init_sample_rate(subs->stream->chip, fmt->iface, alts, fmt, rate);
- if (ret < 0)
- return ret;
- subs->cur_rate = rate;
- }
-
- if (changed) {
- mutex_lock(&subs->stream->chip->shutdown_mutex);
- /* format changed */
- stop_endpoints(subs, 0, 0, 0);
- ret = snd_usb_endpoint_set_params(subs->data_endpoint, hw_params, fmt,
- subs->sync_endpoint);
- if (ret < 0)
- goto unlock;
+ subs->interface = fmt->iface;
+ subs->altset_idx = fmt->altset_idx;
+ subs->need_setup_ep = true;
- if (subs->sync_endpoint)
- ret = snd_usb_endpoint_set_params(subs->sync_endpoint,
- hw_params, fmt, NULL);
-unlock:
- mutex_unlock(&subs->stream->chip->shutdown_mutex);
- }
-
- if (ret == 0) {
- subs->interface = fmt->iface;
- subs->altset_idx = fmt->altset_idx;
- }
-
- return ret;
+ return 0;
}
/*
@@ -537,6 +543,9 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_usb_substream *subs = runtime->private_data;
+ struct usb_host_interface *alts;
+ struct usb_interface *iface;
+ int ret;
if (! subs->cur_audiofmt) {
snd_printk(KERN_ERR "usbaudio: no format is specified!\n");
@@ -546,6 +555,27 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
if (snd_BUG_ON(!subs->data_endpoint))
return -EIO;
+ ret = set_format(subs, subs->cur_audiofmt);
+ if (ret < 0)
+ return ret;
+
+ iface = usb_ifnum_to_if(subs->dev, subs->cur_audiofmt->iface);
+ alts = &iface->altsetting[subs->cur_audiofmt->altset_idx];
+ ret = snd_usb_init_sample_rate(subs->stream->chip,
+ subs->cur_audiofmt->iface,
+ alts,
+ subs->cur_audiofmt,
+ subs->cur_rate);
+ if (ret < 0)
+ return ret;
+
+ if (subs->need_setup_ep) {
+ ret = configure_endpoint(subs);
+ if (ret < 0)
+ return ret;
+ subs->need_setup_ep = false;
+ }
+
/* some unit conversions in runtime */
subs->data_endpoint->maxframesize =
bytes_to_frames(runtime, subs->data_endpoint->maxpacksize);
diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h
index 79780fa..d73ac9b 100644
--- a/sound/usb/quirks-table.h
+++ b/sound/usb/quirks-table.h
@@ -2781,6 +2781,59 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
+/* Microsoft XboxLive Headset/Xbox Communicator */
+{
+ USB_DEVICE(0x045e, 0x0283),
+ .bInterfaceClass = USB_CLASS_PER_INTERFACE,
+ .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ .vendor_name = "Microsoft",
+ .product_name = "XboxLive Headset/Xbox Communicator",
+ .ifnum = QUIRK_ANY_INTERFACE,
+ .type = QUIRK_COMPOSITE,
+ .data = &(const struct snd_usb_audio_quirk[]) {
+ {
+ /* playback */
+ .ifnum = 0,
+ .type = QUIRK_AUDIO_FIXED_ENDPOINT,
+ .data = &(const struct audioformat) {
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels = 1,
+ .iface = 0,
+ .altsetting = 0,
+ .altset_idx = 0,
+ .attributes = 0,
+ .endpoint = 0x04,
+ .ep_attr = 0x05,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 22050,
+ .rate_max = 22050
+ }
+ },
+ {
+ /* capture */
+ .ifnum = 1,
+ .type = QUIRK_AUDIO_FIXED_ENDPOINT,
+ .data = &(const struct audioformat) {
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels = 1,
+ .iface = 1,
+ .altsetting = 0,
+ .altset_idx = 0,
+ .attributes = 0,
+ .endpoint = 0x85,
+ .ep_attr = 0x05,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 16000,
+ .rate_max = 16000
+ }
+ },
+ {
+ .ifnum = -1
+ }
+ }
+ }
+},
+
{
/*
* Some USB MIDI devices don't have an audio control interface,
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 2781726..0f58b4b 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -761,3 +761,27 @@ void snd_usb_set_format_quirk(struct snd_usb_substream *subs,
}
}
+void snd_usb_endpoint_start_quirk(struct snd_usb_endpoint *ep)
+{
+ /*
+ * "Playback Design" products send bogus feedback data at the start
+ * of the stream. Ignore them.
+ */
+ if ((le16_to_cpu(ep->chip->dev->descriptor.idVendor) == 0x23ba) &&
+ ep->type == SND_USB_ENDPOINT_TYPE_SYNC)
+ ep->skip_packets = 4;
+}
+
+void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe,
+ __u8 request, __u8 requesttype, __u16 value,
+ __u16 index, void *data, __u16 size)
+{
+ /*
+ * "Playback Design" products need a 20ms delay after each
+ * class compliant request
+ */
+ if ((le16_to_cpu(dev->descriptor.idVendor) == 0x23ba) &&
+ (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS)
+ mdelay(20);
+}
+
diff --git a/sound/usb/quirks.h b/sound/usb/quirks.h
index 03e5e94..0ca9e91 100644
--- a/sound/usb/quirks.h
+++ b/sound/usb/quirks.h
@@ -1,6 +1,10 @@
#ifndef __USBAUDIO_QUIRKS_H
#define __USBAUDIO_QUIRKS_H
+struct audioformat;
+struct snd_usb_endpoint;
+struct snd_usb_substream;
+
int snd_usb_create_quirk(struct snd_usb_audio *chip,
struct usb_interface *iface,
struct usb_driver *driver,
@@ -20,4 +24,10 @@ void snd_usb_set_format_quirk(struct snd_usb_substream *subs,
int snd_usb_is_big_endian_format(struct snd_usb_audio *chip,
struct audioformat *fp);
+void snd_usb_endpoint_start_quirk(struct snd_usb_endpoint *ep);
+
+void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe,
+ __u8 request, __u8 requesttype, __u16 value,
+ __u16 index, void *data, __u16 size);
+
#endif /* __USBAUDIO_QUIRKS_H */
diff --git a/sound/usb/usx2y/us122l.c b/sound/usb/usx2y/us122l.c
index c4fd3b1..d0323a6 100644
--- a/sound/usb/usx2y/us122l.c
+++ b/sound/usb/usx2y/us122l.c
@@ -262,7 +262,7 @@ static int usb_stream_hwdep_mmap(struct snd_hwdep *hw,
}
area->vm_ops = &usb_stream_hwdep_vm_ops;
- area->vm_flags |= VM_RESERVED;
+ area->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
area->vm_private_data = us122l;
atomic_inc(&us122l->mmap_count);
out:
diff --git a/sound/usb/usx2y/usX2Yhwdep.c b/sound/usb/usx2y/usX2Yhwdep.c
index 04aafb4..0b34dbc 100644
--- a/sound/usb/usx2y/usX2Yhwdep.c
+++ b/sound/usb/usx2y/usX2Yhwdep.c
@@ -82,7 +82,7 @@ static int snd_us428ctls_mmap(struct snd_hwdep * hw, struct file *filp, struct v
us428->us428ctls_sharedmem->CtlSnapShotLast = -2;
}
area->vm_ops = &us428ctls_vm_ops;
- area->vm_flags |= VM_RESERVED | VM_DONTEXPAND;
+ area->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
area->vm_private_data = hw->private_data;
return 0;
}
diff --git a/sound/usb/usx2y/usx2yhwdeppcm.c b/sound/usb/usx2y/usx2yhwdeppcm.c
index 8e40b6e..cc56007 100644
--- a/sound/usb/usx2y/usx2yhwdeppcm.c
+++ b/sound/usb/usx2y/usx2yhwdeppcm.c
@@ -723,7 +723,7 @@ static int snd_usX2Y_hwdep_pcm_mmap(struct snd_hwdep * hw, struct file *filp, st
return -ENODEV;
}
area->vm_ops = &snd_usX2Y_hwdep_pcm_vm_ops;
- area->vm_flags |= VM_RESERVED | VM_DONTEXPAND;
+ area->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
area->vm_private_data = hw->private_data;
return 0;
}