This commit was manufactured by cvs2svn to create tag
[linux-2.6.git] / sound / core / ioctl32 / pcm32.c
index 66ce5e1..caf5ee4 100644 (file)
@@ -24,7 +24,6 @@
 #include <linux/compat.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
-#include <sound/minors.h>
 #include "ioctl32.h"
 
 
@@ -42,15 +41,23 @@ struct sndrv_pcm_uframes_str32 {
        u32 val;
 };
 
-#define CVT_sndrv_pcm_sframes_str() { COPY_CVT(val); }
-#define CVT_sndrv_pcm_uframes_str() { COPY_CVT(val); }
+#define CVT_sndrv_pcm_sframes_str() { COPY(val); }
+#define CVT_sndrv_pcm_uframes_str() { COPY(val); }
 
 
+struct sndrv_interval32 {
+       u32 min, max;
+       unsigned int openmin:1,
+                    openmax:1,
+                    integer:1,
+                    empty:1;
+};
+
 struct sndrv_pcm_hw_params32 {
        u32 flags;
        struct sndrv_mask masks[SNDRV_PCM_HW_PARAM_LAST_MASK - SNDRV_PCM_HW_PARAM_FIRST_MASK + 1]; /* this must be identical */
        struct sndrv_mask mres[5];      /* reserved masks */
-       struct sndrv_interval intervals[SNDRV_PCM_HW_PARAM_LAST_INTERVAL - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL + 1];
+       struct sndrv_interval32 intervals[SNDRV_PCM_HW_PARAM_LAST_INTERVAL - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL + 1];
        struct sndrv_interval ires[9];  /* reserved intervals */
        u32 rmask;
        u32 cmask;
@@ -62,6 +69,31 @@ struct sndrv_pcm_hw_params32 {
        unsigned char reserved[64];
 } __attribute__((packed));
 
+#define numberof(array) ARRAY_SIZE(array)
+
+#define CVT_sndrv_pcm_hw_params()\
+{\
+       unsigned int i;\
+       COPY(flags);\
+       for (i = 0; i < numberof(dst->masks); i++)\
+               COPY(masks[i]);\
+       for (i = 0; i < numberof(dst->intervals); i++) {\
+               COPY(intervals[i].min);\
+               COPY(intervals[i].max);\
+               COPY(intervals[i].openmin);\
+               COPY(intervals[i].openmax);\
+               COPY(intervals[i].integer);\
+               COPY(intervals[i].empty);\
+       }\
+       COPY(rmask);\
+       COPY(cmask);\
+       COPY(info);\
+       COPY(msbits);\
+       COPY(rate_num);\
+       COPY(rate_den);\
+       COPY(fifo_size);\
+}
+
 struct sndrv_pcm_sw_params32 {
        s32 tstamp_mode;
        u32 period_step;
@@ -81,13 +113,13 @@ struct sndrv_pcm_sw_params32 {
        COPY(tstamp_mode);\
        COPY(period_step);\
        COPY(sleep_min);\
-       COPY_CVT(avail_min);\
-       COPY_CVT(xfer_align);\
-       COPY_CVT(start_threshold);\
-       COPY_CVT(stop_threshold);\
-       COPY_CVT(silence_threshold);\
-       COPY_CVT(silence_size);\
-       COPY_CVT(boundary);\
+       COPY(avail_min);\
+       COPY(xfer_align);\
+       COPY(start_threshold);\
+       COPY(stop_threshold);\
+       COPY(silence_threshold);\
+       COPY(silence_size);\
+       COPY(boundary);\
 }
 
 struct sndrv_pcm_channel_info32 {
@@ -100,7 +132,7 @@ struct sndrv_pcm_channel_info32 {
 #define CVT_sndrv_pcm_channel_info()\
 {\
        COPY(channel);\
-       COPY_CVT(offset);\
+       COPY(offset);\
        COPY(first);\
        COPY(step);\
 }
@@ -122,96 +154,26 @@ struct sndrv_pcm_status32 {
 #define CVT_sndrv_pcm_status()\
 {\
        COPY(state);\
-       COPY_CVT(trigger_tstamp.tv_sec);\
-       COPY_CVT(trigger_tstamp.tv_nsec);\
-       COPY_CVT(tstamp.tv_sec);\
-       COPY_CVT(tstamp.tv_nsec);\
-       COPY_CVT(appl_ptr);\
-       COPY_CVT(hw_ptr);\
-       COPY_CVT(delay);\
-       COPY_CVT(avail);\
-       COPY_CVT(avail_max);\
-       COPY_CVT(overrange);\
+       COPY(trigger_tstamp.tv_sec);\
+       COPY(trigger_tstamp.tv_nsec);\
+       COPY(tstamp.tv_sec);\
+       COPY(tstamp.tv_nsec);\
+       COPY(appl_ptr);\
+       COPY(hw_ptr);\
+       COPY(delay);\
+       COPY(avail);\
+       COPY(avail_max);\
+       COPY(overrange);\
        COPY(suspended_state);\
 }
 
 DEFINE_ALSA_IOCTL(pcm_uframes_str);
 DEFINE_ALSA_IOCTL(pcm_sframes_str);
+DEFINE_ALSA_IOCTL_BIG(pcm_hw_params);
 DEFINE_ALSA_IOCTL(pcm_sw_params);
 DEFINE_ALSA_IOCTL(pcm_channel_info);
 DEFINE_ALSA_IOCTL(pcm_status);
 
-/* sanity device check */
-extern int snd_major;
-static int sanity_check_pcm(struct file *file)
-{
-       unsigned short minor;
-       if (imajor(file->f_dentry->d_inode) != snd_major)
-               return -ENOTTY;
-       minor = iminor(file->f_dentry->d_inode);
-       if (minor >= 256 || 
-           minor % SNDRV_MINOR_DEVICES < SNDRV_MINOR_PCM_PLAYBACK)
-               return -ENOTTY;
-       return 0;
-}
-
-/* recalcuate the boundary within 32bit */
-static void recalculate_boundary(snd_pcm_runtime_t *runtime)
-{
-       if (! runtime->buffer_size)
-               return;
-       runtime->boundary = runtime->buffer_size;
-       while (runtime->boundary * 2 <= 0x7fffffffUL - runtime->buffer_size)
-               runtime->boundary *= 2;
-}
-
-/* both for HW_PARAMS and HW_REFINE */
-static int _snd_ioctl32_pcm_hw_params(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file, unsigned int native_ctl)
-{
-       struct sndrv_pcm_hw_params32 __user *data32;
-       struct sndrv_pcm_hw_params *data;
-       snd_pcm_file_t *pcm_file;
-       snd_pcm_substream_t *substream;
-       snd_pcm_runtime_t *runtime;
-       int err;
-
-       if (sanity_check_pcm(file))
-               return -ENOTTY;
-       if (! (pcm_file = file->private_data))
-               return -ENOTTY;
-       if (! (substream = pcm_file->substream))
-               return -ENOTTY;
-       if (! (runtime = substream->runtime))
-               return -ENOTTY;
-
-       data32 = compat_ptr(arg);
-       data = kmalloc(sizeof(*data), GFP_KERNEL);
-       if (data == NULL)
-               return -ENOMEM;
-       if (copy_from_user(data, data32, sizeof(*data32))) {
-               err = -EFAULT;
-               goto error;
-       }
-       if (native_ctl == SNDRV_PCM_IOCTL_HW_REFINE)
-               err = snd_pcm_hw_refine(substream, data);
-       else
-               err = snd_pcm_hw_params(substream, data);
-       if (err < 0)
-               goto error;
-       if (copy_to_user(data32, data, sizeof(*data32)) ||
-           __put_user((u32)data->fifo_size, &data32->fifo_size)) {
-               err = -EFAULT;
-               goto error;
-       }
-
-       if (native_ctl == SNDRV_PCM_IOCTL_HW_PARAMS)
-               recalculate_boundary(runtime);
- error:
-       kfree(data);
-       return err;
-}
-
-
 /*
  */
 struct sndrv_xferi32 {
@@ -223,24 +185,24 @@ struct sndrv_xferi32 {
 static int _snd_ioctl32_xferi(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file, unsigned int native_ctl)
 {
        struct sndrv_xferi32 data32;
-       struct sndrv_xferi __user *data;
-       snd_pcm_sframes_t result;
+       struct sndrv_xferi data;
+       mm_segment_t oldseg;
        int err;
 
        if (copy_from_user(&data32, (void __user *)arg, sizeof(data32)))
                return -EFAULT;
-       data = compat_alloc_user_space(sizeof(*data));
-       if (put_user((snd_pcm_sframes_t)data32.result, &data->result) ||
-           __put_user(compat_ptr(data32.buf), &data->buf) ||
-           __put_user((snd_pcm_uframes_t)data32.frames, &data->frames))
-               return -EFAULT;
-       err = file->f_op->ioctl(file->f_dentry->d_inode, file, native_ctl, (unsigned long)data);
+       memset(&data, 0, sizeof(data));
+       data.result = data32.result;
+       data.buf = compat_ptr(data32.buf);
+       data.frames = data32.frames;
+       oldseg = get_fs();
+       set_fs(KERNEL_DS);
+       err = file->f_op->ioctl(file->f_dentry->d_inode, file, native_ctl, (unsigned long)&data);
+       set_fs(oldseg);
        if (err < 0)
                return err;
        /* copy the result */
-       if (__get_user(result, &data->result))
-               return -EFAULT;
-       data32.result = result;
+       data32.result = data.result;
        if (copy_to_user((void __user *)arg, &data32, sizeof(data32)))
                return -EFAULT;
        return 0;
@@ -264,20 +226,18 @@ static int _snd_ioctl32_xfern(unsigned int fd, unsigned int cmd, unsigned long a
 {
        snd_pcm_file_t *pcm_file;
        snd_pcm_substream_t *substream;
-       struct sndrv_xfern32 __user *srcptr = compat_ptr(arg);
        struct sndrv_xfern32 data32;
-       void __user **bufs;
+       struct sndrv_xfern32 __user *srcptr = (void __user *)arg;
+       void __user **bufs = NULL;
        int err = 0, ch, i;
        u32 __user *bufptr;
+       mm_segment_t oldseg;
 
-       if (sanity_check_pcm(file))
-               return -ENOTTY;
-       if (! (pcm_file = file->private_data))
-               return -ENOTTY;
-       if (! (substream = pcm_file->substream))
-               return -ENOTTY;
-       if (! substream->runtime)
-               return -ENOTTY;
+       /* FIXME: need to check whether fop->ioctl is sane */
+
+       pcm_file = file->private_data;
+       substream = pcm_file->substream;
+       snd_assert(substream != NULL && substream->runtime, return -ENXIO);
 
        /* check validty of the command */
        switch (native_ctl) {
@@ -294,21 +254,22 @@ static int _snd_ioctl32_xfern(unsigned int fd, unsigned int cmd, unsigned long a
        }
        if ((ch = substream->runtime->channels) > 128)
                return -EINVAL;
-       if (copy_from_user(&data32, (void __user *)arg, sizeof(data32)))
+       if (get_user(data32.frames, &srcptr->frames))
                return -EFAULT;
+       __get_user(data32.bufs, &srcptr->bufs);
        bufptr = compat_ptr(data32.bufs);
-       bufs = kmalloc(sizeof(void __user *) * ch, GFP_KERNEL);
+       bufs = kmalloc(sizeof(void *) * 128, GFP_KERNEL);
        if (bufs == NULL)
                return -ENOMEM;
        for (i = 0; i < ch; i++) {
                u32 ptr;
-               if (get_user(ptr, bufptr)) {
-                       kfree(bufs);
+               if (get_user(ptr, bufptr))
                        return -EFAULT;
-               }
                bufs[ch] = compat_ptr(ptr);
                bufptr++;
        }
+       oldseg = get_fs();
+       set_fs(KERNEL_DS);
        switch (native_ctl) {
        case SNDRV_PCM_IOCTL_WRITEN_FRAMES:
                err = snd_pcm_lib_writev(substream, bufs, data32.frames);
@@ -317,15 +278,107 @@ static int _snd_ioctl32_xfern(unsigned int fd, unsigned int cmd, unsigned long a
                err = snd_pcm_lib_readv(substream, bufs, data32.frames);
                break;
        }
+       set_fs(oldseg);
        if (err >= 0) {
                if (put_user(err, &srcptr->result))
                        err = -EFAULT;
        }
        kfree(bufs);
-       return err;
+       return 0;
 }
 
 
+struct sndrv_pcm_hw_params_old32 {
+       u32 flags;
+       u32 masks[SNDRV_PCM_HW_PARAM_SUBFORMAT -
+                          SNDRV_PCM_HW_PARAM_ACCESS + 1];
+       struct sndrv_interval32 intervals[SNDRV_PCM_HW_PARAM_TICK_TIME -
+                                       SNDRV_PCM_HW_PARAM_SAMPLE_BITS + 1];
+       u32 rmask;
+       u32 cmask;
+       u32 info;
+       u32 msbits;
+       u32 rate_num;
+       u32 rate_den;
+       u32 fifo_size;
+       unsigned char reserved[64];
+} __attribute__((packed));
+
+#define __OLD_TO_NEW_MASK(x) ((x&7)|((x&0x07fffff8)<<5))
+#define __NEW_TO_OLD_MASK(x) ((x&7)|((x&0xffffff00)>>5))
+
+static void snd_pcm_hw_convert_from_old_params(snd_pcm_hw_params_t *params, struct sndrv_pcm_hw_params_old32 *oparams)
+{
+       unsigned int i;
+
+       memset(params, 0, sizeof(*params));
+       params->flags = oparams->flags;
+       for (i = 0; i < ARRAY_SIZE(oparams->masks); i++)
+               params->masks[i].bits[0] = oparams->masks[i];
+       memcpy(params->intervals, oparams->intervals, sizeof(oparams->intervals));
+       params->rmask = __OLD_TO_NEW_MASK(oparams->rmask);
+       params->cmask = __OLD_TO_NEW_MASK(oparams->cmask);
+       params->info = oparams->info;
+       params->msbits = oparams->msbits;
+       params->rate_num = oparams->rate_num;
+       params->rate_den = oparams->rate_den;
+       params->fifo_size = oparams->fifo_size;
+}
+
+static void snd_pcm_hw_convert_to_old_params(struct sndrv_pcm_hw_params_old32 *oparams, snd_pcm_hw_params_t *params)
+{
+       unsigned int i;
+
+       memset(oparams, 0, sizeof(*oparams));
+       oparams->flags = params->flags;
+       for (i = 0; i < ARRAY_SIZE(oparams->masks); i++)
+               oparams->masks[i] = params->masks[i].bits[0];
+       memcpy(oparams->intervals, params->intervals, sizeof(oparams->intervals));
+       oparams->rmask = __NEW_TO_OLD_MASK(params->rmask);
+       oparams->cmask = __NEW_TO_OLD_MASK(params->cmask);
+       oparams->info = params->info;
+       oparams->msbits = params->msbits;
+       oparams->rate_num = params->rate_num;
+       oparams->rate_den = params->rate_den;
+       oparams->fifo_size = params->fifo_size;
+}
+
+static int _snd_ioctl32_pcm_hw_params_old(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file, unsigned int native_ctl)
+{
+       struct sndrv_pcm_hw_params_old32 *data32;
+       struct sndrv_pcm_hw_params *data;
+       mm_segment_t oldseg;
+       int err;
+
+       data32 = kcalloc(1, sizeof(*data32), GFP_KERNEL);
+       data = kcalloc(1, sizeof(*data), GFP_KERNEL);
+       if (data32 == NULL || data == NULL) {
+               err = -ENOMEM;
+               goto __end;
+       }
+       if (copy_from_user(data32, (void __user *)arg, sizeof(*data32))) {
+               err = -EFAULT;
+               goto __end;
+       }
+       snd_pcm_hw_convert_from_old_params(data, data32);
+       oldseg = get_fs();
+       set_fs(KERNEL_DS);
+       err = file->f_op->ioctl(file->f_dentry->d_inode, file, native_ctl, (unsigned long)data);
+       set_fs(oldseg);
+       if (err < 0)
+               goto __end;
+       snd_pcm_hw_convert_to_old_params(data32, data);
+       err = 0;
+       if (copy_to_user((void __user *)arg, data32, sizeof(*data32)))
+               err = -EFAULT;
+      __end:
+       if (data)
+               kfree(data);
+       if (data32)
+               kfree(data32);
+       return err;
+}
+
 struct sndrv_pcm_mmap_status32 {
        s32 state;
        s32 pad1;
@@ -356,15 +409,15 @@ struct sndrv_pcm_sync_ptr32 {
        COPY(flags);\
        COPY(s.status.state);\
        COPY(s.status.pad1);\
-       COPY_CVT(s.status.hw_ptr);\
-       COPY_CVT(s.status.tstamp.tv_sec);\
-       COPY_CVT(s.status.tstamp.tv_nsec);\
+       COPY(s.status.hw_ptr);\
+       COPY(s.status.tstamp.tv_sec);\
+       COPY(s.status.tstamp.tv_nsec);\
        COPY(s.status.suspended_state);\
-       COPY_CVT(c.control.appl_ptr);\
-       COPY_CVT(c.control.avail_min);\
+       COPY(c.control.appl_ptr);\
+       COPY(c.control.avail_min);\
 }
 
-DEFINE_ALSA_IOCTL(pcm_sync_ptr);
+DEFINE_ALSA_IOCTL_BIG(pcm_sync_ptr);
 
 /*
  */
@@ -372,6 +425,8 @@ DEFINE_ALSA_IOCTL(pcm_sync_ptr);
 DEFINE_ALSA_IOCTL_ENTRY(pcm_hw_refine, pcm_hw_params, SNDRV_PCM_IOCTL_HW_REFINE);
 DEFINE_ALSA_IOCTL_ENTRY(pcm_hw_params, pcm_hw_params, SNDRV_PCM_IOCTL_HW_PARAMS);
 DEFINE_ALSA_IOCTL_ENTRY(pcm_sw_params, pcm_sw_params, SNDRV_PCM_IOCTL_SW_PARAMS);
+DEFINE_ALSA_IOCTL_ENTRY(pcm_hw_refine_old, pcm_hw_params_old, SNDRV_PCM_IOCTL_HW_REFINE);
+DEFINE_ALSA_IOCTL_ENTRY(pcm_hw_params_old, pcm_hw_params_old, SNDRV_PCM_IOCTL_HW_PARAMS);
 DEFINE_ALSA_IOCTL_ENTRY(pcm_status, pcm_status, SNDRV_PCM_IOCTL_STATUS);
 DEFINE_ALSA_IOCTL_ENTRY(pcm_delay, pcm_sframes_str, SNDRV_PCM_IOCTL_DELAY);
 DEFINE_ALSA_IOCTL_ENTRY(pcm_channel_info, pcm_channel_info, SNDRV_PCM_IOCTL_CHANNEL_INFO);
@@ -383,30 +438,6 @@ DEFINE_ALSA_IOCTL_ENTRY(pcm_writen, xfern, SNDRV_PCM_IOCTL_WRITEN_FRAMES);
 DEFINE_ALSA_IOCTL_ENTRY(pcm_sync_ptr, pcm_sync_ptr, SNDRV_PCM_IOCTL_SYNC_PTR);
 
 
-/*
- * When PCM is used on 32bit mode, we need to disable
- * mmap of PCM status/control records because of the size
- * incompatibility.
- * 
- * Since INFO ioctl is always called at first, we mark the
- * mmap-disabling in this ioctl wrapper.
- */
-static int snd_pcm_info_ioctl32(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *filp)
-{
-       snd_pcm_file_t *pcm_file;
-       snd_pcm_substream_t *substream;
-       if (! filp->f_op || ! filp->f_op->ioctl)
-               return -ENOTTY;
-       pcm_file = filp->private_data;
-       if (! pcm_file)
-               return -ENOTTY;
-       substream = pcm_file->substream;
-       if (! substream)
-               return -ENOTTY;
-       substream->no_mmap_ctrl = 1;
-       return filp->f_op->ioctl(filp->f_dentry->d_inode, filp, cmd, arg);
-}
-
 /*
  */
 #define AP(x) snd_ioctl32_##x
@@ -423,17 +454,20 @@ enum {
        SNDRV_PCM_IOCTL_READI_FRAMES32 = _IOR('A', 0x51, struct sndrv_xferi32),
        SNDRV_PCM_IOCTL_WRITEN_FRAMES32 = _IOW('A', 0x52, struct sndrv_xfern32),
        SNDRV_PCM_IOCTL_READN_FRAMES32 = _IOR('A', 0x53, struct sndrv_xfern32),
-       SNDRV_PCM_IOCTL_SYNC_PTR32 = _IOWR('A', 0x23, struct sndrv_pcm_sync_ptr32),
+       SNDRV_PCM_IOCTL_HW_REFINE_OLD32 = _IOWR('A', 0x10, struct sndrv_pcm_hw_params_old32),
+       SNDRV_PCM_IOCTL_HW_PARAMS_OLD32 = _IOWR('A', 0x11, struct sndrv_pcm_hw_params_old32),
+       SNDRV_PCM_IOCTL_SYNC_PTR32 = _IOWR('A', 0x23, struct sndrv_pcm_sync_ptr),
 
 };
 
 struct ioctl32_mapper pcm_mappers[] = {
        MAP_COMPAT(SNDRV_PCM_IOCTL_PVERSION),
-       /* MAP_COMPAT(SNDRV_PCM_IOCTL_INFO), */
-       { SNDRV_PCM_IOCTL_INFO, snd_pcm_info_ioctl32 },
+       MAP_COMPAT(SNDRV_PCM_IOCTL_INFO),
        MAP_COMPAT(SNDRV_PCM_IOCTL_TSTAMP),
        { SNDRV_PCM_IOCTL_HW_REFINE32, AP(pcm_hw_refine) },
        { SNDRV_PCM_IOCTL_HW_PARAMS32, AP(pcm_hw_params) },
+       { SNDRV_PCM_IOCTL_HW_REFINE_OLD32, AP(pcm_hw_refine_old) },
+       { SNDRV_PCM_IOCTL_HW_PARAMS_OLD32, AP(pcm_hw_params_old) },
        MAP_COMPAT(SNDRV_PCM_IOCTL_HW_FREE),
        { SNDRV_PCM_IOCTL_SW_PARAMS32, AP(pcm_sw_params) },
        { SNDRV_PCM_IOCTL_STATUS32, AP(pcm_status) },