[linux 驱动]input输入子系统详解与实战

目录

1 描述

2 结构体

2.1 input_class

2.2 input_dev

2.4 input_event

2.4 input_dev_type

3 input接口

3.1 input_allocate_device

3.2 input_free_device

3.3 input_register_device

3.4 input_unregister_device

3.5 input_event

3.6 input_sync

3.7 input_set_capability

4 input.c分析

4.1 注册字符设备

4.2 函数分析

4.2.1 class_register

4.2.2 dev_name

4.2.3 register_chrdev_region

4.2.3 MKDEV

5 示例

5.1 示例 1

5.1.1 代码

5.1.2 操作

5.2 mcu_cec驱动分析

5.2.1 注册输入事件

5.2.2 函数分析

5.2.2.1 devm_input_allocate_device

5.2.3 上报事件


1 描述

        input 子系统分为 input 驱动层、input 核心层、input 事件处理层,最终给用户空间提供可访问的设备节点,input 子系统框架如图

驱动层:输入设备的具体驱动程序,比如按键驱动程序,向内核层报告输入内容。

核心层:承上启下,为驱动层提供输入设备 注册和操作接口。通知事件层对输入事件进行 处理。

事件层:主要和用户空间进行交互。

        input 核心层会向 Linux 内核注册一个字符设备,在 drivers/input/input.c文件中描述

        如何找到input 设备节点文件对应的具体设备?

        方法(1):使用sudo cat 命令打开/dev/input/下的设备节点文件,然后分别操作各个输入设备,如果有输出有乱码,说明此时打开的设备文件就对应于当前的输入设备。

        方法(2):使用sudo hexdump 命令打开/dev/input/下设备文件,然后分别操作各个输入设备,如果有输出16进制数,说明此时打开的设备文件就对应于当前的输入设备。

        方法(3):查看/proc/bus/input/devices文件,里面记载了各个已经安装输入设备的信息。通过文件内容name和handler来查看设备文件与设备的对应关系。

127|rk3399_Android11:/ # cat /proc/bus/input/devices
I: Bus=0019 Vendor=524b Product=0006 Version=0100
N: Name="ff420030.pwm"
P: Phys=gpio-keys/remotectl
S: Sysfs=/devices/platform/ff420030.pwm/input/input0
U: Uniq=
H: Handlers=event0 cpufreq dmcfreq
B: PROP=0
B: EV=3
B: KEY=70010 20000000000000 0 100010002000000 78000004000a800 1e16c000000000 10004ffcI: Bus=0019 Vendor=0001 Product=0001 Version=0100
N: Name="adc_keys"
P: Phys=adc-keys/input0
S: Sysfs=/devices/platform/adc_keys/input/input1
U: Uniq=
H: Handlers=event1 cpufreq dmcfreq
B: PROP=0
B: EV=3
B: KEY=c000000000000 0I: Bus=0019 Vendor=0001 Product=0001 Version=0100
N: Name="gpio-keys"
P: Phys=gpio-keys/input0
S: Sysfs=/devices/platform/gpio-keys/input/input2
U: Uniq=
H: Handlers=event2 cpufreq dmcfreq
B: PROP=0
B: EV=100003
B: KEY=10000000000000 0I: Bus=0000 Vendor=0001 Product=0001 Version=0100
N: Name="rk-headset"
P: Phys=
S: Sysfs=/devices/platform/rk-headset/input/input3
U: Uniq=
H: Handlers=event3
B: PROP=0
B: EV=3
B: KEY=400000000 0 0 0rk3399_Android11:/ #

2 结构体

2.1 input_class

1782 struct class input_class = {
1783         .name           = "input",
1784         .devnode        = input_devnode,
1785 };

2.2 input_dev

        input_dev 结构体是用于描述和管理输入设备的复杂数据结构,涵盖了设备的基本信息、支持的事件、状态管理、操作函数等多个方面,是 Linux 输入子系统的重要组成部分。

130 struct input_dev {
131         const char *name;
132         const char *phys;
133         const char *uniq;
134         struct input_id id;
135 
136         unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];
137 
138         unsigned long evbit[BITS_TO_LONGS(EV_CNT)];
139         unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];
140         unsigned long relbit[BITS_TO_LONGS(REL_CNT)];
141         unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];
142         unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];
143         unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];
144         unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];
145         unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];
146         unsigned long swbit[BITS_TO_LONGS(SW_CNT)];
147 
148         unsigned int hint_events_per_packet;
149 
150         unsigned int keycodemax;
151         unsigned int keycodesize;
152         void *keycode;
153 
154         int (*setkeycode)(struct input_dev *dev,
155                           const struct input_keymap_entry *ke,
156                           unsigned int *old_keycode);
157         int (*getkeycode)(struct input_dev *dev,
158                           struct input_keymap_entry *ke);
159 
160         struct ff_device *ff;
161 
162         unsigned int repeat_key;
163         struct timer_list timer;
164 
165         int rep[REP_CNT];
166 
167         struct input_mt *mt;
168 
169         struct input_absinfo *absinfo;
170 
171         unsigned long key[BITS_TO_LONGS(KEY_CNT)];
172         unsigned long led[BITS_TO_LONGS(LED_CNT)];
173         unsigned long snd[BITS_TO_LONGS(SND_CNT)];
174         unsigned long sw[BITS_TO_LONGS(SW_CNT)];
175 
176         int (*open)(struct input_dev *dev);
177         void (*close)(struct input_dev *dev);
178         int (*flush)(struct input_dev *dev, struct file *file);
179         int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);
180 
181         struct input_handle __rcu *grab;
182 
183         spinlock_t event_lock;
184         struct mutex mutex;
185 
186         unsigned int users;
187         bool going_away;
188 
189         struct device dev;
190 
191         struct list_head        h_list;
192         struct list_head        node;
193 
194         unsigned int num_vals;
195         unsigned int max_vals;
196         struct input_value *vals;
197 
198         bool devres_managed;
199 
200         ktime_t timestamp[INPUT_CLK_MAX];
201 };

基本信息

const char *name;

const char *phys;

const char *uniq;

name: 输入设备的名称。

