1
2
3
4
5
6
7
8
9
10
11
12#include <linux/config.h>
13#include <linux/fs.h>
14#include <linux/major.h>
15#include <linux/string.h>
16#include <linux/time.h>
17#include <linux/stat.h>
18#include <linux/fcntl.h>
19#include <linux/errno.h>
20#include <linux/module.h>
21#include <linux/smp_lock.h>
22#include <linux/devfs_fs_kernel.h>
23#ifdef CONFIG_KMOD
24#include <linux/kmod.h>
25
26#include <linux/tty.h>
27
28
29struct tty_driver *get_tty_driver(kdev_t device);
30#define isa_tty_dev(ma) (ma == TTY_MAJOR || ma == TTYAUX_MAJOR)
31#define need_serial(ma,mi) (get_tty_driver(mk_kdev(ma,mi)) == NULL)
32#endif
33
34struct device_struct {
35 const char * name;
36 struct file_operations * fops;
37};
38
39static rwlock_t chrdevs_lock = RW_LOCK_UNLOCKED;
40static struct device_struct chrdevs[MAX_CHRDEV];
41
42extern int get_blkdev_list(char *);
43
44int get_device_list(char * page)
45{
46 int i;
47 int len;
48
49 len = sprintf(page, "Character devices:\n");
50 read_lock(&chrdevs_lock);
51 for (i = 0; i < MAX_CHRDEV ; i++) {
52 if (chrdevs[i].fops) {
53 len += sprintf(page+len, "%3d %s\n", i, chrdevs[i].name);
54 }
55 }
56 read_unlock(&chrdevs_lock);
57 len += get_blkdev_list(page+len);
58 return len;
59}
60
61
62
63
64
65
66static struct file_operations * get_chrfops(unsigned int major, unsigned int minor)
67{
68 struct file_operations *ret = NULL;
69
70 if (!major || major >= MAX_CHRDEV)
71 return NULL;
72
73 read_lock(&chrdevs_lock);
74 ret = fops_get(chrdevs[major].fops);
75 read_unlock(&chrdevs_lock);
76#ifdef CONFIG_KMOD
77 if (ret && isa_tty_dev(major)) {
78 lock_kernel();
79 if (need_serial(major,minor)) {
80
81 fops_put(ret);
82 ret = NULL;
83 }
84 unlock_kernel();
85 }
86 if (!ret) {
87 char name[20];
88 sprintf(name, "char-major-%d", major);
89 request_module(name);
90
91 read_lock(&chrdevs_lock);
92 ret = fops_get(chrdevs[major].fops);
93 read_unlock(&chrdevs_lock);
94 }
95#endif
96 return ret;
97}
98
99int register_chrdev(unsigned int major, const char * name, struct file_operations *fops)
100{
101 if (devfs_only())
102 return 0;
103 if (major == 0) {
104 write_lock(&chrdevs_lock);
105 for (major = MAX_CHRDEV-1; major > 0; major--) {
106 if (chrdevs[major].fops == NULL) {
107 chrdevs[major].name = name;
108 chrdevs[major].fops = fops;
109 write_unlock(&chrdevs_lock);
110 return major;
111 }
112 }
113 write_unlock(&chrdevs_lock);
114 return -EBUSY;
115 }
116 if (major >= MAX_CHRDEV)
117 return -EINVAL;
118 write_lock(&chrdevs_lock);
119 if (chrdevs[major].fops && chrdevs[major].fops != fops) {
120 write_unlock(&chrdevs_lock);
121 return -EBUSY;
122 }
123 chrdevs[major].name = name;
124 chrdevs[major].fops = fops;
125 write_unlock(&chrdevs_lock);
126 return 0;
127}
128
129int unregister_chrdev(unsigned int major, const char * name)
130{
131 if (devfs_only())
132 return 0;
133 if (major >= MAX_CHRDEV)
134 return -EINVAL;
135 write_lock(&chrdevs_lock);
136 if (!chrdevs[major].fops || strcmp(chrdevs[major].name, name)) {
137 write_unlock(&chrdevs_lock);
138 return -EINVAL;
139 }
140 chrdevs[major].name = NULL;
141 chrdevs[major].fops = NULL;
142 write_unlock(&chrdevs_lock);
143 return 0;
144}
145
146
147
148
149int chrdev_open(struct inode * inode, struct file * filp)
150{
151 int ret = -ENODEV;
152
153 filp->f_op = get_chrfops(major(inode->i_rdev), minor(inode->i_rdev));
154 if (filp->f_op) {
155 ret = 0;
156 if (filp->f_op->open != NULL) {
157 lock_kernel();
158 ret = filp->f_op->open(inode,filp);
159 unlock_kernel();
160 }
161 }
162 return ret;
163}
164
165
166
167
168
169
170static struct file_operations def_chr_fops = {
171 .open = chrdev_open,
172};
173
174
175
176
177
178const char * kdevname(kdev_t dev)
179{
180 static char buffer[32];
181 sprintf(buffer, "%02x:%02x", major(dev), minor(dev));
182 return buffer;
183}
184
185const char * cdevname(kdev_t dev)
186{
187 static char buffer[32];
188 const char * name = chrdevs[major(dev)].name;
189
190 if (!name)
191 name = "unknown-char";
192 sprintf(buffer, "%s(%d,%d)", name, major(dev), minor(dev));
193 return buffer;
194}
195
196static int sock_no_open(struct inode *irrelevant, struct file *dontcare)
197{
198 return -ENXIO;
199}
200
201static struct file_operations bad_sock_fops = {
202 .open = sock_no_open
203};
204
205void init_special_inode(struct inode *inode, umode_t mode, int rdev)
206{
207 inode->i_mode = mode;
208 if (S_ISCHR(mode)) {
209 inode->i_fop = &def_chr_fops;
210 inode->i_rdev = to_kdev_t(rdev);
211 inode->i_cdev = cdget(rdev);
212 } else if (S_ISBLK(mode)) {
213 inode->i_fop = &def_blk_fops;
214 inode->i_rdev = to_kdev_t(rdev);
215 } else if (S_ISFIFO(mode))
216 inode->i_fop = &def_fifo_fops;
217 else if (S_ISSOCK(mode))
218 inode->i_fop = &bad_sock_fops;
219 else
220 printk(KERN_DEBUG "init_special_inode: bogus imode (%o)\n", mode);
221}
222