summaryrefslogtreecommitdiff
path: root/bsd-core/drm_drv.c
diff options
context:
space:
mode:
Diffstat (limited to 'bsd-core/drm_drv.c')
-rw-r--r--bsd-core/drm_drv.c74
1 files changed, 65 insertions, 9 deletions
diff --git a/bsd-core/drm_drv.c b/bsd-core/drm_drv.c
index 45e145dc1..81ca644a4 100644
--- a/bsd-core/drm_drv.c
+++ b/bsd-core/drm_drv.c
@@ -1221,27 +1221,83 @@ int DRM(unlock)( DRM_IOCTL_ARGS )
}
#if DRM_LINUX
+#define LINUX_IOCTL_DRM_MIN 0x6400
+#define LINUX_IOCTL_DRM_MAX 0x64ff
+
static linux_ioctl_function_t DRM( linux_ioctl);
static struct linux_ioctl_handler DRM( handler) = {DRM( linux_ioctl), LINUX_IOCTL_DRM_MIN, LINUX_IOCTL_DRM_MAX};
SYSINIT (DRM( register), SI_SUB_KLD, SI_ORDER_MIDDLE, linux_ioctl_register_handler, &DRM( handler));
SYSUNINIT(DRM( unregister), SI_SUB_KLD, SI_ORDER_MIDDLE, linux_ioctl_unregister_handler, &DRM( handler));
+#define LINUX_IOC_VOID IOC_VOID
+#define LINUX_IOC_IN IOC_OUT /* Linux has the values the other way around */
+#define LINUX_IOC_OUT IOC_IN
+
/*
* Linux emulation IOCTL
*/
static int
DRM(linux_ioctl)(DRM_STRUCTPROC *p, struct linux_ioctl_args* args)
{
+ u_long cmd = args->cmd;
+#define STK_PARAMS 128
+ union {
+ char stkbuf[STK_PARAMS];
+ long align;
+ } ubuf;
+ caddr_t data=NULL, memp=NULL;
+ u_int size = IOCPARM_LEN(cmd);
+ int error;
#if (__FreeBSD_version >= 500000)
- struct file *fp = p->td_proc->p_fd->fd_ofiles[args->fd];
+ struct file *fp;
#else
- struct file *fp = p->p_fd->fd_ofiles[args->fd];
-#endif
- u_long cmd = args->cmd;
- caddr_t data = (caddr_t) args->arg;
- /*
- * Pass the ioctl off to our standard handler.
- */
- return(fo_ioctl(fp, cmd, data, p));
+ struct file *fp = p->p_fd->fd_ofiles[args->fd];
+#endif
+ if ( size > STK_PARAMS ) {
+ if ( size > IOCPARM_MAX )
+ return EINVAL;
+ memp = malloc( (u_long)size, DRM(M_DRM), M_WAITOK );
+ data = memp;
+ } else {
+ data = ubuf.stkbuf;
+ }
+
+ if ( cmd & LINUX_IOC_IN ) {
+ if ( size ) {
+ error = copyin( (caddr_t)args->arg, data, (u_int)size );
+ if (error) {
+ if ( memp )
+ free( data, DRM(M_DRM) );
+ return error;
+ }
+ } else {
+ data = (caddr_t)args->arg;
+ }
+ } else if ( (cmd & LINUX_IOC_OUT) && size ) {
+ /*
+ * Zero the buffer so the user always
+ * gets back something deterministic.
+ */
+ bzero( data, size );
+ } else if ( cmd & LINUX_IOC_VOID ) {
+ *(caddr_t *)data = (caddr_t)args->arg;
+ }
+
+#if (__FreeBSD_version >= 500000)
+ if ( (error = fget( p, args->fd, &fp )) != 0 ) {
+ if ( memp )
+ free( memp, DRM(M_DRM) );
+ return (error);
+ }
+ error = fo_ioctl( fp, cmd, data, p->td_ucred, p );
+ fdrop( fp, p );
+#else
+ error = fo_ioctl( fp, cmd, data, p );
+#endif
+ if ( error == 0 && (cmd & LINUX_IOC_OUT) && size )
+ error = copyout( data, (caddr_t)args->arg, (u_int)size );
+ if ( memp )
+ free( memp, DRM(M_DRM) );
+ return error;
}
#endif /* DRM_LINUX */