phys: 物理路径,通常用于描述设备的实际连接地址。

uniq: 设备的唯一标识符,用于区分不同设备。

设备 ID

struct input_id id;

id: 包含设备类型、制造商和产品 ID 的结构体,用于唯一标识输入设备。

属性位

unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];

这些位字段用于表示设备的特性(如支持哪些输入属性)。

evbit: 表示设备支持的事件类型(如按键、鼠标移动等)。

keybit: 表示支持的按键。

relbit: 表示支持的相对坐标变化事件(如鼠标移动)。

absbit: 表示支持的绝对坐标事件。

mscbit: 表示支持的特殊事件类型。

ledbit: 表示支持的 LED 控制。

sndbit: 表示支持的声音控制。

ffbit: 表示支持的力反馈事件。

swbit: 表示支持的开关状态。

键盘设置

unsigned int keycodemax;

unsigned int keycodesize;

void *keycode;

keycodemax: 最大键码数量。

keycodesize: 每个键码的大小。

keycode: 指向键码数组的指针。

键码处理函数

int (*setkeycode)(struct input_dev *dev, const struct input_keymap_entry *ke, unsigned int *old_keycode);

int (*getkeycode)(struct input_dev *dev, struct input_keymap_entry *ke);

setkeycode: 设置键码的回调函数。

getkeycode: 获取键码的回调函数。

其他功能

unsigned int repeat_key;

struct timer_list timer;

repeat_key: 用于设置键重复的时间间隔。

timer: 定时器,用于处理键重复事件。

多点触控和绝对信息

struct input_mt *mt;

struct input_absinfo *absinfo;

mt: 多点触控相关信息。

absinfo: 绝对坐标信息。

状态管理

unsigned long key[BITS_TO_LONGS(KEY_CNT)];

unsigned long led[BITS_TO_LONGS(LED_CNT)];

unsigned long snd[BITS_TO_LONGS(SND_CNT)];

unsigned long sw[BITS_TO_LONGS(SW_CNT)];

用于存储设备当前状态的信息,包括按键状态、LED 状态、声音状态和开关状态。

设备操作函数

int (*open)(struct input_dev *dev);

void (*close)(struct input_dev *dev);

int (*flush)(struct input_dev *dev, struct file *file);

int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);

open: 打开设备的回调函数。

close: 关闭设备的回调函数。

flush: 刷新设备状态的回调函数。

event: 处理输入事件的回调函数。

锁和同步

spinlock_t event_lock;

struct mutex mutex;

event_lock: 自旋锁,用于保护事件队列。

mutex: 互斥锁,用于设备操作的同步。

引用计数和状态

unsigned int users;

bool going_away;

users: 当前打开设备的用户数量。

going_away: 表示设备是否正在被移除。

设备结构

struct device dev;

dev: 表示内核设备结构,包含设备的基本信息。

列表和管理

struct list_head h_list;

struct list_head node;

h_list: 用于哈希表中的链表节点。

node: 用于设备列表中的链表节点。

值管理

unsigned int num_vals;

unsigned int max_vals;

struct input_value *vals;

num_vals: 当前值的数量。

max_vals: 最大值数量。

vals: 指向输入值数组的指针。

设备资源管理

bool devres_managed;

devres_managed: 表示设备资源是否由设备管理系统管理。

时间戳

ktime_t timestamp[INPUT_CLK_MAX];

存储输入事件的时间戳。

2.4 input_event

        Linux 内核使用 input_event 这个结构体来表示所有的输入事件

