X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=sound%2Fcore%2Fioctl32%2Fpcm32.c;h=1e37cda343d241e2c83e7d751effd18e1fcea86f;hb=6a77f38946aaee1cd85eeec6cf4229b204c15071;hp=caf5ee4c0793e73136bd93252065784d8a4d05e2;hpb=87fc8d1bb10cd459024a742c6a10961fefcef18f;p=linux-2.6.git diff --git a/sound/core/ioctl32/pcm32.c b/sound/core/ioctl32/pcm32.c index caf5ee4c0..1e37cda34 100644 --- a/sound/core/ioctl32/pcm32.c +++ b/sound/core/ioctl32/pcm32.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "ioctl32.h" @@ -41,23 +42,15 @@ struct sndrv_pcm_uframes_str32 { u32 val; }; -#define CVT_sndrv_pcm_sframes_str() { COPY(val); } -#define CVT_sndrv_pcm_uframes_str() { COPY(val); } +#define CVT_sndrv_pcm_sframes_str() { COPY_CVT(val); } +#define CVT_sndrv_pcm_uframes_str() { COPY_CVT(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_interval32 intervals[SNDRV_PCM_HW_PARAM_LAST_INTERVAL - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL + 1]; + struct sndrv_interval 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; @@ -69,31 +62,6 @@ 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; @@ -113,13 +81,13 @@ struct sndrv_pcm_sw_params32 { COPY(tstamp_mode);\ COPY(period_step);\ COPY(sleep_min);\ - COPY(avail_min);\ - COPY(xfer_align);\ - COPY(start_threshold);\ - COPY(stop_threshold);\ - COPY(silence_threshold);\ - COPY(silence_size);\ - COPY(boundary);\ + 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);\ } struct sndrv_pcm_channel_info32 { @@ -132,7 +100,7 @@ struct sndrv_pcm_channel_info32 { #define CVT_sndrv_pcm_channel_info()\ {\ COPY(channel);\ - COPY(offset);\ + COPY_CVT(offset);\ COPY(first);\ COPY(step);\ } @@ -154,26 +122,96 @@ struct sndrv_pcm_status32 { #define CVT_sndrv_pcm_status()\ {\ COPY(state);\ - 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_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(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 { @@ -185,24 +223,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 data; - mm_segment_t oldseg; + struct sndrv_xferi __user *data; + snd_pcm_sframes_t result; int err; if (copy_from_user(&data32, (void __user *)arg, sizeof(data32))) return -EFAULT; - 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); + 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); if (err < 0) return err; /* copy the result */ - data32.result = data.result; + if (__get_user(result, &data->result)) + return -EFAULT; + data32.result = result; if (copy_to_user((void __user *)arg, &data32, sizeof(data32))) return -EFAULT; return 0; @@ -226,18 +264,20 @@ 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; - struct sndrv_xfern32 __user *srcptr = (void __user *)arg; - void __user **bufs = NULL; + void __user **bufs; int err = 0, ch, i; u32 __user *bufptr; - mm_segment_t oldseg; - /* 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); + 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; /* check validty of the command */ switch (native_ctl) { @@ -254,22 +294,21 @@ static int _snd_ioctl32_xfern(unsigned int fd, unsigned int cmd, unsigned long a } if ((ch = substream->runtime->channels) > 128) return -EINVAL; - if (get_user(data32.frames, &srcptr->frames)) + if (copy_from_user(&data32, (void __user *)arg, sizeof(data32))) return -EFAULT; - __get_user(data32.bufs, &srcptr->bufs); bufptr = compat_ptr(data32.bufs); - bufs = kmalloc(sizeof(void *) * 128, GFP_KERNEL); + bufs = kmalloc(sizeof(void __user *) * ch, GFP_KERNEL); if (bufs == NULL) return -ENOMEM; for (i = 0; i < ch; i++) { u32 ptr; - if (get_user(ptr, bufptr)) + if (get_user(ptr, bufptr)) { + kfree(bufs); 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); @@ -278,107 +317,15 @@ 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 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; @@ -409,15 +356,15 @@ struct sndrv_pcm_sync_ptr32 { COPY(flags);\ COPY(s.status.state);\ COPY(s.status.pad1);\ - COPY(s.status.hw_ptr);\ - COPY(s.status.tstamp.tv_sec);\ - COPY(s.status.tstamp.tv_nsec);\ + COPY_CVT(s.status.hw_ptr);\ + COPY_CVT(s.status.tstamp.tv_sec);\ + COPY_CVT(s.status.tstamp.tv_nsec);\ COPY(s.status.suspended_state);\ - COPY(c.control.appl_ptr);\ - COPY(c.control.avail_min);\ + COPY_CVT(c.control.appl_ptr);\ + COPY_CVT(c.control.avail_min);\ } -DEFINE_ALSA_IOCTL_BIG(pcm_sync_ptr); +DEFINE_ALSA_IOCTL(pcm_sync_ptr); /* */ @@ -425,12 +372,11 @@ DEFINE_ALSA_IOCTL_BIG(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); DEFINE_ALSA_IOCTL_ENTRY(pcm_rewind, pcm_uframes_str, SNDRV_PCM_IOCTL_REWIND); +DEFINE_ALSA_IOCTL_ENTRY(pcm_forward, pcm_uframes_str, SNDRV_PCM_IOCTL_FORWARD); DEFINE_ALSA_IOCTL_ENTRY(pcm_readi, xferi, SNDRV_PCM_IOCTL_READI_FRAMES); DEFINE_ALSA_IOCTL_ENTRY(pcm_writei, xferi, SNDRV_PCM_IOCTL_WRITEI_FRAMES); DEFINE_ALSA_IOCTL_ENTRY(pcm_readn, xfern, SNDRV_PCM_IOCTL_READN_FRAMES); @@ -438,6 +384,30 @@ 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 @@ -450,24 +420,22 @@ enum { SNDRV_PCM_IOCTL_DELAY32 = _IOR('A', 0x21, s32), SNDRV_PCM_IOCTL_CHANNEL_INFO32 = _IOR('A', 0x32, struct sndrv_pcm_channel_info32), SNDRV_PCM_IOCTL_REWIND32 = _IOW('A', 0x46, u32), + SNDRV_PCM_IOCTL_FORWARD32 = _IOW('A', 0x49, u32), SNDRV_PCM_IOCTL_WRITEI_FRAMES32 = _IOW('A', 0x50, struct sndrv_xferi32), 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_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), + SNDRV_PCM_IOCTL_SYNC_PTR32 = _IOWR('A', 0x23, struct sndrv_pcm_sync_ptr32), }; struct ioctl32_mapper pcm_mappers[] = { MAP_COMPAT(SNDRV_PCM_IOCTL_PVERSION), - MAP_COMPAT(SNDRV_PCM_IOCTL_INFO), + /* MAP_COMPAT(SNDRV_PCM_IOCTL_INFO), */ + { SNDRV_PCM_IOCTL_INFO, snd_pcm_info_ioctl32 }, 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) }, @@ -484,6 +452,7 @@ struct ioctl32_mapper pcm_mappers[] = { { SNDRV_PCM_IOCTL_REWIND32, AP(pcm_rewind) }, MAP_COMPAT(SNDRV_PCM_IOCTL_RESUME), MAP_COMPAT(SNDRV_PCM_IOCTL_XRUN), + { SNDRV_PCM_IOCTL_FORWARD32, AP(pcm_forward) }, { SNDRV_PCM_IOCTL_WRITEI_FRAMES32, AP(pcm_writei) }, { SNDRV_PCM_IOCTL_READI_FRAMES32, AP(pcm_readi) }, { SNDRV_PCM_IOCTL_WRITEN_FRAMES32, AP(pcm_writen) },