long drm_ioctl_kernel(struct file *file, drm_ioctl_t *func, void *kdata, u32 flags) { struct drm_file *file_priv = file->private_data; struct drm_device *dev = file_priv->minor->dev; int retcode; if (drm_dev_is_unplugged(dev)) return -ENODEV; retcode = drm_ioctl_permit(flags, file_priv); if (unlikely(retcode)) return retcode; /* Enforce sane locking for modern driver ioctls. */ if (!drm_core_check_feature(dev, DRIVER_LEGACY) || (flags & DRM_UNLOCKED)) retcode = func(dev, kdata, file_priv); else { mutex_lock(&drm_global_mutex); retcode = func(dev, kdata, file_priv); mutex_unlock(&drm_global_mutex); } return retcode; }
static long vmw_generic_ioctl(struct file *filp, unsigned int cmd, unsigned long arg, long (*ioctl_func)(struct file *, unsigned int, unsigned long)) { struct drm_file *file_priv = filp->private_data; struct drm_device *dev = file_priv->minor->dev; unsigned int nr = DRM_IOCTL_NR(cmd); struct vmw_master *vmaster; unsigned int flags; long ret; /* * Do extra checking on driver private ioctls. */ if ((nr >= DRM_COMMAND_BASE) && (nr < DRM_COMMAND_END) && (nr < DRM_COMMAND_BASE + dev->driver->num_ioctls)) { const struct drm_ioctl_desc *ioctl = &vmw_ioctls[nr - DRM_COMMAND_BASE]; if (nr == DRM_COMMAND_BASE + DRM_VMW_EXECBUF) { ret = (long) drm_ioctl_permit(ioctl->flags, file_priv); if (unlikely(ret != 0)) return ret; if (unlikely((cmd & (IOC_IN | IOC_OUT)) != IOC_IN)) goto out_io_encoding; return (long) vmw_execbuf_ioctl(dev, arg, file_priv, _IOC_SIZE(cmd)); } if (unlikely(ioctl->cmd != cmd)) goto out_io_encoding; flags = ioctl->flags; } else if (!drm_ioctl_flags(nr, &flags)) return -EINVAL; vmaster = vmw_master_check(dev, file_priv, flags); if (IS_ERR(vmaster)) { ret = PTR_ERR(vmaster); if (ret != -ERESTARTSYS) DRM_INFO("IOCTL ERROR Command %d, Error %ld.\n", nr, ret); return ret; } ret = ioctl_func(filp, cmd, arg); if (vmaster) ttm_read_unlock(&vmaster->lock); return ret; out_io_encoding: DRM_ERROR("Invalid command format, ioctl %d\n", nr - DRM_COMMAND_BASE); return -EINVAL; }
/** * Called whenever a process performs an ioctl on /dev/drm. * * \param inode device inode. * \param file_priv DRM file private. * \param cmd command. * \param arg user argument. * \return zero on success or negative number on failure. * * Looks up the ioctl function in the ::ioctls table, checking for root * previleges if so required, and dispatches to the respective function. */ long drm_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct drm_file *file_priv = filp->private_data; struct drm_device *dev; const struct drm_ioctl_desc *ioctl = NULL; drm_ioctl_t *func; unsigned int nr = DRM_IOCTL_NR(cmd); int retcode = -EINVAL; char stack_kdata[128]; char *kdata = NULL; unsigned int usize, asize; dev = file_priv->minor->dev; if (drm_device_is_unplugged(dev)) return -ENODEV; if ((nr >= DRM_CORE_IOCTL_COUNT) && ((nr < DRM_COMMAND_BASE) || (nr >= DRM_COMMAND_END))) goto err_i1; if ((nr >= DRM_COMMAND_BASE) && (nr < DRM_COMMAND_END) && (nr < DRM_COMMAND_BASE + dev->driver->num_ioctls)) { u32 drv_size; ioctl = &dev->driver->ioctls[nr - DRM_COMMAND_BASE]; drv_size = _IOC_SIZE(ioctl->cmd_drv); usize = asize = _IOC_SIZE(cmd); if (drv_size > asize) asize = drv_size; cmd = ioctl->cmd_drv; } else if ((nr >= DRM_COMMAND_END) || (nr < DRM_COMMAND_BASE)) { u32 drv_size; ioctl = &drm_ioctls[nr]; drv_size = _IOC_SIZE(ioctl->cmd); usize = asize = _IOC_SIZE(cmd); if (drv_size > asize) asize = drv_size; cmd = ioctl->cmd; } else goto err_i1; DRM_DEBUG("pid=%d, dev=0x%lx, auth=%d, %s\n", task_pid_nr(current), (long)old_encode_dev(file_priv->minor->kdev->devt), file_priv->authenticated, ioctl->name); /* Do not trust userspace, use our own definition */ func = ioctl->func; if (unlikely(!func)) { DRM_DEBUG("no function\n"); retcode = -EINVAL; goto err_i1; } retcode = drm_ioctl_permit(ioctl->flags, file_priv); if (unlikely(retcode)) goto err_i1; if (cmd & (IOC_IN | IOC_OUT)) { if (asize <= sizeof(stack_kdata)) { kdata = stack_kdata; } else { kdata = kmalloc(asize, GFP_KERNEL); if (!kdata) { retcode = -ENOMEM; goto err_i1; } } if (asize > usize) memset(kdata + usize, 0, asize - usize); } if (cmd & IOC_IN) { if (copy_from_user(kdata, (void __user *)arg, usize) != 0) { retcode = -EFAULT; goto err_i1; } } else if (cmd & IOC_OUT) { memset(kdata, 0, usize); } if (ioctl->flags & DRM_UNLOCKED) retcode = func(dev, kdata, file_priv); else { mutex_lock(&drm_global_mutex); retcode = func(dev, kdata, file_priv); mutex_unlock(&drm_global_mutex); } if (cmd & IOC_OUT) { if (copy_to_user((void __user *)arg, kdata, usize) != 0) retcode = -EFAULT; } err_i1: if (!ioctl) DRM_DEBUG("invalid ioctl: pid=%d, dev=0x%lx, auth=%d, cmd=0x%02x, nr=0x%02x\n", task_pid_nr(current), (long)old_encode_dev(file_priv->minor->kdev->devt), file_priv->authenticated, cmd, nr); if (kdata != stack_kdata) kfree(kdata); if (retcode) DRM_DEBUG("ret = %d\n", retcode); return retcode; }
static int init_drm(void) { int err; int i; struct drm_file *file_priv; struct drm_mode_card_res res = {0}; PTR_TYPE res_fb_buf[10] = {0}; PTR_TYPE res_crtc_buf[10] = {0}; PTR_TYPE res_conn_buf[10] = {0}; PTR_TYPE res_enc_buf[10] = {0}; struct drm_mode_modeinfo conn_mode_buf[20]; PTR_TYPE conn_prop_buf[20]; PTR_TYPE conn_propval_buf[20]; PTR_TYPE conn_enc_buf[20]; struct drm_mode_get_connector conn; struct drm_mode_create_dumb create_dumb; struct drm_mode_map_dumb map_dumb; struct drm_mode_fb_cmd cmd_dumb; struct drm_mode_get_encoder enc; struct drm_mode_crtc crtc; // make kernel and user memory in common space old_fs = get_fs(); set_fs(KERNEL_DS); // open device file filp = filp_open(DRM_DEV_PATH, O_RDWR | O_CLOEXEC, 0); if (IS_ERR(filp)) { err = PTR_ERR(filp); printk(KERN_ERR "drmtest: unable to open file: %s (%d)", DRM_DEV_PATH, err); return err; } // set master err = drm_ioctl(filp, DRM_IOCTL_SET_MASTER, 0); if(err) { printk(KERN_ERR "drmtest: error in drm_ioctl, DRM_IOCTL_SET_MASTER (%d)", err); goto master_release; } // check if master permission is set file_priv = filp->private_data; err = drm_ioctl_permit(DRM_MASTER, file_priv); if(err) { printk(KERN_ERR "drmtest: cannot set MASTER permissions (%d)", err); goto master_release; } // get resources count err = drm_ioctl(filp, DRM_IOCTL_MODE_GETRESOURCES, (long unsigned int)&res); if(err) { printk(KERN_ERR "drmtest: error in drm_ioctl, DRM_IOCTL_MODE_GETRESOURCES (%d)", err); goto master_release; } // set pointers res.fb_id_ptr=(PTR_TYPE)res_fb_buf; res.crtc_id_ptr=(PTR_TYPE)res_crtc_buf; res.connector_id_ptr=(PTR_TYPE)res_conn_buf; res.encoder_id_ptr=(PTR_TYPE)res_enc_buf; // get resources data err = drm_ioctl(filp, DRM_IOCTL_MODE_GETRESOURCES, (long unsigned int)&res); if(err) { printk(KERN_ERR "drmtest: error in drm_ioctl, DRM_IOCTL_MODE_GETRESOURCES (%d)", err); goto master_release; } // print resources info printk("fb: %d, crtc: %d, conn: %d, enc: %d\n",res.count_fbs,res.count_crtcs,res.count_connectors,res.count_encoders); //Loop though all available connectors for (i=0; i<res.count_connectors; i++) { // clear memset(conn_mode_buf, 0, sizeof(struct drm_mode_modeinfo)*20); memset(conn_prop_buf, 0, sizeof(PTR_TYPE)*20); memset(conn_propval_buf, 0, sizeof(PTR_TYPE)*20); memset(conn_enc_buf, 0, sizeof(PTR_TYPE)*20); memset(&conn, 0, sizeof(struct drm_mode_get_connector)); conn.connector_id = res_conn_buf[i]; //get connector resource counts err = drm_ioctl(filp, DRM_IOCTL_MODE_GETCONNECTOR, (long unsigned int)&conn); if(err) { printk(KERN_ERR "drmtest: error in drm_ioctl, DRM_IOCTL_MODE_GETCONNECTOR (%d)", err); goto master_release; } // set pointers conn.modes_ptr=(PTR_TYPE)conn_mode_buf; conn.props_ptr=(PTR_TYPE)conn_prop_buf; conn.prop_values_ptr=(PTR_TYPE)conn_propval_buf; conn.encoders_ptr=(PTR_TYPE)conn_enc_buf; // get connector resources err = drm_ioctl(filp, DRM_IOCTL_MODE_GETCONNECTOR, (long unsigned int)&conn); if(err) { printk(KERN_ERR "drmtest: error in drm_ioctl, DRM_IOCTL_MODE_GETCONNECTOR (%d)", err); goto master_release; } // check if the connector is connected if (conn.count_encoders<1 || conn.count_modes<1 || !conn.encoder_id || !conn.connection) { printk("Not connected\n"); continue; } // ***************************** // create dumb buffer // ***************************** memset(&create_dumb, 0, sizeof(struct drm_mode_create_dumb)); memset(&map_dumb, 0, sizeof(struct drm_mode_map_dumb)); memset(&cmd_dumb, 0, sizeof(struct drm_mode_fb_cmd)); // set screen params create_dumb.width = conn_mode_buf[0].hdisplay; create_dumb.height = conn_mode_buf[0].vdisplay; create_dumb.bpp = 32; create_dumb.flags = 0; create_dumb.pitch = 0; create_dumb.size = 0; create_dumb.handle = 0; // create dumb buffer err = drm_ioctl(filp, DRM_IOCTL_MODE_CREATE_DUMB, (long unsigned int)&create_dumb); if(err) { printk(KERN_ERR "drmtest: error in drm_ioctl, DRM_IOCTL_MODE_CREATE_DUMB (%d)", err); goto master_release; } cmd_dumb.width=create_dumb.width; cmd_dumb.height=create_dumb.height; cmd_dumb.bpp=create_dumb.bpp; cmd_dumb.pitch=create_dumb.pitch; cmd_dumb.depth=24; cmd_dumb.handle=create_dumb.handle; // add framebuffer err = drm_ioctl(filp, DRM_IOCTL_MODE_ADDFB, (long unsigned int)&cmd_dumb); if(err) { printk(KERN_ERR "drmtest: error in drm_ioctl, DRM_IOCTL_MODE_ADDFB (%d)", err); goto master_release; } // prepare dumb buffer to mmap map_dumb.handle=create_dumb.handle; err = drm_ioctl(filp, DRM_IOCTL_MODE_MAP_DUMB, (long unsigned int)&map_dumb); if(err) { printk(KERN_ERR "drmtest: error in drm_ioctl, DRM_IOCTL_MODE_MAP_DUMB (%d)", err); goto master_release; } // map buffer to memory fb_base[i] = (void *)vm_mmap(filp, 0, create_dumb.size, PROT_READ | PROT_WRITE, MAP_SHARED, map_dumb.offset); fb_w[i]=create_dumb.width; fb_h[i]=create_dumb.height; // ************************* // kernel mode setting // ************************* printk("%d : mode: %d, prop: %d, enc: %d\n",conn.connection,conn.count_modes,conn.count_props,conn.count_encoders); printk("modes: %dx%d FB: %d\n", conn_mode_buf[0].hdisplay, conn_mode_buf[0].vdisplay, (int)fb_base[i]); // init encoder memset(&enc, 0, sizeof(struct drm_mode_get_encoder)); enc.encoder_id=conn.encoder_id; // get encoder err = drm_ioctl(filp, DRM_IOCTL_MODE_GETENCODER, (long unsigned int)&enc); if(err) { printk(KERN_ERR "drmtest: error in drm_ioctl, DRM_IOCTL_MODE_GETENCODER (%d)", err); goto master_release; } // init crtc memset(&crtc, 0, sizeof(struct drm_mode_crtc)); crtc.crtc_id=enc.crtc_id; err = drm_ioctl(filp, DRM_IOCTL_MODE_GETCRTC, (long unsigned int)&crtc); if(err) { printk(KERN_ERR "drmtest: error in drm_ioctl, DRM_IOCTL_MODE_GETCRTC (%d)", err); goto master_release; } crtc.fb_id=cmd_dumb.fb_id; crtc.set_connectors_ptr=(PTR_TYPE)&res_conn_buf[i]; crtc.count_connectors=1; crtc.mode=conn_mode_buf[0]; crtc.mode_valid=1; err = drm_ioctl(filp, DRM_IOCTL_MODE_SETCRTC, (long unsigned int)&crtc); if(err) { printk(KERN_ERR "drmtest: error in drm_ioctl, DRM_IOCTL_MODE_SETCRTC (%d)", err); goto master_release; } } connectors_count = res.count_connectors; return 0; master_release: exit_drm(); return err; }
/** * drm_ioctl - ioctl callback implementation for DRM drivers * @filp: file this ioctl is called on * @cmd: ioctl cmd number * @arg: user argument * * Looks up the ioctl function in the ::ioctls table, checking for root * previleges if so required, and dispatches to the respective function. * * Returns: * Zero on success, negative error code on failure. */ long drm_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct drm_file *file_priv = filp->private_data; struct drm_device *dev; const struct drm_ioctl_desc *ioctl = NULL; drm_ioctl_t *func; unsigned int nr = DRM_IOCTL_NR(cmd); int retcode = -EINVAL; char stack_kdata[128]; char *kdata = NULL; unsigned int in_size, out_size, drv_size, ksize; bool is_driver_ioctl; dev = file_priv->minor->dev; if (drm_device_is_unplugged(dev)) return -ENODEV; is_driver_ioctl = nr >= DRM_COMMAND_BASE && nr < DRM_COMMAND_END; if (is_driver_ioctl) { /* driver ioctl */ if (nr - DRM_COMMAND_BASE >= dev->driver->num_ioctls) goto err_i1; ioctl = &dev->driver->ioctls[nr - DRM_COMMAND_BASE]; } else { /* core ioctl */ if (nr >= DRM_CORE_IOCTL_COUNT) goto err_i1; ioctl = &drm_ioctls[nr]; } drv_size = _IOC_SIZE(ioctl->cmd); out_size = in_size = _IOC_SIZE(cmd); if ((cmd & ioctl->cmd & IOC_IN) == 0) in_size = 0; if ((cmd & ioctl->cmd & IOC_OUT) == 0) out_size = 0; ksize = max(max(in_size, out_size), drv_size); DRM_DEBUG("pid=%d, dev=0x%lx, auth=%d, %s\n", task_pid_nr(current), (long)old_encode_dev(file_priv->minor->kdev->devt), file_priv->authenticated, ioctl->name); /* Do not trust userspace, use our own definition */ func = ioctl->func; if (unlikely(!func)) { DRM_DEBUG("no function\n"); retcode = -EINVAL; goto err_i1; } retcode = drm_ioctl_permit(ioctl->flags, file_priv); if (unlikely(retcode)) goto err_i1; if (ksize <= sizeof(stack_kdata)) { kdata = stack_kdata; } else { kdata = kmalloc(ksize, GFP_KERNEL); if (!kdata) { retcode = -ENOMEM; goto err_i1; } } if (copy_from_user(kdata, (void __user *)arg, in_size) != 0) { retcode = -EFAULT; goto err_i1; } if (ksize > in_size) memset(kdata + in_size, 0, ksize - in_size); /* Enforce sane locking for modern driver ioctls. Core ioctls are * too messy still. */ if ((!drm_core_check_feature(dev, DRIVER_LEGACY) && is_driver_ioctl) || (ioctl->flags & DRM_UNLOCKED)) retcode = func(dev, kdata, file_priv); else { mutex_lock(&drm_global_mutex); retcode = func(dev, kdata, file_priv); mutex_unlock(&drm_global_mutex); } if (copy_to_user((void __user *)arg, kdata, out_size) != 0) retcode = -EFAULT; err_i1: if (!ioctl) DRM_DEBUG("invalid ioctl: pid=%d, dev=0x%lx, auth=%d, cmd=0x%02x, nr=0x%02x\n", task_pid_nr(current), (long)old_encode_dev(file_priv->minor->kdev->devt), file_priv->authenticated, cmd, nr); if (kdata != stack_kdata) kfree(kdata); if (retcode) DRM_DEBUG("ret = %d\n", retcode); return retcode; }