28 struct input_event {29 #if (__BITS_PER_LONG != 32 || !defined(__USE_TIME_BITS64)) && !defined(__KERNEL__)30         struct timeval time;31 #define input_event_sec time.tv_sec32 #define input_event_usec time.tv_usec33 #else    34         __kernel_ulong_t __sec;35 #if defined(__sparc__) && defined(__arch64__)36         unsigned int __usec;37         unsigned int __pad;38 #else    39         __kernel_ulong_t __usec;40 #endif41 #define input_event_sec  __sec42 #define input_event_usec __usec43 #endif44         __u16 type;45         __u16 code;46         __s32 value;47 };

type事件类型,比如 EV_KEY,表示此次事件为按键事件,此成员变量为 16 位。

code:事件码,比如在 EV_KEY 事件中 code 就表示具体的按键码,如:KEY_0、KEY_1等等这些按键。此成员变量为 16 位。

value:值,比如 EV_KEY 事件中 value 就是按键值,表示按键有没有被按下,如果为 1 的话说明按键按下,如果为 0 的话说明按键没有被按下或者按键松开了。

2.4 input_dev_type

1768 static const struct device_type input_dev_type = {
1769         .groups         = input_dev_attr_groups,
1770         .release        = input_dev_release,
1771         .uevent         = input_dev_uevent,
1772 #ifdef CONFIG_PM_SLEEP
1773         .pm             = &input_dev_pm_ops,
1774 #endif
1775 };
1519 static const struct attribute_group *input_dev_attr_groups[] = {
1520         &input_dev_attr_group,
1521         &input_dev_id_attr_group,
1522         &input_dev_caps_attr_group,
1523         NULL
1524 };
1526 static void input_dev_release(struct device *device)
1527 {
1528         struct input_dev *dev = to_input_dev(device);
1529 
1530         input_ff_destroy(dev);
1531         input_mt_destroy_slots(dev);
1532         kfree(dev->absinfo);
1533         kfree(dev->vals);
1534         kfree(dev);
1535 
1536         module_put(THIS_MODULE);
1537 }
1759 static const struct dev_pm_ops input_dev_pm_ops = {
1760         .suspend        = input_dev_suspend,
1761         .resume         = input_dev_resume,
1762         .freeze         = input_dev_freeze,
1763         .poweroff       = input_dev_poweroff,
1764         .restore        = input_dev_resume,
1765 };

3 input接口

3.1 input_allocate_device

函数原型

struct input_dev *input_allocate_device(void)

返回值

struct input_dev *

成功:input_dev结构体指针 失败:NULL

功能

申请一个 input_dev

1797 struct input_dev *input_allocate_device(void)
1798 {  
1799         static atomic_t input_no = ATOMIC_INIT(-1);
1800         struct input_dev *dev;
1801    
1802         dev = kzalloc(sizeof(*dev), GFP_KERNEL);
1803         if (dev) {
1804                 dev->dev.type = &input_dev_type;
1805                 dev->dev.class = &input_class;
1806                 device_initialize(&dev->dev);
1807                 mutex_init(&dev->mutex);
1808                 spin_lock_init(&dev->event_lock);
1809                 timer_setup(&dev->timer, NULL, 0);
1810                 INIT_LIST_HEAD(&dev->h_list);
1811                 INIT_LIST_HEAD(&dev->node);
1812    
1813                 dev_set_name(&dev->dev, "input%lu",
1814                              (unsigned long)atomic_inc_return(&input_no));
1815    
1816                 __module_get(THIS_MODULE);
1817         }
1818    
1819         return dev;
1820 }  
1768 static const struct device_type input_dev_type = {
1769         .groups         = input_dev_attr_groups,
1770         .release        = input_dev_release,
1771         .uevent         = input_dev_uevent,
1772 #ifdef CONFIG_PM_SLEEP
1773         .pm             = &input_dev_pm_ops,
1774 #endif
1775 };

3.2 input_free_device

函数原型

void input_free_device(struct input_dev *dev)

参数

struct input_dev *

input_dev结构体指针

功能

释放一个 input_dev

1902 void input_free_device(struct input_dev *dev)
1903 {
1904         if (dev) {
1905                 if (dev->devres_managed)
1906                         WARN_ON(devres_destroy(dev->dev.parent,
1907                                                 devm_input_device_release,
1908                                                 devm_input_device_match,
1909                                                 dev));
1910                 input_put_device(dev);
1911         }
1912 }

3.3 input_register_device

函数原型

int input_register_device(struct input_dev *dev)

参数

struct input_dev *dev

input_dev结构体指针

返回值

int

成功:0 失败:错误码

功能

向 Linux 内核注册 input_dev

2140 int input_register_device(struct input_dev *dev)
2141 {
2142         struct input_devres *devres = NULL;
2143         struct input_handler *handler;
2144         unsigned int packet_size;
2145         const char *path;
2146         int error;
2147 
2148         if (test_bit(EV_ABS, dev->evbit) && !dev->absinfo) {
2149                 dev_err(&dev->dev,
2150                         "Absolute device without dev->absinfo, refusing to register\n");
2151                 return -EINVAL;
2152         }
2153 
2154         if (dev->devres_managed) {
2155                 devres = devres_alloc(devm_input_device_unregister,
2156                                       sizeof(*devres), GFP_KERNEL);
2157                 if (!devres)
2158                         return -ENOMEM;
2159 
2160                 devres->input = dev;
2161         }
2162 
2163         /* Every input device generates EV_SYN/SYN_REPORT events. */
2164         __set_bit(EV_SYN, dev->evbit);
2165 
2166         /* KEY_RESERVED is not supposed to be transmitted to userspace. */
2167         __clear_bit(KEY_RESERVED, dev->keybit);
2168 
2169         /* Make sure that bitmasks not mentioned in dev->evbit are clean. */
2170         input_cleanse_bitmasks(dev);
2171 
2172         packet_size = input_estimate_events_per_packet(dev);
2173         if (dev->hint_events_per_packet < packet_size)
2174                 dev->hint_events_per_packet = packet_size;
2175 
2176         dev->max_vals = dev->hint_events_per_packet + 2;
2177         dev->vals = kcalloc(dev->max_vals, sizeof(*dev->vals), GFP_KERNEL);
2178         if (!dev->vals) {
2179                 error = -ENOMEM;
2180                 goto err_devres_free;
2181         }
2182 
2183         /*
2184          * If delay and period are pre-set by the driver, then autorepeating
2185          * is handled by the driver itself and we don't do it in input.c.
2186          */
2187         if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD])
2188                 input_enable_softrepeat(dev, 250, 33);
2189 
2190         if (!dev->getkeycode)
2191                 dev->getkeycode = input_default_getkeycode;
2192 
2193         if (!dev->setkeycode)
2194                 dev->setkeycode = input_default_setkeycode;
2195 
2196         error = device_add(&dev->dev);
2197         if (error)
2198                 goto err_free_vals;
2199 
2200         path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
2201         pr_info("%s as %s\n",
2202                 dev->name ? dev->name : "Unspecified device",
2203                 path ? path : "N/A");
2204         kfree(path);
2205 
2206         error = mutex_lock_interruptible(&input_mutex);
2207         if (error)
2208                 goto err_device_del;
2209 
2210         list_add_tail(&dev->node, &input_dev_list);
2211 
2212         list_for_each_entry(handler, &input_handler_list, node)
2213                 input_attach_handler(dev, handler);
2214 
2215         input_wakeup_procfs_readers();
2216 
2217         mutex_unlock(&input_mutex);
2218 
2219         if (dev->devres_managed) {
2220                 dev_dbg(dev->dev.parent, "%s: registering %s with devres.\n",
2221                         __func__, dev_name(&dev->dev));
2222                 devres_add(dev->dev.parent, devres);
2223         }
2224         return 0;
2225 
2226 err_device_del:
2227         device_del(&dev->dev);
2228 err_free_vals:
2229         kfree(dev->vals);
2230         dev->vals = NULL;
2231 err_devres_free:
2232         devres_free(devres);
2233         return error;
2234 }

3.4 input_unregister_device

函数原型

void input_unregister_device(struct input_dev *dev)

参数

struct input_dev *dev

input_dev结构体指针

返回值

功能

向 Linux 内核注销input_dev

2244 void input_unregister_device(struct input_dev *dev)
2245 {
2246         if (dev->devres_managed) {
2247                 WARN_ON(devres_destroy(dev->dev.parent,
2248                                         devm_input_device_unregister,
2249                                         devm_input_device_match,
2250                                         dev));
2251                 __input_unregister_device(dev);
2252                 /*
2253                  * We do not do input_put_device() here because it will be done
2254                  * when 2nd devres fires up.
2255                  */
2256         } else {
2257                 __input_unregister_device(dev);
2258                 input_put_device(dev);
2259         }
2260 }

