1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * ISA Plug & Play support |
4 | * Copyright (c) by Jaroslav Kysela <perex@perex.cz> |
5 | */ |
6 | |
7 | #include <linux/module.h> |
8 | #include <linux/isapnp.h> |
9 | #include <linux/proc_fs.h> |
10 | #include <linux/init.h> |
11 | #include <linux/uaccess.h> |
12 | |
13 | extern struct pnp_protocol isapnp_protocol; |
14 | |
15 | static struct proc_dir_entry *isapnp_proc_bus_dir = NULL; |
16 | |
17 | static loff_t isapnp_proc_bus_lseek(struct file *file, loff_t off, int whence) |
18 | { |
19 | return fixed_size_llseek(file, offset: off, whence, size: 256); |
20 | } |
21 | |
22 | static ssize_t isapnp_proc_bus_read(struct file *file, char __user * buf, |
23 | size_t nbytes, loff_t * ppos) |
24 | { |
25 | struct pnp_dev *dev = pde_data(inode: file_inode(f: file)); |
26 | int pos = *ppos; |
27 | int cnt, size = 256; |
28 | |
29 | if (pos >= size) |
30 | return 0; |
31 | if (nbytes >= size) |
32 | nbytes = size; |
33 | if (pos + nbytes > size) |
34 | nbytes = size - pos; |
35 | cnt = nbytes; |
36 | |
37 | if (!access_ok(buf, cnt)) |
38 | return -EINVAL; |
39 | |
40 | isapnp_cfg_begin(csn: dev->card->number, device: dev->number); |
41 | for (; pos < 256 && cnt > 0; pos++, buf++, cnt--) { |
42 | unsigned char val; |
43 | val = isapnp_read_byte(idx: pos); |
44 | __put_user(val, buf); |
45 | } |
46 | isapnp_cfg_end(); |
47 | |
48 | *ppos = pos; |
49 | return nbytes; |
50 | } |
51 | |
52 | static const struct proc_ops isapnp_proc_bus_proc_ops = { |
53 | .proc_lseek = isapnp_proc_bus_lseek, |
54 | .proc_read = isapnp_proc_bus_read, |
55 | }; |
56 | |
57 | static int isapnp_proc_attach_device(struct pnp_dev *dev) |
58 | { |
59 | struct pnp_card *bus = dev->card; |
60 | char name[16]; |
61 | |
62 | if (!bus->procdir) { |
63 | sprintf(buf: name, fmt: "%02x" , bus->number); |
64 | bus->procdir = proc_mkdir(name, isapnp_proc_bus_dir); |
65 | if (!bus->procdir) |
66 | return -ENOMEM; |
67 | } |
68 | sprintf(buf: name, fmt: "%02x" , dev->number); |
69 | dev->procent = proc_create_data(name, S_IFREG | S_IRUGO, bus->procdir, |
70 | &isapnp_proc_bus_proc_ops, dev); |
71 | if (!dev->procent) |
72 | return -ENOMEM; |
73 | proc_set_size(dev->procent, 256); |
74 | return 0; |
75 | } |
76 | |
77 | int __init isapnp_proc_init(void) |
78 | { |
79 | struct pnp_dev *dev; |
80 | |
81 | isapnp_proc_bus_dir = proc_mkdir("bus/isapnp" , NULL); |
82 | protocol_for_each_dev(&isapnp_protocol, dev) { |
83 | isapnp_proc_attach_device(dev); |
84 | } |
85 | return 0; |
86 | } |
87 | |