FazBrowse GitHub Viewer
|
Trending
|
URL:
|
Home
Tools:
[Download Repo ZIP]
[View Raw Code]
[Original HTTPS Page]
linux-suse/fs/char_dev.c at master · DispatchCode/linux-suse · GitHub
DispatchCode
/
linux-suse
Public
forked from
SUSE/kernel
Notifications
You must be signed in to change notification settings
Fork
0
Star
0
Code
Pull requests
0
Actions
Projects
Security and quality
0
Insights
Additional navigation options
Code
Pull requests
Actions
Projects
Security and quality
Insights
Expand file tree
Breadcrumbs
linux-suse
/
fs
/
char_dev.c
Copy path
More file actions
More file actions
Latest commit
History
History
History
689 lines (597 loc) · 16.6 KB
Breadcrumbs
linux-suse
/
fs
/
char_dev.c
Copy path
File metadata and controls
689 lines (597 loc) · 16.6 KB
Raw
Copy raw file
Download raw file
Open symbols panel
Edit and raw actions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
// SPDX-License-Identifier: GPL-2.0
/*
* linux/fs/char_dev.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
*/
#include
<linux/init.h>
#include
<linux/fs.h>
#include
<linux/kdev_t.h>
#include
<linux/slab.h>
#include
<linux/string.h>
#include
<linux/cleanup.h>
#include
<linux/major.h>
#include
<linux/errno.h>
#include
<linux/module.h>
#include
<linux/seq_file.h>
#include
<linux/kobject.h>
#include
<linux/kobj_map.h>
#include
<linux/cdev.h>
#include
<linux/mutex.h>
#include
<linux/backing-dev.h>
#include
<linux/tty.h>
#include
"internal.h"
static
struct
kobj_map
*
cdev_map
__ro_after_init
;
static
DEFINE_MUTEX
(
chrdevs_lock
);
#define
CHRDEV_MAJOR_HASH_SIZE
255
static
struct
char_device_struct
{
struct
char_device_struct
*
next
;
unsigned
int
major
;
unsigned
int
baseminor
;
int
minorct
;
char
name
[
64
];
struct
cdev
*
cdev
;
/* will die */
}
*
chrdevs
[
CHRDEV_MAJOR_HASH_SIZE
];
/* index in the above */
static
inline
int
major_to_index
(
unsigned
major
)
{
return
major
%
CHRDEV_MAJOR_HASH_SIZE
;
}
#ifdef
CONFIG_PROC_FS
void
chrdev_show
(
struct
seq_file
*
f
,
off_t
offset
)
{
struct
char_device_struct
*
cd
;
mutex_lock
(
&
chrdevs_lock
);
for
(
cd
=
chrdevs
[
major_to_index
(
offset
)];
cd
;
cd
=
cd
->
next
) {
if
(
cd
->
major
==
offset
)
seq_printf
(
f
,
"%3d %s\n"
,
cd
->
major
,
cd
->
name
);
}
mutex_unlock
(
&
chrdevs_lock
);
}
#endif
/* CONFIG_PROC_FS */
static
int
find_dynamic_major
(
void
)
{
int
i
;
struct
char_device_struct
*
cd
;
for
(
i
=
ARRAY_SIZE
(
chrdevs
)
-
1
;
i
>=
CHRDEV_MAJOR_DYN_END
;
i
--
) {
if
(
chrdevs
[
i
]
==
NULL
)
return
i
;
}
for
(
i
=
CHRDEV_MAJOR_DYN_EXT_START
;
i
>=
CHRDEV_MAJOR_DYN_EXT_END
;
i
--
) {
for
(
cd
=
chrdevs
[
major_to_index
(
i
)];
cd
;
cd
=
cd
->
next
)
if
(
cd
->
major
==
i
)
break
;
if
(
cd
==
NULL
)
return
i
;
}
return
-
EBUSY
;
}
/*
* Register a single major with a specified minor range.
*
* If major == 0 this function will dynamically allocate an unused major.
* If major > 0 this function will attempt to reserve the range of minors
* with given major.
*
*/
static
struct
char_device_struct
*
__register_chrdev_region
(
unsigned
int
major
,
unsigned
int
baseminor
,
int
minorct
,
const
char
*
name
)
{
struct
char_device_struct
*
cd
__free
(
kfree
)
=
NULL
;
struct
char_device_struct
*
curr
,
*
prev
=
NULL
;
int
ret
;
int
i
;
if
(
major
>=
CHRDEV_MAJOR_MAX
) {
pr_err
(
"CHRDEV \"%s\" major requested (%u) is greater than the maximum (%u)\n"
,
name
,
major
,
CHRDEV_MAJOR_MAX
-
1
);
return
ERR_PTR
(
-
EINVAL
);
}
if
(
minorct
>
MINORMASK
+
1
-
baseminor
) {
pr_err
(
"CHRDEV \"%s\" minor range requested (%u-%u) is out of range of maximum range (%u-%u) for a single major\n"
,
name
,
baseminor
,
baseminor
+
minorct
-
1
,
0
,
MINORMASK
);
return
ERR_PTR
(
-
EINVAL
);
}
cd
=
kzalloc_obj
(
struct
char_device_struct
);
if
(
cd
==
NULL
)
return
ERR_PTR
(
-
ENOMEM
);
guard
(
mutex
)(
&
chrdevs_lock
);
if
(
major
==
0
) {
ret
=
find_dynamic_major
();
if
(
ret
<
0
) {
pr_err
(
"CHRDEV \"%s\" dynamic allocation region is full\n"
,
name
);
return
ERR_PTR
(
ret
);
}
major
=
ret
;
}
ret
=
-
EBUSY
;
i
=
major_to_index
(
major
);
for
(
curr
=
chrdevs
[
i
];
curr
;
prev
=
curr
,
curr
=
curr
->
next
) {
if
(
curr
->
major
<
major
)
continue
;
if
(
curr
->
major
>
major
)
break
;
if
(
curr
->
baseminor
+
curr
->
minorct
<=
baseminor
)
continue
;
if
(
curr
->
baseminor
>=
baseminor
+
minorct
)
break
;
return
ERR_PTR
(
ret
);
}
cd
->
major
=
major
;
cd
->
baseminor
=
baseminor
;
cd
->
minorct
=
minorct
;
strscpy
(
cd
->
name
,
name
,
sizeof
(
cd
->
name
));
if
(!
prev
) {
cd
->
next
=
curr
;
chrdevs
[
i
]
=
cd
;
}
else
{
cd
->
next
=
prev
->
next
;
prev
->
next
=
cd
;
}
return_ptr
(
cd
);
}
static
struct
char_device_struct
*
__unregister_chrdev_region
(
unsigned
major
,
unsigned
baseminor
,
int
minorct
)
{
struct
char_device_struct
*
cd
=
NULL
,
*
*
cp
;
int
i
=
major_to_index
(
major
);
mutex_lock
(
&
chrdevs_lock
);
for
(
cp
=
&
chrdevs
[
i
];
*
cp
;
cp
=
&
(
*
cp
)
->
next
)
if
((
*
cp
)
->
major
==
major
&&
(
*
cp
)
->
baseminor
==
baseminor
&&
(
*
cp
)
->
minorct
==
minorct
)
break
;
if
(
*
cp
) {
cd
=
*
cp
;
*
cp
=
cd
->
next
;
}
mutex_unlock
(
&
chrdevs_lock
);
return
cd
;
}
/**
* register_chrdev_region() - register a range of device numbers
* @from: the first in the desired range of device numbers; must include
* the major number.
* @count: the number of consecutive device numbers required
* @name: the name of the device or driver.
*
* Return value is zero on success, a negative error code on failure.
*/
int
register_chrdev_region
(
dev_t
from
,
unsigned
count
,
const
char
*
name
)
{
struct
char_device_struct
*
cd
;
dev_t
to
=
from
+
count
;
dev_t
n
,
next
;
for
(
n
=
from
;
n
<
to
;
n
=
next
) {
next
=
MKDEV
(
MAJOR
(
n
)
+
1
,
0
);
if
(
next
>
to
)
next
=
to
;
cd
=
__register_chrdev_region
(
MAJOR
(
n
),
MINOR
(
n
),
next
-
n
,
name
);
if
(
IS_ERR
(
cd
))
goto
fail
;
}
return
0
;
fail
:
to
=
n
;
for
(
n
=
from
;
n
<
to
;
n
=
next
) {
next
=
MKDEV
(
MAJOR
(
n
)
+
1
,
0
);
kfree
(
__unregister_chrdev_region
(
MAJOR
(
n
),
MINOR
(
n
),
next
-
n
));
}
return
PTR_ERR
(
cd
);
}
/**
* alloc_chrdev_region() - register a range of char device numbers
* @dev: output parameter for first assigned number
* @baseminor: first of the requested range of minor numbers
* @count: the number of minor numbers required
* @name: the name of the associated device or driver
*
* Allocates a range of char device numbers. The major number will be
* chosen dynamically, and returned (along with the first minor number)
* in @dev. Returns zero or a negative error code.
*/
int
alloc_chrdev_region
(
dev_t
*
dev
,
unsigned
baseminor
,
unsigned
count
,
const
char
*
name
)
{
struct
char_device_struct
*
cd
;
cd
=
__register_chrdev_region
(
0
,
baseminor
,
count
,
name
);
if
(
IS_ERR
(
cd
))
return
PTR_ERR
(
cd
);
*
dev
=
MKDEV
(
cd
->
major
,
cd
->
baseminor
);
return
0
;
}
/**
* __register_chrdev() - create and register a cdev occupying a range of minors
* @major: major device number or 0 for dynamic allocation
* @baseminor: first of the requested range of minor numbers
* @count: the number of minor numbers required
* @name: name of this range of devices
* @fops: file operations associated with this devices
*
* If @major == 0 this functions will dynamically allocate a major and return
* its number.
*
* If @major > 0 this function will attempt to reserve a device with the given
* major number and will return zero on success.
*
* Returns a -ve errno on failure.
*
* The name of this device has nothing to do with the name of the device in
* /dev. It only helps to keep track of the different owners of devices. If
* your module name has only one type of devices it's ok to use e.g. the name
* of the module here.
*/
int
__register_chrdev
(
unsigned
int
major
,
unsigned
int
baseminor
,
unsigned
int
count
,
const
char
*
name
,
const
struct
file_operations
*
fops
)
{
struct
char_device_struct
*
cd
;
struct
cdev
*
cdev
;
int
err
=
-
ENOMEM
;
cd
=
__register_chrdev_region
(
major
,
baseminor
,
count
,
name
);
if
(
IS_ERR
(
cd
))
return
PTR_ERR
(
cd
);
cdev
=
cdev_alloc
();
if
(!
cdev
)
goto
out2
;
cdev
->
owner
=
fops
->
owner
;
cdev
->
ops
=
fops
;
kobject_set_name
(
&
cdev
->
kobj
,
"%s"
,
name
);
err
=
cdev_add
(
cdev
,
MKDEV
(
cd
->
major
,
baseminor
),
count
);
if
(
err
)
goto
out
;
cd
->
cdev
=
cdev
;
return
major
?
0
:
cd
->
major
;
out
:
kobject_put
(
&
cdev
->
kobj
);
out2
:
kfree
(
__unregister_chrdev_region
(
cd
->
major
,
baseminor
,
count
));
return
err
;
}
/**
* unregister_chrdev_region() - unregister a range of device numbers
* @from: the first in the range of numbers to unregister
* @count: the number of device numbers to unregister
*
* This function will unregister a range of @count device numbers,
* starting with @from. The caller should normally be the one who
* allocated those numbers in the first place...
*/
void
unregister_chrdev_region
(
dev_t
from
,
unsigned
count
)
{
dev_t
to
=
from
+
count
;
dev_t
n
,
next
;
for
(
n
=
from
;
n
<
to
;
n
=
next
) {
next
=
MKDEV
(
MAJOR
(
n
)
+
1
,
0
);
if
(
next
>
to
)
next
=
to
;
kfree
(
__unregister_chrdev_region
(
MAJOR
(
n
),
MINOR
(
n
),
next
-
n
));
}
}
/**
* __unregister_chrdev - unregister and destroy a cdev
* @major: major device number
* @baseminor: first of the range of minor numbers
* @count: the number of minor numbers this cdev is occupying
* @name: name of this range of devices
*
* Unregister and destroy the cdev occupying the region described by
* @major, @baseminor and @count. This function undoes what
* __register_chrdev() did.
*/
void
__unregister_chrdev
(
unsigned
int
major
,
unsigned
int
baseminor
,
unsigned
int
count
,
const
char
*
name
)
{
struct
char_device_struct
*
cd
;
cd
=
__unregister_chrdev_region
(
major
,
baseminor
,
count
);
if
(
cd
&&
cd
->
cdev
)
cdev_del
(
cd
->
cdev
);
kfree
(
cd
);
}
static
__cacheline_aligned_in_smp
DEFINE_SPINLOCK
(
cdev_lock
);
static
struct
kobject
*
cdev_get
(
struct
cdev
*
p
)
{
struct
module
*
owner
=
p
->
owner
;
struct
kobject
*
kobj
;
if
(!
try_module_get
(
owner
))
return
NULL
;
kobj
=
kobject_get_unless_zero
(
&
p
->
kobj
);
if
(!
kobj
)
module_put
(
owner
);
return
kobj
;
}
void
cdev_put
(
struct
cdev
*
p
)
{
if
(
p
) {
struct
module
*
owner
=
p
->
owner
;
kobject_put
(
&
p
->
kobj
);
module_put
(
owner
);
}
}
/*
* Called every time a character special file is opened
*/
static
int
chrdev_open
(
struct
inode
*
inode
,
struct
file
*
filp
)
{
const
struct
file_operations
*
fops
;
struct
cdev
*
p
;
struct
cdev
*
new
=
NULL
;
int
ret
=
0
;
spin_lock
(
&
cdev_lock
);
p
=
inode
->
i_cdev
;
if
(!
p
) {
struct
kobject
*
kobj
;
int
idx
;
spin_unlock
(
&
cdev_lock
);
kobj
=
kobj_lookup
(
cdev_map
,
inode
->
i_rdev
,
&
idx
);
if
(!
kobj
)
return
-
ENXIO
;
new
=
container_of
(
kobj
,
struct
cdev
,
kobj
);
spin_lock
(
&
cdev_lock
);
/* Check i_cdev again in case somebody beat us to it while
we dropped the lock. */
p
=
inode
->
i_cdev
;
if
(!
p
) {
inode
->
i_cdev
=
p
=
new
;
list_add
(
&
inode
->
i_devices
,
&
p
->
list
);
new
=
NULL
;
}
else
if
(!
cdev_get
(
p
))
ret
=
-
ENXIO
;
}
else
if
(!
cdev_get
(
p
))
ret
=
-
ENXIO
;
spin_unlock
(
&
cdev_lock
);
cdev_put
(
new
);
if
(
ret
)
return
ret
;
ret
=
-
ENXIO
;
fops
=
fops_get
(
p
->
ops
);
if
(!
fops
)
goto
out_cdev_put
;
replace_fops
(
filp
,
fops
);
if
(
filp
->
f_op
->
open
) {
ret
=
filp
->
f_op
->
open
(
inode
,
filp
);
if
(
ret
)
goto
out_cdev_put
;
}
return
0
;
out_cdev_put
:
cdev_put
(
p
);
return
ret
;
}
void
cd_forget
(
struct
inode
*
inode
)
{
spin_lock
(
&
cdev_lock
);
list_del_init
(
&
inode
->
i_devices
);
inode
->
i_cdev
=
NULL
;
inode
->
i_mapping
=
&
inode
->
i_data
;
spin_unlock
(
&
cdev_lock
);
}
static
void
cdev_purge
(
struct
cdev
*
cdev
)
{
spin_lock
(
&
cdev_lock
);
while
(!
list_empty
(
&
cdev
->
list
)) {
struct
inode
*
inode
;
inode
=
container_of
(
cdev
->
list
.
next
,
struct
inode
,
i_devices
);
list_del_init
(
&
inode
->
i_devices
);
inode
->
i_cdev
=
NULL
;
}
spin_unlock
(
&
cdev_lock
);
}
/*
* Dummy default file-operations: the only thing this does
* is contain the open that then fills in the correct operations
* depending on the special file...
*/
const
struct
file_operations
def_chr_fops
=
{
.
open
=
chrdev_open
,
.
llseek
=
noop_llseek
,
};
static
struct
kobject
*
exact_match
(
dev_t
dev
,
int
*
part
,
void
*
data
)
{
struct
cdev
*
p
=
data
;
return
&
p
->
kobj
;
}
static
int
exact_lock
(
dev_t
dev
,
void
*
data
)
{
struct
cdev
*
p
=
data
;
return
cdev_get
(
p
) ?
0
:
-1
;
}
/**
* cdev_add() - add a char device to the system
* @p: the cdev structure for the device
* @dev: the first device number for which this device is responsible
* @count: the number of consecutive minor numbers corresponding to this
* device
*
* cdev_add() adds the device represented by @p to the system, making it
* live immediately. A negative error code is returned on failure.
*/
int
cdev_add
(
struct
cdev
*
p
,
dev_t
dev
,
unsigned
count
)
{
int
error
;
p
->
dev
=
dev
;
p
->
count
=
count
;
if
(
WARN_ON
(
dev
==
WHITEOUT_DEV
)) {
error
=
-
EBUSY
;
goto
err
;
}
error
=
kobj_map
(
cdev_map
,
dev
,
count
,
NULL
,
exact_match
,
exact_lock
,
p
);
if
(
error
)
goto
err
;
kobject_get
(
p
->
kobj
.
parent
);
return
0
;
err
:
kfree_const
(
p
->
kobj
.
name
);
p
->
kobj
.
name
=
NULL
;
return
error
;
}
/**
* cdev_set_parent() - set the parent kobject for a char device
* @p: the cdev structure
* @kobj: the kobject to take a reference to
*
* cdev_set_parent() sets a parent kobject which will be referenced
* appropriately so the parent is not freed before the cdev. This
* should be called before cdev_add.
*/
void
cdev_set_parent
(
struct
cdev
*
p
,
struct
kobject
*
kobj
)
{
WARN_ON
(!
kobj
->
state_initialized
);
p
->
kobj
.
parent
=
kobj
;
}
/**
* cdev_device_add() - add a char device and it's corresponding
* struct device, linkink
* @dev: the device structure
* @cdev: the cdev structure
*
* cdev_device_add() adds the char device represented by @cdev to the system,
* just as cdev_add does. It then adds @dev to the system using device_add
* The dev_t for the char device will be taken from the struct device which
* needs to be initialized first. This helper function correctly takes a
* reference to the parent device so the parent will not get released until
* all references to the cdev are released.
*
* This helper uses dev->devt for the device number. If it is not set
* it will not add the cdev and it will be equivalent to device_add.
*
* This function should be used whenever the struct cdev and the
* struct device are members of the same structure whose lifetime is
* managed by the struct device.
*
* NOTE: Callers must assume that userspace was able to open the cdev and
* can call cdev fops callbacks at any time, even if this function fails.
*/
int
cdev_device_add
(
struct
cdev
*
cdev
,
struct
device
*
dev
)
{
int
rc
=
0
;
if
(
dev
->
devt
) {
cdev_set_parent
(
cdev
,
&
dev
->
kobj
);
rc
=
cdev_add
(
cdev
,
dev
->
devt
,
1
);
if
(
rc
)
return
rc
;
}
rc
=
device_add
(
dev
);
if
(
rc
&&
dev
->
devt
)
cdev_del
(
cdev
);
return
rc
;
}
/**
* cdev_device_del() - inverse of cdev_device_add
* @cdev: the cdev structure
* @dev: the device structure
*
* cdev_device_del() is a helper function to call cdev_del and device_del.
* It should be used whenever cdev_device_add is used.
*
* If dev->devt is not set it will not remove the cdev and will be equivalent
* to device_del.
*
* NOTE: This guarantees that associated sysfs callbacks are not running
* or runnable, however any cdevs already open will remain and their fops
* will still be callable even after this function returns.
*/
void
cdev_device_del
(
struct
cdev
*
cdev
,
struct
device
*
dev
)
{
device_del
(
dev
);
if
(
dev
->
devt
)
cdev_del
(
cdev
);
}
static
void
cdev_unmap
(
dev_t
dev
,
unsigned
count
)
{
kobj_unmap
(
cdev_map
,
dev
,
count
);
}
/**
* cdev_del() - remove a cdev from the system
* @p: the cdev structure to be removed
*
* cdev_del() removes @p from the system, possibly freeing the structure
* itself.
*
* NOTE: This guarantees that cdev device will no longer be able to be
* opened, however any cdevs already open will remain and their fops will
* still be callable even after cdev_del returns.
*/
void
cdev_del
(
struct
cdev
*
p
)
{
cdev_unmap
(
p
->
dev
,
p
->
count
);
kobject_put
(
&
p
->
kobj
);
}
static
void
cdev_default_release
(
struct
kobject
*
kobj
)
{
struct
cdev
*
p
=
container_of
(
kobj
,
struct
cdev
,
kobj
);
struct
kobject
*
parent
=
kobj
->
parent
;
cdev_purge
(
p
);
kobject_put
(
parent
);
}
static
void
cdev_dynamic_release
(
struct
kobject
*
kobj
)
{
struct
cdev
*
p
=
container_of
(
kobj
,
struct
cdev
,
kobj
);
struct
kobject
*
parent
=
kobj
->
parent
;
cdev_purge
(
p
);
kfree
(
p
);
kobject_put
(
parent
);
}
static
struct
kobj_type
ktype_cdev_default
=
{
.
release
=
cdev_default_release
,
};
static
struct
kobj_type
ktype_cdev_dynamic
=
{
.
release
=
cdev_dynamic_release
,
};
/**
* cdev_alloc() - allocate a cdev structure
*
* Allocates and returns a cdev structure, or NULL on failure.
*/
struct
cdev
*
cdev_alloc
(
void
)
{
struct
cdev
*
p
=
kzalloc_obj
(
struct
cdev
);
if
(
p
) {
INIT_LIST_HEAD
(
&
p
->
list
);
kobject_init
(
&
p
->
kobj
,
&
ktype_cdev_dynamic
);
}
return
p
;
}
/**
* cdev_init() - initialize a cdev structure
* @cdev: the structure to initialize
* @fops: the file_operations for this device
*
* Initializes @cdev, remembering @fops, making it ready to add to the
* system with cdev_add().
*/
void
cdev_init
(
struct
cdev
*
cdev
,
const
struct
file_operations
*
fops
)
{
memset
(
cdev
,
0
,
sizeof
*
cdev
);
INIT_LIST_HEAD
(
&
cdev
->
list
);
kobject_init
(
&
cdev
->
kobj
,
&
ktype_cdev_default
);
cdev
->
ops
=
fops
;
}
static
struct
kobject
*
base_probe
(
dev_t
dev
,
int
*
part
,
void
*
data
)
{
if
(
request_module
(
"char-major-%d-%d"
,
MAJOR
(
dev
),
MINOR
(
dev
))
>
0
)
/* Make old-style 2.4 aliases work */
request_module
(
"char-major-%d"
,
MAJOR
(
dev
));
return
NULL
;
}
void
__init
chrdev_init
(
void
)
{
cdev_map
=
kobj_map_init
(
base_probe
,
&
chrdevs_lock
);
}
/* Let modules do char dev stuff */
EXPORT_SYMBOL
(
register_chrdev_region
);
EXPORT_SYMBOL
(
unregister_chrdev_region
);
EXPORT_SYMBOL
(
alloc_chrdev_region
);
EXPORT_SYMBOL
(
cdev_init
);
EXPORT_SYMBOL
(
cdev_alloc
);
EXPORT_SYMBOL
(
cdev_del
);
EXPORT_SYMBOL
(
cdev_add
);
EXPORT_SYMBOL
(
cdev_set_parent
);
EXPORT_SYMBOL
(
cdev_device_add
);
EXPORT_SYMBOL
(
cdev_device_del
);
EXPORT_SYMBOL
(
__register_chrdev
);
EXPORT_SYMBOL
(
__unregister_chrdev
);
Back
|
FazBrowse Home
|
New Git URL