3.5 input_event

函数原型

void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)

参数

struct input_dev *dev

需要上报的 input_dev

unsigned int type

上报的事件类型,比如 EV_KEY

unsigned int code

事件码,也就是我们注册的按键值,比如 KEY_0、KEY_1

int value

事件值,比如 1 表示按键按下,0 表示按键松开

返回值

功能

上报指定的事件以及对应的值

436 void input_event(struct input_dev *dev,437                  unsigned int type, unsigned int code, int value)438 {439         unsigned long flags;440 441         if (is_event_supported(type, dev->evbit, EV_MAX)) {442 443                 spin_lock_irqsave(&dev->event_lock, flags);444                 input_handle_event(dev, type, code, value);445                 spin_unlock_irqrestore(&dev->event_lock, flags);446         }447 }

3.6 input_sync

函数原型

static inline void input_sync(struct input_dev *dev)

参数

struct input_dev *dev

需要上报的 input_dev

返回值

功能

告诉 Linux 内核 input 子系统上报结束,input_sync 函数本质是上报一个同步事件

430 static inline void input_sync(struct input_dev *dev)
431 {        
432         input_event(dev, EV_SYN, SYN_REPORT, 0);
433 }    

3.7 input_set_capability

函数原型

void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code)

参数

struct input_dev *dev

输入设备结构体指针

unsigned int type

输入事件的类型

unsigned int code

事件的特定代码

返回值

功能

向一个已注册的输入设备添加特定的输入能力

1964 void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code)
1965 {
1966         switch (type) {
1967         case EV_KEY:
1968                 __set_bit(code, dev->keybit);
1969                 break;
1970 
1971         case EV_REL:
1972                 __set_bit(code, dev->relbit);
1973                 break;
1974 
1975         case EV_ABS:
1976                 input_alloc_absinfo(dev);
1977                 if (!dev->absinfo)
1978                         return;
1979 
1980                 __set_bit(code, dev->absbit);
1981                 break;
1982 
1983         case EV_MSC:
1984                 __set_bit(code, dev->mscbit);
1985                 break;
1986 
1987         case EV_SW:
1988                 __set_bit(code, dev->swbit);
1989                 break;
1990 
1991         case EV_LED:
1992                 __set_bit(code, dev->ledbit);
1993                 break;
1994 
1995         case EV_SND:
1996                 __set_bit(code, dev->sndbit);
1997                 break;
1998 
1999         case EV_FF:
2000                 __set_bit(code, dev->ffbit);
2001                 break;
2002 
2003         case EV_PWR:
2004                 /* do nothing */
2005                 break;
2006 
2007         default:
2008                 pr_err("%s: unknown type %u (code %u)\n", __func__, type, code);
2009                 dump_stack();
2010                 return;
2011         }
2012 
2013         __set_bit(type, dev->evbit);
2014 }

4 input.c分析

4.1 注册字符设备

        input 核心层会向 Linux 内核注册一个字符设备,class_register函数注册一个 input 类,这样系统启动以后就会在/sys/class 目录下有一个 input 子目录

zwzn2064@zwzn2064-CVN-Z690D5-GAMING-PRO:~$ ls /sys/class
ata_device   devfreq-event  i2c-dev   nvme            printer       scsi_generic  vc
ata_link     dma            input     nvme-subsystem  ptp           scsi_host     vfio
ata_port     dmi            iommu     pci_bus         pwm           sound         virtio-ports
backlight    extcon         leds      pci_epc         rapidio_port  spi_master    vtconsole
bdi          firmware       mdio_bus  phy             regulator     spi_slave     wakeup
block        gpio           mem       powercap        remoteproc    thermal       watchdog
bsg          graphics       misc      power_supply    rfkill        tpm           wmi_bus
dax          hidraw         mmc_host  ppdev           rtc           tpmrm
devcoredump  hwmon          nd        ppp             scsi_device   tty
devfreq      i2c-adapter    net       pps             scsi_disk     usbmisc
zwzn2064@zwzn2064-CVN-Z690D5-GAMING-PRO:~$ ls /sys/class/input/
event0   event11  event3  event6  event9  input10  input2  input5  input8
event1   event12  event4  event7  input0  input11  input3  input6  input9
event10  event2   event5  event8  input1  input12  input4  input7  mice
zwzn2064@zwzn2064-CVN-Z690D5-GAMING-PRO:~$ 

代码如下所示

#define INPUT_MAJOR             13
36 #define INPUT_MAX_CHAR_DEVICES          10241777 static char *input_devnode(struct device *dev, umode_t *mode)
1778 {
1779         return kasprintf(GFP_KERNEL, "input/%s", dev_name(dev));
1780 }1782 struct class input_class = {
1783         .name           = "input",
1784         .devnode        = input_devnode,
1785 };2476 static int __init input_init(void)
2477 {
2478         int err;
2479 
2480         err = class_register(&input_class);
2481         if (err) {
2482                 pr_err("unable to register input_dev class\n");
2483                 return err;
2484         }
2485 
2486         err = input_proc_init();
2487         if (err)
2488                 goto fail1;
2489 
2490         err = register_chrdev_region(MKDEV(INPUT_MAJOR, 0),
2491                                      INPUT_MAX_CHAR_DEVICES, "input");
2492         if (err) {
2493                 pr_err("unable to register char major %d", INPUT_MAJOR);
2494                 goto fail2;
2495         }
2496 
2497         return 0;
2498 
2499  fail2: input_proc_exit();
2500  fail1: class_unregister(&input_class);
2501         return err;
2502 }
2503 
2504 static void __exit input_exit(void)
2505 {
2506         input_proc_exit();
2507         unregister_chrdev_region(MKDEV(INPUT_MAJOR, 0),
2508                                  INPUT_MAX_CHAR_DEVICES);
2509         class_unregister(&input_class);
2510 }
2511 
2512 subsys_initcall(input_init);
2513 module_exit(input_exit);

4.2 函数分析

4.2.1 class_register

函数原型

#define class_register(class) \

({ \

static struct lock_class_key __key; \

__class_register(class, &__key); \

})

int __class_register(struct class *cls, struct lock_class_key *key)

