github.com/afumu/libc@v0.0.6/musl/src/misc/ioctl.c (about)

     1  #include <sys/ioctl.h>
     2  #include <stdarg.h>
     3  #include <errno.h>
     4  #include <time.h>
     5  #include <sys/time.h>
     6  #include <stddef.h>
     7  #include <string.h>
     8  #include "syscall.h"
     9  
    10  #define alignof(t) offsetof(struct { char c; t x; }, x)
    11  
    12  #define W 1
    13  #define R 2
    14  #define WR 3
    15  
    16  struct ioctl_compat_map {
    17  	int new_req, old_req;
    18  	unsigned char old_size, dir, force_align, noffs;
    19  	unsigned char offsets[8];
    20  };
    21  
    22  #define NINTH(a,b,c,d,e,f,g,h,i,...) i
    23  #define COUNT(...) NINTH(__VA_ARGS__,8,7,6,5,4,3,2,1,0)
    24  #define OFFS(...) COUNT(__VA_ARGS__), { __VA_ARGS__ }
    25  
    26  /* yields a type for a struct with original size n, with a misaligned
    27   * timeval/timespec expanded from 32- to 64-bit. for use with ioctl
    28   * number producing macros; only size of result is meaningful. */
    29  #define new_misaligned(n) struct { int i; time_t t; char c[(n)-4]; }
    30  
    31  static const struct ioctl_compat_map compat_map[] = {
    32  	{ SIOCGSTAMP, SIOCGSTAMP_OLD, 8, R, 0, OFFS(0, 4) },
    33  	{ SIOCGSTAMPNS, SIOCGSTAMPNS_OLD, 8, R, 0, OFFS(0, 4) },
    34  
    35  	/* SNDRV_TIMER_IOCTL_STATUS */
    36  	{ _IOR('T', 0x14, char[96]), _IOR('T', 0x14, 88), 88, R, 0, OFFS(0,4) },
    37  
    38  	/* SNDRV_PCM_IOCTL_STATUS[_EXT] */
    39  	{ _IOR('A', 0x20, char[128]), _IOR('A', 0x20, char[108]), 108, R, 1, OFFS(4,8,12,16,52,56,60,64) },
    40  	{ _IOWR('A', 0x24, char[128]), _IOWR('A', 0x24, char[108]), 108, WR, 1, OFFS(4,8,12,16,52,56,60,64) },
    41  
    42  	/* SNDRV_RAWMIDI_IOCTL_STATUS */
    43  	{ _IOWR('W', 0x20, char[48]), _IOWR('W', 0x20, char[36]), 36, WR, 1, OFFS(4,8) },
    44  
    45  	/* SNDRV_PCM_IOCTL_SYNC_PTR - with 3 subtables */
    46  	{ _IOWR('A', 0x23, char[136]), _IOWR('A', 0x23, char[132]), 0, WR, 1, 0 },
    47  	{ 0, 0, 4, WR, 1, 0 }, /* snd_pcm_sync_ptr (flags only) */
    48  	{ 0, 0, 32, WR, 1, OFFS(8,12,16,24,28) }, /* snd_pcm_mmap_status */
    49  	{ 0, 0, 8, WR, 1, OFFS(0,4) }, /* snd_pcm_mmap_control */
    50  
    51  	/* VIDIOC_QUERYBUF, VIDIOC_QBUF, VIDIOC_DQBUF, VIDIOC_PREPARE_BUF */
    52  	{ _IOWR('V',  9, new_misaligned(72)), _IOWR('V',  9, char[72]), 72, WR, 0, OFFS(20) },
    53  	{ _IOWR('V', 15, new_misaligned(72)), _IOWR('V', 15, char[72]), 72, WR, 0, OFFS(20) },
    54  	{ _IOWR('V', 17, new_misaligned(72)), _IOWR('V', 17, char[72]), 72, WR, 0, OFFS(20) },
    55  	{ _IOWR('V', 93, new_misaligned(72)), _IOWR('V', 93, char[72]), 72, WR, 0, OFFS(20) },
    56  
    57  	/* VIDIOC_DQEVENT */
    58  	{ _IOR('V', 89, new_misaligned(96)), _IOR('V', 89, char[96]), 96, R, 0, OFFS(76,80) },
    59  
    60  	/* VIDIOC_OMAP3ISP_STAT_REQ */
    61  	{ _IOWR('V', 192+6, char[32]), _IOWR('V', 192+6, char[24]), 22, WR, 0, OFFS(0,4) },
    62  
    63  	/* PPPIOCGIDLE */
    64  	{ _IOR('t', 63, char[16]), _IOR('t', 63, char[8]), 8, R, 0, OFFS(0,4) },
    65  
    66  	/* PPGETTIME, PPSETTIME */
    67  	{ _IOR('p', 0x95, char[16]), _IOR('p', 0x95, char[8]), 8, R, 0, OFFS(0,4) },
    68  	{ _IOW('p', 0x96, char[16]), _IOW('p', 0x96, char[8]), 8, W, 0, OFFS(0,4) },
    69  
    70  	/* LPSETTIMEOUT */
    71  	{ _IOW(0x6, 0xf, char[16]), 0x060f, 8, W, 0, OFFS(0,4) },
    72  };
    73  
    74  static void convert_ioctl_struct(const struct ioctl_compat_map *map, char *old, char *new, int dir)
    75  {
    76  	int new_offset = 0;
    77  	int old_offset = 0;
    78  	int old_size = map->old_size;
    79  	if (!(dir & map->dir)) return;
    80  	if (!map->old_size) {
    81  		/* offsets hard-coded for SNDRV_PCM_IOCTL_SYNC_PTR;
    82  		 * if another exception appears this needs changing. */
    83  		convert_ioctl_struct(map+1, old, new, dir);
    84  		convert_ioctl_struct(map+2, old+4, new+8, dir);
    85  		convert_ioctl_struct(map+3, old+68, new+72, dir);
    86  		return;
    87  	}
    88  	for (int i=0; i < map->noffs; i++) {
    89  		int ts_offset = map->offsets[i];
    90  		int len = ts_offset-old_offset;
    91  		if (dir==W) memcpy(old+old_offset, new+new_offset, len);
    92  		else memcpy(new+new_offset, old+old_offset, len);
    93  		new_offset += len;
    94  		old_offset += len;
    95  		long long new_ts;
    96  		long old_ts;
    97  		int align = map->force_align ? sizeof(time_t) : alignof(time_t);
    98  		new_offset += (align-1) & -new_offset;
    99  		if (dir==W) {
   100  			memcpy(&new_ts, new+new_offset, sizeof new_ts);
   101  			old_ts = new_ts;
   102  			memcpy(old+old_offset, &old_ts, sizeof old_ts);
   103  		} else {
   104  			memcpy(&old_ts, old+old_offset, sizeof old_ts);
   105  			new_ts = old_ts;
   106  			memcpy(new+new_offset, &new_ts, sizeof new_ts);
   107  		}
   108  		new_offset += sizeof new_ts;
   109  		old_offset += sizeof old_ts;
   110  	}
   111  	if (dir==W) memcpy(old+old_offset, new+new_offset, old_size-old_offset);
   112  	else memcpy(new+new_offset, old+old_offset, old_size-old_offset);
   113  }
   114  
   115  int ioctl(int fd, int req, ...)
   116  {
   117  	void *arg;
   118  	va_list ap;
   119  	va_start(ap, req);
   120  	arg = va_arg(ap, void *);
   121  	va_end(ap);
   122  	int r = __syscall(SYS_ioctl, fd, req, arg);
   123  	if (SIOCGSTAMP != SIOCGSTAMP_OLD && req && r==-ENOTTY) {
   124  		for (int i=0; i<sizeof compat_map/sizeof *compat_map; i++) {
   125  			if (compat_map[i].new_req != req) continue;
   126  			union {
   127  				long long align;
   128  				char buf[256];
   129  			} u;
   130  			convert_ioctl_struct(&compat_map[i], u.buf, arg, W);
   131  			r = __syscall(SYS_ioctl, fd, compat_map[i].old_req, u.buf);
   132  			if (r<0) break;
   133  			convert_ioctl_struct(&compat_map[i], u.buf, arg, R);
   134  			break;
   135  		}
   136  	}
   137  	return __syscall_ret(r);
   138  }