1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * 32bit -> 64bit ioctl wrapper for timer API |
4 | * Copyright (c) by Takashi Iwai <tiwai@suse.de> |
5 | */ |
6 | |
7 | /* This file included from timer.c */ |
8 | |
9 | #include <linux/compat.h> |
10 | |
11 | /* |
12 | * ILP32/LP64 has different size for 'long' type. Additionally, the size |
13 | * of storage alignment differs depending on architectures. Here, '__packed' |
14 | * qualifier is used so that the size of this structure is multiple of 4 and |
15 | * it fits to any architectures with 32 bit storage alignment. |
16 | */ |
17 | struct snd_timer_gparams32 { |
18 | struct snd_timer_id tid; |
19 | u32 period_num; |
20 | u32 period_den; |
21 | unsigned char reserved[32]; |
22 | } __packed; |
23 | |
24 | struct snd_timer_info32 { |
25 | u32 flags; |
26 | s32 card; |
27 | unsigned char id[64]; |
28 | unsigned char name[80]; |
29 | u32 reserved0; |
30 | u32 resolution; |
31 | unsigned char reserved[64]; |
32 | }; |
33 | |
34 | static int snd_timer_user_gparams_compat(struct file *file, |
35 | struct snd_timer_gparams32 __user *user) |
36 | { |
37 | struct snd_timer_gparams gparams; |
38 | |
39 | if (copy_from_user(to: &gparams.tid, from: &user->tid, n: sizeof(gparams.tid)) || |
40 | get_user(gparams.period_num, &user->period_num) || |
41 | get_user(gparams.period_den, &user->period_den)) |
42 | return -EFAULT; |
43 | |
44 | return timer_set_gparams(gparams: &gparams); |
45 | } |
46 | |
47 | static int snd_timer_user_info_compat(struct file *file, |
48 | struct snd_timer_info32 __user *_info) |
49 | { |
50 | struct snd_timer_user *tu; |
51 | struct snd_timer_info32 info; |
52 | struct snd_timer *t; |
53 | |
54 | tu = file->private_data; |
55 | if (!tu->timeri) |
56 | return -EBADFD; |
57 | t = tu->timeri->timer; |
58 | if (!t) |
59 | return -EBADFD; |
60 | memset(&info, 0, sizeof(info)); |
61 | info.card = t->card ? t->card->number : -1; |
62 | if (t->hw.flags & SNDRV_TIMER_HW_SLAVE) |
63 | info.flags |= SNDRV_TIMER_FLG_SLAVE; |
64 | strscpy(info.id, t->id, sizeof(info.id)); |
65 | strscpy(info.name, t->name, sizeof(info.name)); |
66 | info.resolution = t->hw.resolution; |
67 | if (copy_to_user(to: _info, from: &info, n: sizeof(*_info))) |
68 | return -EFAULT; |
69 | return 0; |
70 | } |
71 | |
72 | enum { |
73 | SNDRV_TIMER_IOCTL_GPARAMS32 = _IOW('T', 0x04, struct snd_timer_gparams32), |
74 | SNDRV_TIMER_IOCTL_INFO32 = _IOR('T', 0x11, struct snd_timer_info32), |
75 | SNDRV_TIMER_IOCTL_STATUS_COMPAT32 = _IOW('T', 0x14, struct snd_timer_status32), |
76 | SNDRV_TIMER_IOCTL_STATUS_COMPAT64 = _IOW('T', 0x14, struct snd_timer_status64), |
77 | }; |
78 | |
79 | static long __snd_timer_user_ioctl_compat(struct file *file, unsigned int cmd, |
80 | unsigned long arg) |
81 | { |
82 | void __user *argp = compat_ptr(uptr: arg); |
83 | |
84 | switch (cmd) { |
85 | case SNDRV_TIMER_IOCTL_PVERSION: |
86 | case SNDRV_TIMER_IOCTL_TREAD_OLD: |
87 | case SNDRV_TIMER_IOCTL_TREAD64: |
88 | case SNDRV_TIMER_IOCTL_GINFO: |
89 | case SNDRV_TIMER_IOCTL_GSTATUS: |
90 | case SNDRV_TIMER_IOCTL_SELECT: |
91 | case SNDRV_TIMER_IOCTL_PARAMS: |
92 | case SNDRV_TIMER_IOCTL_START: |
93 | case SNDRV_TIMER_IOCTL_START_OLD: |
94 | case SNDRV_TIMER_IOCTL_STOP: |
95 | case SNDRV_TIMER_IOCTL_STOP_OLD: |
96 | case SNDRV_TIMER_IOCTL_CONTINUE: |
97 | case SNDRV_TIMER_IOCTL_CONTINUE_OLD: |
98 | case SNDRV_TIMER_IOCTL_PAUSE: |
99 | case SNDRV_TIMER_IOCTL_PAUSE_OLD: |
100 | case SNDRV_TIMER_IOCTL_NEXT_DEVICE: |
101 | return __snd_timer_user_ioctl(file, cmd, arg: (unsigned long)argp, compat: true); |
102 | case SNDRV_TIMER_IOCTL_GPARAMS32: |
103 | return snd_timer_user_gparams_compat(file, user: argp); |
104 | case SNDRV_TIMER_IOCTL_INFO32: |
105 | return snd_timer_user_info_compat(file, info: argp); |
106 | case SNDRV_TIMER_IOCTL_STATUS_COMPAT32: |
107 | return snd_timer_user_status32(file, status: argp); |
108 | case SNDRV_TIMER_IOCTL_STATUS_COMPAT64: |
109 | return snd_timer_user_status64(file, status: argp); |
110 | } |
111 | return -ENOIOCTLCMD; |
112 | } |
113 | |
114 | static long snd_timer_user_ioctl_compat(struct file *file, unsigned int cmd, |
115 | unsigned long arg) |
116 | { |
117 | struct snd_timer_user *tu = file->private_data; |
118 | |
119 | guard(mutex)(T: &tu->ioctl_lock); |
120 | return __snd_timer_user_ioctl_compat(file, cmd, arg); |
121 | } |
122 | |