参数

struct class *cls

指向 class 结构体的指针,表示要注册的设备类

struct lock_class_key *key

指向 lock_class_key 结构体的指针,用于锁的调试和分类(通常用于锁的层次化管理)

返回值

int

成功:0 失败:错误码

功能

注册设备类

146 int __class_register(struct class *cls, struct lock_class_key *key)
147 {
148         struct subsys_private *cp;
149         int error;
150 
151         pr_debug("device class '%s': registering\n", cls->name);
152 
153         cp = kzalloc(sizeof(*cp), GFP_KERNEL);
154         if (!cp)
155                 return -ENOMEM;
156         klist_init(&cp->klist_devices, klist_class_dev_get, klist_class_dev_put);
157         INIT_LIST_HEAD(&cp->interfaces);
158         kset_init(&cp->glue_dirs);
159         __mutex_init(&cp->mutex, "subsys mutex", key);
160         error = kobject_set_name(&cp->subsys.kobj, "%s", cls->name);
161         if (error) {
162                 kfree(cp);
163                 return error;
164         }
165 
166         /* set the default /sys/dev directory for devices of this class */
167         if (!cls->dev_kobj)
168                 cls->dev_kobj = sysfs_dev_char_kobj;
169 
170 #if defined(CONFIG_BLOCK)
171         /* let the block class directory show up in the root of sysfs */
172         if (!sysfs_deprecated || cls != &block_class)
173                 cp->subsys.kobj.kset = class_kset;
174 #else    
175         cp->subsys.kobj.kset = class_kset;
176 #endif   
177         cp->subsys.kobj.ktype = &class_ktype;
178         cp->class = cls;
179         cls->p = cp;
180 
181         error = kset_register(&cp->subsys);
182         if (error) {
183                 kfree(cp);
184                 return error;
185         }                                 
186         error = class_add_groups(class_get(cls), cls->class_groups);
187         class_put(cls);
188         return error;
189 }

4.2.2 dev_name

函数原型

static inline const char *dev_name(const struct device *dev)

参数

const struct device *dev

指向 struct device 结构体的指针,该结构体表示一个设备实例

返回值

const char *

函数返回一个指向 const char 的指针,这个指针指向设备名称的字符串

功能

获取设备名称

1132 static inline const char *dev_name(const struct device *dev)
1133 {
1134         /* Use the init name until the kobject becomes available */
1135         if (dev->init_name)
1136                 return dev->init_name;
1137 
1138         return kobject_name(&dev->kobj);
1139 }

4.2.3 register_chrdev_region

函数原型

int register_chrdev_region(dev_t from, unsigned count, const char *name)

参数

dev_t from

dev_t from: 起始设备号,指定从哪个设备号开始分配。

unsigned count

unsigned count: 设备号的数量,指定需要多少个连续的设备号

const char *name

const char *name: 设备的名称,通常用于在系统中标识这个设备

返回值

int

成功:字符设备分配设备号 失败:负值

功能

注册字符设备号

209 int register_chrdev_region(dev_t from, unsigned count, const char *name)
210 {
211         struct char_device_struct *cd;
212         dev_t to = from + count;
213         dev_t n, next;
214                 
215         for (n = from; n < to; n = next) {
216                 next = MKDEV(MAJOR(n)+1, 0);
217                 if (next > to)
218                         next = to;
219                 cd = __register_chrdev_region(MAJOR(n), MINOR(n),
220                                next - n, name);
221                 if (IS_ERR(cd))
222                         goto fail;
223         }
224         return 0;
225 fail:
226         to = n;
227         for (n = from; n < to; n = next) {
228                 next = MKDEV(MAJOR(n)+1, 0);
229                 kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));
230         }
231         return PTR_ERR(cd);       
232 }

4.2.3 MKDEV

函数原型

#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))

#define MINORBITS 20

参数

ma

主设备号

mi

此设备号

返回值

功能

将主设备号和次设备号组合成一个 dev_t 类型的设备号

5 示例

5.1 示例 1

5.1.1 代码

#include <linux/init.h> 
#include <linux/module.h> 
#include <linux/fs.h>
#include <linux/input.h>struct input_dev *input_dev_s;static int test_init(void){int ret;input_dev_s = input_allocate_device();input_dev_s->name = "input_test";input_dev_s->id.bustype = BUS_HOST;input_dev_s->id.vendor = 0x0001;input_dev_s->id.product = 0x0001;input_dev_s->id.version = 0x0100;ret = input_register_device(input_dev_s);if(ret){printk(KERN_ERR "register input device error\n");input_free_device(input_dev_s);goto failed;}printk("register input device ok\r\n");		return 0;
failed:return ret;}static void test_exit(void){printk("unregister input device\r\n");input_unregister_device(input_dev_s);}module_init(test_init);
module_exit(test_exit);
MODULE_LICENSE("GPL");

5.1.2 操作

        注册成功后,/dev/input/下多了一个event4 文件节点

console:/data # ls /dev/input/                                                 
event0  event1  event2  event3
console:/data # insmod input_test.ko                                           
[  261.075255] input: input_test as /devices/virtual/input/input5
[  261.077768] register input device ok
console:/data # ls /dev/input/                                                 
event0  event1  event2  event3  event4

        使用 cat /proc/bus/input/devices 命令查看输入设备信息,可以看到 input_test 的输入设备信息。

