~ubuntu-branches/debian/wheezy/linux-2.6/wheezy

« back to all changes in this revision

Viewing changes to drivers/media/media-devnode.c

  • Committer: Bazaar Package Importer
  • Author(s): Ben Hutchings, Ben Hutchings, Aurelien Jarno
  • Date: 2011-06-07 12:14:05 UTC
  • mfrom: (43.1.9 sid)
  • Revision ID: james.westby@ubuntu.com-20110607121405-i3h1rd7nrnd2b73h
Tags: 2.6.39-2
[ Ben Hutchings ]
* [x86] Enable BACKLIGHT_APPLE, replacing BACKLIGHT_MBP_NVIDIA
  (Closes: #627492)
* cgroups: Disable memory resource controller by default. Allow it
  to be enabled using kernel parameter 'cgroup_enable=memory'.
* rt2800usb: Enable support for more USB devices including
  Linksys WUSB600N (Closes: #596626) (this change was accidentally
  omitted from 2.6.39-1)
* [x86] Remove Celeron from list of processors supporting PAE. Most
  'Celeron M' models do not.
* Update debconf template translations:
  - Swedish (Martin Bagge) (Closes: #628932)
  - French (David Prévot) (Closes: #628191)
* aufs: Update for 2.6.39 (Closes: #627837)
* Add stable 2.6.39.1, including:
  - ext4: dont set PageUptodate in ext4_end_bio()
  - pata_cmd64x: fix boot crash on parisc (Closes: #622997, #622745)
  - ext3: Fix fs corruption when make_indexed_dir() fails
  - netfilter: nf_ct_sip: validate Content-Length in TCP SIP messages
  - sctp: fix race between sctp_bind_addr_free() and
    sctp_bind_addr_conflict()
  - sctp: fix memory leak of the ASCONF queue when free asoc
  - md/bitmap: fix saving of events_cleared and other state
  - cdc_acm: Fix oops when Droids MuIn LCD is connected
  - cx88: Fix conversion from BKL to fine-grained locks (Closes: #619827)
  - keys: Set cred->user_ns in key_replace_session_keyring (CVE-2011-2184)
  - tmpfs: fix race between truncate and writepage
  - nfs41: Correct offset for LAYOUTCOMMIT
  - xen/mmu: fix a race window causing leave_mm BUG()
  - ext4: fix possible use-after-free in ext4_remove_li_request()
  For the complete list of changes, see:
   http://www.kernel.org/pub/linux/kernel/v2.6/ChangeLog-2.6.39.1
* Bump ABI to 2
* netfilter: Enable IP_SET, IP_SET_BITMAP_IP, IP_SET_BITMAP_IPMAC,
  IP_SET_BITMAP_PORT, IP_SET_HASH_IP, IP_SET_HASH_IPPORT,
  IP_SET_HASH_IPPORTIP, IP_SET_HASH_IPPORTNET, IP_SET_HASH_NET,
  IP_SET_HASH_NETPORT, IP_SET_LIST_SET, NETFILTER_XT_SET as modules
  (Closes: #629401)

[ Aurelien Jarno ]
* [mipsel/loongson-2f] Disable_SCSI_LPFC to workaround GCC ICE.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Media device node
 
3
 *
 
4
 * Copyright (C) 2010 Nokia Corporation
 
5
 *
 
6
 * Based on drivers/media/video/v4l2_dev.c code authored by
 
7
 *      Mauro Carvalho Chehab <mchehab@infradead.org> (version 2)
 
8
 *      Alan Cox, <alan@lxorguk.ukuu.org.uk> (version 1)
 
9
 *
 
10
 * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
 
11
 *           Sakari Ailus <sakari.ailus@iki.fi>
 
12
 *
 
13
 * This program is free software; you can redistribute it and/or modify
 
14
 * it under the terms of the GNU General Public License version 2 as
 
15
 * published by the Free Software Foundation.
 
16
 *
 
17
 * This program is distributed in the hope that it will be useful,
 
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
20
 * GNU General Public License for more details.
 
21
 *
 
22
 * You should have received a copy of the GNU General Public License
 
23
 * along with this program; if not, write to the Free Software
 
24
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
25
 *
 
26
 * --
 
27
 *
 
28
 * Generic media device node infrastructure to register and unregister
 
29
 * character devices using a dynamic major number and proper reference
 
30
 * counting.
 
31
 */
 
32
 
 
33
#include <linux/errno.h>
 
34
#include <linux/init.h>
 
35
#include <linux/module.h>
 
36
#include <linux/kernel.h>
 
37
#include <linux/kmod.h>
 
38
#include <linux/slab.h>
 
39
#include <linux/mm.h>
 
40
#include <linux/string.h>
 
41
#include <linux/types.h>
 
42
#include <linux/uaccess.h>
 
43
#include <asm/system.h>
 
44
 
 
45
#include <media/media-devnode.h>
 
46
 
 
47
#define MEDIA_NUM_DEVICES       256
 
48
#define MEDIA_NAME              "media"
 
49
 
 
50
static dev_t media_dev_t;
 
51
 
 
52
/*
 
53
 *      Active devices
 
54
 */
 
55
static DEFINE_MUTEX(media_devnode_lock);
 
56
static DECLARE_BITMAP(media_devnode_nums, MEDIA_NUM_DEVICES);
 
57
 
 
58
/* Called when the last user of the media device exits. */
 
59
static void media_devnode_release(struct device *cd)
 
60
{
 
61
        struct media_devnode *mdev = to_media_devnode(cd);
 
62
 
 
63
        mutex_lock(&media_devnode_lock);
 
64
 
 
65
        /* Delete the cdev on this minor as well */
 
66
        cdev_del(&mdev->cdev);
 
67
 
 
68
        /* Mark device node number as free */
 
69
        clear_bit(mdev->minor, media_devnode_nums);
 
70
 
 
71
        mutex_unlock(&media_devnode_lock);
 
72
 
 
73
        /* Release media_devnode and perform other cleanups as needed. */
 
74
        if (mdev->release)
 
75
                mdev->release(mdev);
 
76
}
 
77
 
 
78
static struct bus_type media_bus_type = {
 
79
        .name = MEDIA_NAME,
 
80
};
 
81
 
 
82
static ssize_t media_read(struct file *filp, char __user *buf,
 
83
                size_t sz, loff_t *off)
 
84
{
 
85
        struct media_devnode *mdev = media_devnode_data(filp);
 
86
 
 
87
        if (!mdev->fops->read)
 
88
                return -EINVAL;
 
89
        if (!media_devnode_is_registered(mdev))
 
90
                return -EIO;
 
91
        return mdev->fops->read(filp, buf, sz, off);
 
92
}
 
93
 
 
94
static ssize_t media_write(struct file *filp, const char __user *buf,
 
95
                size_t sz, loff_t *off)
 
96
{
 
97
        struct media_devnode *mdev = media_devnode_data(filp);
 
98
 
 
99
        if (!mdev->fops->write)
 
100
                return -EINVAL;
 
101
        if (!media_devnode_is_registered(mdev))
 
102
                return -EIO;
 
103
        return mdev->fops->write(filp, buf, sz, off);
 
104
}
 
105
 
 
106
static unsigned int media_poll(struct file *filp,
 
107
                               struct poll_table_struct *poll)
 
108
{
 
109
        struct media_devnode *mdev = media_devnode_data(filp);
 
110
 
 
111
        if (!media_devnode_is_registered(mdev))
 
112
                return POLLERR | POLLHUP;
 
113
        if (!mdev->fops->poll)
 
114
                return DEFAULT_POLLMASK;
 
115
        return mdev->fops->poll(filp, poll);
 
116
}
 
117
 
 
118
static long media_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 
119
{
 
120
        struct media_devnode *mdev = media_devnode_data(filp);
 
121
 
 
122
        if (!mdev->fops->ioctl)
 
123
                return -ENOTTY;
 
124
 
 
125
        if (!media_devnode_is_registered(mdev))
 
126
                return -EIO;
 
127
 
 
128
        return mdev->fops->ioctl(filp, cmd, arg);
 
129
}
 
130
 
 
131
/* Override for the open function */
 
132
static int media_open(struct inode *inode, struct file *filp)
 
133
{
 
134
        struct media_devnode *mdev;
 
135
        int ret;
 
136
 
 
137
        /* Check if the media device is available. This needs to be done with
 
138
         * the media_devnode_lock held to prevent an open/unregister race:
 
139
         * without the lock, the device could be unregistered and freed between
 
140
         * the media_devnode_is_registered() and get_device() calls, leading to
 
141
         * a crash.
 
142
         */
 
143
        mutex_lock(&media_devnode_lock);
 
144
        mdev = container_of(inode->i_cdev, struct media_devnode, cdev);
 
145
        /* return ENXIO if the media device has been removed
 
146
           already or if it is not registered anymore. */
 
147
        if (!media_devnode_is_registered(mdev)) {
 
148
                mutex_unlock(&media_devnode_lock);
 
149
                return -ENXIO;
 
150
        }
 
151
        /* and increase the device refcount */
 
152
        get_device(&mdev->dev);
 
153
        mutex_unlock(&media_devnode_lock);
 
154
 
 
155
        filp->private_data = mdev;
 
156
 
 
157
        if (mdev->fops->open) {
 
158
                ret = mdev->fops->open(filp);
 
159
                if (ret) {
 
160
                        put_device(&mdev->dev);
 
161
                        return ret;
 
162
                }
 
163
        }
 
164
 
 
165
        return 0;
 
166
}
 
167
 
 
168
/* Override for the release function */
 
169
static int media_release(struct inode *inode, struct file *filp)
 
170
{
 
171
        struct media_devnode *mdev = media_devnode_data(filp);
 
172
        int ret = 0;
 
173
 
 
174
        if (mdev->fops->release)
 
175
                mdev->fops->release(filp);
 
176
 
 
177
        /* decrease the refcount unconditionally since the release()
 
178
           return value is ignored. */
 
179
        put_device(&mdev->dev);
 
180
        filp->private_data = NULL;
 
181
        return ret;
 
182
}
 
183
 
 
184
static const struct file_operations media_devnode_fops = {
 
185
        .owner = THIS_MODULE,
 
186
        .read = media_read,
 
187
        .write = media_write,
 
188
        .open = media_open,
 
189
        .unlocked_ioctl = media_ioctl,
 
190
        .release = media_release,
 
191
        .poll = media_poll,
 
192
        .llseek = no_llseek,
 
193
};
 
194
 
 
195
/**
 
196
 * media_devnode_register - register a media device node
 
197
 * @mdev: media device node structure we want to register
 
198
 *
 
199
 * The registration code assigns minor numbers and registers the new device node
 
200
 * with the kernel. An error is returned if no free minor number can be found,
 
201
 * or if the registration of the device node fails.
 
202
 *
 
203
 * Zero is returned on success.
 
204
 *
 
205
 * Note that if the media_devnode_register call fails, the release() callback of
 
206
 * the media_devnode structure is *not* called, so the caller is responsible for
 
207
 * freeing any data.
 
208
 */
 
209
int __must_check media_devnode_register(struct media_devnode *mdev)
 
210
{
 
211
        int minor;
 
212
        int ret;
 
213
 
 
214
        /* Part 1: Find a free minor number */
 
215
        mutex_lock(&media_devnode_lock);
 
216
        minor = find_next_zero_bit(media_devnode_nums, 0, MEDIA_NUM_DEVICES);
 
217
        if (minor == MEDIA_NUM_DEVICES) {
 
218
                mutex_unlock(&media_devnode_lock);
 
219
                printk(KERN_ERR "could not get a free minor\n");
 
220
                return -ENFILE;
 
221
        }
 
222
 
 
223
        set_bit(mdev->minor, media_devnode_nums);
 
224
        mutex_unlock(&media_devnode_lock);
 
225
 
 
226
        mdev->minor = minor;
 
227
 
 
228
        /* Part 2: Initialize and register the character device */
 
229
        cdev_init(&mdev->cdev, &media_devnode_fops);
 
230
        mdev->cdev.owner = mdev->fops->owner;
 
231
 
 
232
        ret = cdev_add(&mdev->cdev, MKDEV(MAJOR(media_dev_t), mdev->minor), 1);
 
233
        if (ret < 0) {
 
234
                printk(KERN_ERR "%s: cdev_add failed\n", __func__);
 
235
                goto error;
 
236
        }
 
237
 
 
238
        /* Part 3: Register the media device */
 
239
        mdev->dev.bus = &media_bus_type;
 
240
        mdev->dev.devt = MKDEV(MAJOR(media_dev_t), mdev->minor);
 
241
        mdev->dev.release = media_devnode_release;
 
242
        if (mdev->parent)
 
243
                mdev->dev.parent = mdev->parent;
 
244
        dev_set_name(&mdev->dev, "media%d", mdev->minor);
 
245
        ret = device_register(&mdev->dev);
 
246
        if (ret < 0) {
 
247
                printk(KERN_ERR "%s: device_register failed\n", __func__);
 
248
                goto error;
 
249
        }
 
250
 
 
251
        /* Part 4: Activate this minor. The char device can now be used. */
 
252
        set_bit(MEDIA_FLAG_REGISTERED, &mdev->flags);
 
253
 
 
254
        return 0;
 
255
 
 
256
error:
 
257
        cdev_del(&mdev->cdev);
 
258
        clear_bit(mdev->minor, media_devnode_nums);
 
259
        return ret;
 
260
}
 
261
 
 
262
/**
 
263
 * media_devnode_unregister - unregister a media device node
 
264
 * @mdev: the device node to unregister
 
265
 *
 
266
 * This unregisters the passed device. Future open calls will be met with
 
267
 * errors.
 
268
 *
 
269
 * This function can safely be called if the device node has never been
 
270
 * registered or has already been unregistered.
 
271
 */
 
272
void media_devnode_unregister(struct media_devnode *mdev)
 
273
{
 
274
        /* Check if mdev was ever registered at all */
 
275
        if (!media_devnode_is_registered(mdev))
 
276
                return;
 
277
 
 
278
        mutex_lock(&media_devnode_lock);
 
279
        clear_bit(MEDIA_FLAG_REGISTERED, &mdev->flags);
 
280
        mutex_unlock(&media_devnode_lock);
 
281
        device_unregister(&mdev->dev);
 
282
}
 
283
 
 
284
/*
 
285
 *      Initialise media for linux
 
286
 */
 
287
static int __init media_devnode_init(void)
 
288
{
 
289
        int ret;
 
290
 
 
291
        printk(KERN_INFO "Linux media interface: v0.10\n");
 
292
        ret = alloc_chrdev_region(&media_dev_t, 0, MEDIA_NUM_DEVICES,
 
293
                                  MEDIA_NAME);
 
294
        if (ret < 0) {
 
295
                printk(KERN_WARNING "media: unable to allocate major\n");
 
296
                return ret;
 
297
        }
 
298
 
 
299
        ret = bus_register(&media_bus_type);
 
300
        if (ret < 0) {
 
301
                unregister_chrdev_region(media_dev_t, MEDIA_NUM_DEVICES);
 
302
                printk(KERN_WARNING "media: bus_register failed\n");
 
303
                return -EIO;
 
304
        }
 
305
 
 
306
        return 0;
 
307
}
 
308
 
 
309
static void __exit media_devnode_exit(void)
 
310
{
 
311
        bus_unregister(&media_bus_type);
 
312
        unregister_chrdev_region(media_dev_t, MEDIA_NUM_DEVICES);
 
313
}
 
314
 
 
315
module_init(media_devnode_init)
 
316
module_exit(media_devnode_exit)
 
317
 
 
318
MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
 
319
MODULE_DESCRIPTION("Device node registration for media drivers");
 
320
MODULE_LICENSE("GPL");