1 /* fops.c -- File operations for DRM -*- linux-c -*-
2  * Created: Mon Jan  4 08:58:31 1999 by faith@precisioninsight.com
3  *
4  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
5  * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
6  * All Rights Reserved.
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining a
9  * copy of this software and associated documentation files (the "Software"),
10  * to deal in the Software without restriction, including without limitation
11  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12  * and/or sell copies of the Software, and to permit persons to whom the
13  * Software is furnished to do so, subject to the following conditions:
14  *
15  * The above copyright notice and this permission notice (including the next
16  * paragraph) shall be included in all copies or substantial portions of the
17  * Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
22  * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
23  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
24  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25  * DEALINGS IN THE SOFTWARE.
26  *
27  * Authors:
28  *    Rickard E. (Rik) Faith <faith@valinux.com>
29  *    Daryll Strauss <daryll@valinux.com>
30  *
31  */
32 
33 #define __NO_VERSION__
34 #include "drmP.h"
35 #include <linux/poll.h>
36 
37 /* drm_open is called whenever a process opens /dev/drm. */
38 
drm_open_helper(struct inode * inode,struct file * filp,drm_device_t * dev)39 int drm_open_helper(struct inode *inode, struct file *filp, drm_device_t *dev)
40 {
41 	kdev_t	     minor = MINOR(inode->i_rdev);
42 	drm_file_t   *priv;
43 
44 	if (filp->f_flags & O_EXCL)   return -EBUSY; /* No exclusive opens */
45 	if (!drm_cpu_valid())         return -EINVAL;
46 
47 	DRM_DEBUG("pid = %d, minor = %d\n", current->pid, minor);
48 
49 	priv		    = drm_alloc(sizeof(*priv), DRM_MEM_FILES);
50 	if(priv == NULL)
51 		return -ENOMEM;
52 	memset(priv, 0, sizeof(*priv));
53 
54 	filp->private_data  = priv;
55 	priv->uid	    = current->euid;
56 	priv->pid	    = current->pid;
57 	priv->minor	    = minor;
58 	priv->dev	    = dev;
59 	priv->ioctl_count   = 0;
60 	priv->authenticated = capable(CAP_SYS_ADMIN);
61 
62 	down(&dev->struct_sem);
63 	if (!dev->file_last) {
64 		priv->next	= NULL;
65 		priv->prev	= NULL;
66 		dev->file_first = priv;
67 		dev->file_last	= priv;
68 	} else {
69 		priv->next	     = NULL;
70 		priv->prev	     = dev->file_last;
71 		dev->file_last->next = priv;
72 		dev->file_last	     = priv;
73 	}
74 	up(&dev->struct_sem);
75 
76 	return 0;
77 }
78 
drm_flush(struct file * filp)79 int drm_flush(struct file *filp)
80 {
81 	drm_file_t    *priv   = filp->private_data;
82 	drm_device_t  *dev    = priv->dev;
83 
84 	DRM_DEBUG("pid = %d, device = 0x%x, open_count = %d\n",
85 		  current->pid, dev->device, dev->open_count);
86 	return 0;
87 }
88 
89 /* drm_release is called whenever a process closes /dev/drm*.  Linux calls
90    this only if any mappings have been closed. */
91 
drm_release(struct inode * inode,struct file * filp)92 int drm_release(struct inode *inode, struct file *filp)
93 {
94 	drm_file_t    *priv   = filp->private_data;
95 	drm_device_t  *dev    = priv->dev;
96 
97 	DRM_DEBUG("pid = %d, device = 0x%x, open_count = %d\n",
98 		  current->pid, dev->device, dev->open_count);
99 
100 	if (dev->lock.hw_lock
101 	    && _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)
102 	    && dev->lock.pid == current->pid) {
103 		DRM_ERROR("Process %d dead, freeing lock for context %d\n",
104 			  current->pid,
105 			  _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
106 		drm_lock_free(dev,
107 			      &dev->lock.hw_lock->lock,
108 			      _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
109 
110 				/* FIXME: may require heavy-handed reset of
111                                    hardware at this point, possibly
112                                    processed via a callback to the X
113                                    server. */
114 	}
115 	drm_reclaim_buffers(dev, priv->pid);
116 
117 	drm_fasync(-1, filp, 0);
118 
119 	down(&dev->struct_sem);
120 	if (priv->prev) priv->prev->next = priv->next;
121 	else		dev->file_first	 = priv->next;
122 	if (priv->next) priv->next->prev = priv->prev;
123 	else		dev->file_last	 = priv->prev;
124 	up(&dev->struct_sem);
125 
126 	drm_free(priv, sizeof(*priv), DRM_MEM_FILES);
127 
128 	return 0;
129 }
130 
drm_fasync(int fd,struct file * filp,int on)131 int drm_fasync(int fd, struct file *filp, int on)
132 {
133 	drm_file_t    *priv   = filp->private_data;
134 	drm_device_t  *dev    = priv->dev;
135 	int	      retcode;
136 
137 	DRM_DEBUG("fd = %d, device = 0x%x\n", fd, dev->device);
138 	retcode = fasync_helper(fd, filp, on, &dev->buf_async);
139 	if (retcode < 0) return retcode;
140 	return 0;
141 }
142 
143 
144 /* The drm_read and drm_write_string code (especially that which manages
145    the circular buffer), is based on Alessandro Rubini's LINUX DEVICE
146    DRIVERS (Cambridge: O'Reilly, 1998), pages 111-113. */
147 
drm_read(struct file * filp,char * buf,size_t count,loff_t * off)148 ssize_t drm_read(struct file *filp, char *buf, size_t count, loff_t *off)
149 {
150 	drm_file_t    *priv   = filp->private_data;
151 	drm_device_t  *dev    = priv->dev;
152 	int	      left;
153 	int	      avail;
154 	int	      send;
155 	int	      cur;
156 
157 	DRM_DEBUG("%p, %p\n", dev->buf_rp, dev->buf_wp);
158 
159 	while (dev->buf_rp == dev->buf_wp) {
160 		DRM_DEBUG("  sleeping\n");
161 		if (filp->f_flags & O_NONBLOCK) {
162 			return -EAGAIN;
163 		}
164 		interruptible_sleep_on(&dev->buf_readers);
165 		if (signal_pending(current)) {
166 			DRM_DEBUG("  interrupted\n");
167 			return -ERESTARTSYS;
168 		}
169 		DRM_DEBUG("  awake\n");
170 	}
171 
172 	left  = (dev->buf_rp + DRM_BSZ - dev->buf_wp) % DRM_BSZ;
173 	avail = DRM_BSZ - left;
174 	send  = DRM_MIN(avail, count);
175 
176 	while (send) {
177 		if (dev->buf_wp > dev->buf_rp) {
178 			cur = DRM_MIN(send, dev->buf_wp - dev->buf_rp);
179 		} else {
180 			cur = DRM_MIN(send, dev->buf_end - dev->buf_rp);
181 		}
182 		if (copy_to_user(buf, dev->buf_rp, cur))
183 			return -EFAULT;
184 		dev->buf_rp += cur;
185 		if (dev->buf_rp == dev->buf_end) dev->buf_rp = dev->buf;
186 		send -= cur;
187 	}
188 
189 	wake_up_interruptible(&dev->buf_writers);
190 	return DRM_MIN(avail, count);;
191 }
192 
drm_write_string(drm_device_t * dev,const char * s)193 int drm_write_string(drm_device_t *dev, const char *s)
194 {
195 	int left   = (dev->buf_rp + DRM_BSZ - dev->buf_wp) % DRM_BSZ;
196 	int send   = strlen(s);
197 	int count;
198 
199 	DRM_DEBUG("%d left, %d to send (%p, %p)\n",
200 		  left, send, dev->buf_rp, dev->buf_wp);
201 
202 	if (left == 1 || dev->buf_wp != dev->buf_rp) {
203 		DRM_ERROR("Buffer not empty (%d left, wp = %p, rp = %p)\n",
204 			  left,
205 			  dev->buf_wp,
206 			  dev->buf_rp);
207 	}
208 
209 	while (send) {
210 		if (dev->buf_wp >= dev->buf_rp) {
211 			count = DRM_MIN(send, dev->buf_end - dev->buf_wp);
212 			if (count == left) --count; /* Leave a hole */
213 		} else {
214 			count = DRM_MIN(send, dev->buf_rp - dev->buf_wp - 1);
215 		}
216 		strncpy(dev->buf_wp, s, count);
217 		dev->buf_wp += count;
218 		if (dev->buf_wp == dev->buf_end) dev->buf_wp = dev->buf;
219 		send -= count;
220 	}
221 
222 #if LINUX_VERSION_CODE < 0x020315 && !defined(KILLFASYNCHASTHREEPARAMETERS)
223 	/* The extra parameter to kill_fasync was added in 2.3.21, and is
224            _not_ present in _stock_ 2.2.14 and 2.2.15.  However, some
225            distributions patch 2.2.x kernels to add this parameter.  The
226            Makefile.linux attempts to detect this addition and defines
227            KILLFASYNCHASTHREEPARAMETERS if three parameters are found. */
228 	if (dev->buf_async) kill_fasync(dev->buf_async, SIGIO);
229 #else
230 
231 				/* Parameter added in 2.3.21. */
232 #if LINUX_VERSION_CODE < 0x020400
233 	if (dev->buf_async) kill_fasync(dev->buf_async, SIGIO, POLL_IN);
234 #else
235 				/* Type of first parameter changed in
236                                    Linux 2.4.0-test2... */
237 	if (dev->buf_async) kill_fasync(&dev->buf_async, SIGIO, POLL_IN);
238 #endif
239 #endif
240 	DRM_DEBUG("waking\n");
241 	wake_up_interruptible(&dev->buf_readers);
242 	return 0;
243 }
244 
drm_poll(struct file * filp,struct poll_table_struct * wait)245 unsigned int drm_poll(struct file *filp, struct poll_table_struct *wait)
246 {
247 	drm_file_t   *priv = filp->private_data;
248 	drm_device_t *dev  = priv->dev;
249 
250 	poll_wait(filp, &dev->buf_readers, wait);
251 	if (dev->buf_wp != dev->buf_rp) return POLLIN | POLLRDNORM;
252 	return 0;
253 }
254