130|console:/data #  cat /proc/bus/input/devices
I: Bus=0019 Vendor=524b Product=0006 Version=0100
N: Name="ff420030.pwm"
P: Phys=gpio-keys/remotectl
S: Sysfs=/devices/platform/ff420030.pwm/input/input0
U: Uniq=
H: Handlers=event0 cpufreq dmcfreq 
B: PROP=0
B: EV=3
B: KEY=70010 20000000000000 0 100010002000000 78000004000a800 1e16c000000000 10004ffcI: Bus=0019 Vendor=0001 Product=0001 Version=0100
N: Name="adc_keys"
P: Phys=adc-keys/input0
S: Sysfs=/devices/platform/adc_keys/input/input1
U: Uniq=
H: Handlers=event1 cpufreq dmcfreq 
B: PROP=0
B: EV=3
B: KEY=c000000000000 0I: Bus=0019 Vendor=0001 Product=0001 Version=0100
N: Name="gpio-keys"
P: Phys=gpio-keys/input0
S: Sysfs=/devices/platform/gpio-keys/input/input2
U: Uniq=
H: Handlers=event2 cpufreq dmcfreq 
B: PROP=0
B: EV=100003
B: KEY=10000000000000 0I: Bus=0000 Vendor=0001 Product=0001 Version=0100
N: Name="rk-headset"
P: Phys=
S: Sysfs=/devices/platform/rk-headset/input/input3
U: Uniq=
H: Handlers=event3 
B: PROP=0
B: EV=3
B: KEY=400000000 0 0 0I: Bus=0019 Vendor=0001 Product=0001 Version=0100
N: Name="input_test"
P: Phys=
S: Sysfs=/devices/virtual/input/input5
U: Uniq=
H: Handlers=event4 
B: PROP=0
B: EV=1console:/data # 

5.2 mcu_cec驱动分析

5.2.1 注册输入事件

657 static int  mcu_cec_probe(struct i2c_client *client, const struct i2c_device_id *id)
658 {
659         int ret = 0;
660         int i;
661         struct mcu_cec *mcu_cec;
662         struct input_dev *input;
663         struct device_node *np = client->dev.of_node;
664         printk("%s: probe\n", __FUNCTION__);
665 
666 
667         mcu_cec = devm_kzalloc(&client->dev, sizeof(struct mcu_cec), GFP_KERNEL);
668         if (!mcu_cec)
669                 return -ENOMEM;
705         mcu_cec->mcu_cec_wq = create_singlethread_workqueue("mcu_cec_wq");
706         if (!mcu_cec->mcu_cec_wq){
707                 printk(KERN_ERR"%s: create workqueue failed\n", __func__);
708                 ret = -ENOMEM;
709                 goto failed;
710         }
711 
728         input = devm_input_allocate_device(&client->dev);
729         if (!input) {
730                 ret = -ENOMEM;
731                 goto failed;
732         }
733 
736         input->name = client->name;
737         input->phys = "cec-keys/input0";
738         input->dev.parent = &client->dev;
739 
740         input->id.bustype = BUS_HOST;
741         input->id.vendor = 0x0001;
742         input->id.product = 0x0001;
743         input->id.version = 0x0100;
744 
745         mcu_cec->input_dev = input;
746 
747         for (i=0;i<sizeof(mcu_cec_input_key)/sizeof(struct mcu_cec_key_table);i++){
748                         unsigned int type = EV_KEY;
749 
750                 input_set_capability(input, type, mcu_cec_input_key[i].keyCode);
751         }
752         input_set_capability(input, EV_KEY, KEY_WAKEUP);
753         input_set_capability(input, EV_KEY, KEY_F12);
754         ret = input_register_device(input);
755         if (ret) {
756                 input_free_device(input);
757                 goto failed;
758         }
760         mcu_cec->nb.notifier_call = cec_hotplug_notifier_call;
761         ret = cec_hotplug_reg_notifier(&mcu_cec->nb);
762         if (ret) {
763                 printk("failed to reg notifier: %d\n", ret);
764         }
765 
766         mutex_init(&mcu_cec->m_lock);
767         wake_lock_init(&mcu_cec->w_lock, WAKE_LOCK_SUSPEND, "mcu_lock");
768         wake_lock(&mcu_cec->w_lock);
769         printk("%s: probe ok!!\n", __FUNCTION__);
770     return 0;
771 failed:
772         return ret;
773 }

5.2.2 函数分析

5.2.2.1 devm_input_allocate_device

函数原型

struct input_dev *devm_input_allocate_device(struct device *dev)

参数

struct device *dev

设备结构体指针

返回值

struct input_dev *

输入设备结构体指针

功能

分配并初始化一个输入设备

1862 struct input_dev *devm_input_allocate_device(struct device *dev)
1863 {
1864         struct input_dev *input;
1865         struct input_devres *devres;
1866 
1867         devres = devres_alloc(devm_input_device_release,
1868                               sizeof(*devres), GFP_KERNEL);
1869         if (!devres)
1870                 return NULL;
1871 
1872         input = input_allocate_device();
1873         if (!input) {
1874                 devres_free(devres);
1875                 return NULL;
1876         }
1877 
1878         input->dev.parent = dev;
1879         input->devres_managed = true; 
1880 
1881         devres->input = input;
1882         devres_add(dev, devres);
1883 
1884         return input;
1885 }
1834 static void devm_input_device_release(struct device *dev, void *res)
1835 {
1836         struct input_devres *devres = res;
1837         struct input_dev *input = devres->input;
1838 
1839         dev_dbg(dev, "%s: dropping reference to %s\n",
1840                 __func__, dev_name(&input->dev));
1841         input_put_device(input);
1842 }

devm_input_allocate_device 函数提供了一个方便的方式来分配和初始化 input_dev 设备,并利用设备管理机制自动处理资源释放。这使得设备的创建和销毁变得更加简洁和安全。

devres 机制: devres(设备资源)用于管理与设备相关的资源。devres_add 将资源添加到设备的资源管理列表中,devm_input_device_release 函数会在设备释放时调用,以释放相关资源。

自动资源管理: 通过 devres 机制,确保在设备卸载时,input_dev 设备会被正确释放,避免内存泄漏和资源浪费。

父设备设置: 将 input_dev 的父设备设置为传入的设备 dev,确保设备之间的关系正确设置,有利于设备树的管理和资源的分配。

5.2.3 上报事件

90 static struct mcu_cec_key_table mcu_cec_input_key[] = {91     {0x40, KEY_POWER},     //power off 92     {0x30, KEY_POWER},     //power off 93     {0x01, KEY_UP},94     {0x02, KEY_DOWN},95     {0x03, KEY_LEFT},96     {0x04, KEY_RIGHT},  97     {0x41, KEY_VOLUMEUP},98     {0x42, KEY_VOLUMEDOWN},99     {0x43, KEY_BACK},       //mute
100     {0x2b, KEY_ENTER},
101     {0x20, KEY_0},
102     {0x21, KEY_1},
103     {0x22, KEY_2},
104     {0x23, KEY_3},
105     {0x24, KEY_4},
106     {0x25, KEY_5},
107     {0x26, KEY_6},
108     {0x27, KEY_7},
109     {0x28, KEY_8},
110     {0x29, KEY_9},
111     {0x2a, KEY_DOT},
112     {0xd, KEY_BACK},
113     {0x48, KEY_REWIND},
114     {0x46, KEY_PLAYPAUSE},
115     {0x49, KEY_FASTFORWARD},
116     {0x44, KEY_PLAYPAUSE},
117     {0x45, KEY_STOP},
118     {0x71, KEY_MENU},//KEY_BLUE
119     {0x74, KEY_HOME},//KEY_YELLOW KEY_HOME
120     {0xa, KEY_MENU},//KEY_BLUE
121     {0x09, KEY_HOME},//KEY_YELLOW KEY_HOME
122     {0x4b, KEY_NEXTSONG},
123     {0x4c, KEY_PREVIOUSSONG},
124 };127 static unsigned char cec_get_keycode(unsigned char cecCode)
128 {
129     int i;
130 
131     for (i = 0; i < sizeof(mcu_cec_input_key)/sizeof(struct mcu_cec_key_table); i++){
132         if (mcu_cec_input_key[i].cecCode == cecCode){
133             return mcu_cec_input_key[i].keyCode;
134         }
135     }
136         return 0;
137 }426 static void mcu_cec_report_standby_task(struct work_struct *work)
427 {
428     int ret;
429     unsigned char ceckey;
430     unsigned char keyCode;
431     cec_int_status mcu_value;
432     struct mcu_cec *mcu_cec = container_of(work, struct mcu_cec, work);
433     mutex_lock(&mcu_cec->m_lock);
434     ret = regmap_read(mcu_cec->regmap, CEC_INT_STATUS, (int *)&mcu_value.status);
435     if (ret) {
436           dev_err(&mcu_cec->i2c->dev, "read 0x%x failed\n", CEC_INT_STATUS);
437           goto failed;
438     }
439     if(mcu_value.status_bit.req_standby == 1){
440          mcu_value.status_bit.req_standby = 0;
441          ret = regmap_write(mcu_cec->regmap, CEC_INT_STATUS, (unsigned int)mcu_value.status);
442          if (ret) {
443                 dev_err(&mcu_cec->i2c->dev, "write 0x%x failed\n", CEC_INT_STATUS);
444          }
445          input_event(mcu_cec->input_dev, EV_KEY, KEY_F12, 1);
446          input_sync(mcu_cec->input_dev);
447          input_event(mcu_cec->input_dev, EV_KEY, KEY_F12, 0);//KEY_WAKEUP
448          input_sync(mcu_cec->input_dev);
449          pr_err("report host stanby\n");
450     }else if(mcu_value.status_bit.remote_control == 1){
451          mcu_value.status_bit.remote_control = 0;
452          ret = regmap_write(mcu_cec->regmap, CEC_INT_STATUS, (unsigned int)mcu_value.status);
453          if (ret) {
454                 dev_err(&mcu_cec->i2c->dev, "write 0x%x failed\n", CEC_INT_STATUS);
455          }
456 
457         ret = regmap_read(mcu_cec->regmap, CEC_REMOTE_KEY, (unsigned int *)&ceckey);
458         if (ret) {
459           dev_err(&mcu_cec->i2c->dev, "read 0x%x failed\n", CEC_INT_STATUS);
460           goto failed;
461         }
462 
463         keyCode = cec_get_keycode(ceckey);
464 
465         {
466                 mcu_cec->keycode = keyCode;
467                 if (mcu_cec->keycode == KEY_POWER)
468                 {
469                         if(mcu_cec->input_dev){
470                                 input_event(mcu_cec->input_dev, EV_KEY, KEY_POWER, 1);
471                                 input_sync(mcu_cec->input_dev);
472                                 input_event(mcu_cec->input_dev, EV_KEY, KEY_POWER, 0);//KEY_WAKEUP
473                                 input_sync(mcu_cec->input_dev);
474                         }
475                 }else {
476                         input_event(mcu_cec->input_dev, EV_KEY, mcu_cec->keycode, 1);
477                         input_sync(mcu_cec->input_dev);
478                         input_event(mcu_cec->input_dev, EV_KEY, mcu_cec->keycode, 0);
479                         input_sync(mcu_cec->input_dev);
480                 }
481         }
482 
483 
484         // report_cec_input_keycode(mcu_cec);
485     }
486     enable_irq(mcu_cec->i2c->irq);
487 failed:
488     mutex_unlock(&mcu_cec->m_lock);
489 
490 }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/881045.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

排序算法剖析

文章目录 排序算法浅谈参考资料评价指标可视化工具概览 插入排序折半插入排序希尔排序冒泡排序快速排序简单选择排序堆排序归并排序基数排序 排序算法浅谈 参考资料 数据结构与算法 评价指标 稳定性&#xff1a;两个相同的关键字排序过后相对位置不发生变化时间复杂度空间复…

C# Blazor Server 调用海康H5Player播放摄像头画面

目标 调用海康综合安防平台api&#xff0c;通过摄像头的cameraIndexCode调用【获取监控点预览取流URLv2】api&#xff0c;得到websocket 的url&#xff0c;然后在blazor server中使用htplayer.js播放摄像头实时画面。 步骤 根据摄像头名字&#xff0c;调用【查询监控点列表v2…

python配置环境变量

方法一&#xff1a;首先卸载重新安装&#xff0c;在安装时勾选增加环境变量 方法二&#xff1a;我的电脑-属性-高级系统配置 手动添加环境变量&#xff0c;路径为python的安装路径 检查&#xff1a;查看环境变量是否安装成功 安装第三方lib winr&#xff0c;输入cmd pip ins…

线程互斥函数的例子

代码 #include<stdio.h> #include<pthread.h> #include<sched.h> void *producter_f(void *arg); void *consumer_f(void *arg); int buffer_has_item0; pthread_mutex_t mutex; int running1; int main(void) {pthread_t consumer_t;pthread_t producter_t…

Linux学习笔记(六):服务管理,监控,RPM包管理,yum包管理工具,Linux启动管理,网络管理

Linux学习笔记&#xff08;六&#xff09;&#xff1a;服务管理&#xff0c;监控&#xff0c;RPM包管理&#xff0c;yum包管理工具&#xff0c;Linux启动管理&#xff0c;网络管理 1. 服务管理 1.1 service 启动/停止服务 service 命令是最常用的服务管理工具之一&#xff0c…

音视频入门基础:FLV专题(7)——Tag header简介

一、引言 从《音视频入门基础&#xff1a;FLV专题&#xff08;3&#xff09;——FLV header简介》中可以知道&#xff0c; 在FLV header之后&#xff0c;FLV文件剩下的部分应由PreviousTagSize和Tag组成。FLV文件 FLV header PreviousTagSize0 Tag1 PreviousTagSize1 Ta…

Python或R时偏移算法实现

&#x1f3af;要点 计算单变量或多变量时序距离&#xff0c;使用欧几里得、曼哈顿等函数量化不同时序差异。量化生成时序之间接近度相似性矩阵。使用高尔距离和堪培拉距离等相似度测量。实现最小方差匹配算法&#xff0c;绘制步进模式的图形表示。其他语言包算法实现。 &…

【AI知识点】NP 难问题(NP-Hard Problem)

NP 难问题&#xff08;NP-Hard Problem&#xff09; 是计算复杂性理论中的一个重要概念&#xff0c;描述了那些非常难以求解的问题。NP 难问题中的“NP”代表“非确定性多项式时间”&#xff08;Nondeterministic Polynomial time&#xff09;。这些问题的特性使得求解它们的最…

[uni-app]小兔鲜-07订单+支付

订单模块 基本信息渲染 import type { OrderState } from /services/constants import type { AddressItem } from ./address import type { PageParams } from /types/global/** 获取预付订单 返回信息 */ export type OrderPreResult {/** 商品集合 [ 商品信息 ] */goods: …

[数据集][目标检测]辣椒缺陷检测数据集VOC+YOLO格式695张5类别

重要说明&#xff1a;数据集图片里面都是一个辣椒&#xff0c;请仔细查看图片预览&#xff0c;确认符合要求下载 数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文…

jenkins 构建报错ERROR: Error fetching remote repo ‘origin‘

问题描述 修改项目的仓库地址后&#xff0c;使用jenkins构建报错 Running as SYSTEM Building in workspace /var/jenkins_home/workspace/【测试】客户端/client-fonchain-main The recommended git tool is: NONE using credential 680a5841-cfa5-4d8a-bb38-977f796c26dd&g…

小白快速上手 Docker 03 | Docker数据卷

数据卷 在前面使用Docker时&#xff0c;可能会遇到以下几个问题&#xff1a; 当Docker 里的容器挂了以后打不开&#xff0c;这时候只有删除该容器了&#xff0c;但删除容器会连容器中的产生的数据也一起删除了&#xff0c;大部分场景下这是不能接受的。Docker容器与容器之间不…

【图论】1 (最小生成树虚拟点思想)C.戴森球计划 题解

一. 题目 题目描述 输入输出格式 样例 样例1 样例2 & 样例解释 数据范围 二. 思路 对于前20%数据 解法 因为保证了 x i 1 x_i 1 xi​1&#xff0c;也就是说这些点都在 x 1 x 1 x1 这条直线上。 那么最优解必定是在 c i c_i ci​ 最小的点上建发电站&#xff0c…

4.人员管理模块(开始预备工作)——帝可得管理系统

目录 前言一、需求分析1.页面原型2.创建SQL 二、使用若依框架生成前后端代码1.添加目录菜单2.添加数据字典3.配置代码生成信息4.下载代码并导入项目5.快速导入方法 三、 总结 前言 提示&#xff1a;本篇讲解人员管理模块的开发的预备工作&#xff0c;包括需求分析、生成代码、…

uniapp+Android面向网络学习的时间管理工具软件 微信小程序

目录 项目介绍支持以下技术栈&#xff1a;具体实现截图HBuilderXuniappmysql数据库与主流编程语言java类核心代码部分展示登录的业务流程的顺序是&#xff1a;数据库设计性能分析操作可行性技术可行性系统安全性数据完整性软件测试详细视频演示源码获取方式 项目介绍 用户功能…

最新版本SkyWalking【10.1.0】部署

这里写目录标题 前言前置条件启动Skywalking下载解压启动说明 集成Skywalking Agent下载Agent在IDEA中添加agent启动应用并访问SpringBoot接口 说明 前言 基于当前最新版10.1.0搭建skywalking 前置条件 装有JDK11版本的环境了解SpringBoot相关知识 启动Skywalking 下载 地…

golang grpc进阶

protobuf 官方文档 基本数据类型 .proto TypeNotesGo Typedoublefloat64floatfloat32int32使用变长编码&#xff0c;对于负值的效率很低&#xff0c;如果你的域有可能有负值&#xff0c;请使用sint64替代int32uint32使用变长编码uint32uint64使用变长编码uint64sint32使用变长…

Linux:无法为立即文档创建临时文件: 设备上没有空间

虚拟机磁盘空间不足解决记录 1、问题描述2、问题解决 1、问题描述 在命令行输入命令按Tab键时出现如下报错&#xff1a; 很明显&#xff0c;设备上没有空间&#xff0c;即磁盘空间不足。通过命令查看具体情况如下&#xff1a; df -h2、问题解决 首先想到的是虚拟机扩容。关机虚…

每日学习一个数据结构-树

文章目录 树的相关概念一、树的定义二、树的基本术语三、树的分类四、特殊类型的树五、树的遍历六、树的应用场景 树的遍历一、前序遍历二、中序遍历三、后序遍历使用java代码实现遍历总结 树的相关概念 树是一种重要的非线性数据结构&#xff0c;在计算机科学中有着广泛的应用…

C++IO流

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 CIO流 收录于专栏 [C进阶学习] 本专栏旨在分享学习C的一点学习笔记&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 1. C语言的输入与输出 2. 流是什…