当前位置: 首页 > news >正文

[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 }

相关文章:

[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_se…...

2023_Spark_实验十:Centos_Spark Local模式部署

参考这篇博客&#xff1a;【Centos8_配置单节点伪分布式Spark环境】_centos8伪分布式环境搭建-CSDN博客...

pyecharts-快速入门

pyecharts文档&#xff1a;渲染图表 - pyecharts - A Python Echarts Plotting Library built with love. pyecharts-gallery文档&#xff1a;中文简介 - Document (pyecharts.org) 一、快速入门案例 from pyecharts.charts import Barbar Bar() bar.add_xaxis(["衬衫…...

vue3打包疯狂报错

打包的时候报错很多Cannot find name ‘xxx‘ 。 但是npm run dev 是运行正常的。 解决方法&#xff1a;package.json中的vue-tsc --noEmit 删掉就可以了。 例如&#xff1a; 这是原来的 {"scripts": {"dev": "vite","build": &quo…...

STM32 软件触发ADC采集

0.91寸OLED屏幕大小的音频频谱&#xff0c;炫酷&#xff01; STM32另一个很少人知道的的功能——时钟监测 晶振与软件的关系&#xff08;深度理解&#xff09; STM32单片机一种另类的IO初始化方法 ADC是一个十分重要的功能&#xff0c;几乎任何一款单片机都会包含这个功能&a…...

Android SystemUI组件(08)睡眠灭屏 锁屏处理流程

该系列文章总纲链接&#xff1a;专题分纲目录 Android SystemUI组件 本章关键点总结 & 说明&#xff1a; 说明&#xff1a;本章节持续迭代之前章节的思维导图&#xff0c;主要关注左侧上方锁屏分析部分 睡眠灭屏 即可。 Power按键的处理逻辑最终是由PhoneWindowManager来完…...

C# 表达式与运算符

本课要点&#xff1a; 1、表达式的基本概念 2、常用的几种运算符 3、运算符的优先级 4、常见问题 一 表达式 表达式是由运算符和操作数组成的。、-、*和/等都是运算符&#xff0c;操作数包括文本、常量、变量和表达式等。 二 算术运算符 2.1 算术运算符的使用 三 常见错误 …...

SpringBoot--最大连接数和最大并发数

原文网址&#xff1a;SpringBoot--最大连接数和最大并发数-CSDN博客 简介 本文介绍SpringBoot的最大连接数和最大并发数。 配置 SpringBoot默认使用tomcat处理请求。tomcat可以指定连接数、线程数等配置。 server:tomcat:# 请求处理线程都在使用中时&#xff0c;新连接请求…...

CF687D Dividing Kingdom II 题解

Description 给定一个 n n n 个点、 m m m 条边的图&#xff0c;有 q q q 次询问&#xff0c;每次询问一个 [ l , r ] [l,r] [l,r] 的区间&#xff0c;求将 n n n 个点分为两个部分后&#xff0c;编号在 [ l , r ] [l,r] [l,r] 内的边中&#xff0c;两端点属于同一部分的…...

高空抛物AI检测算法:精准防控,技术革新守护城市安全

近年来&#xff0c;随着城市化进程的加速&#xff0c;高楼大厦如雨后春笋般涌现&#xff0c;但随之而来的高空抛物问题却成为城市管理的一大难题。高空抛物不仅严重威胁行人的安全&#xff0c;还可能引发法律纠纷和社会问题。为了有效预防和减少高空抛物事件的发生&#xff0c;…...

html+css+js实现Collapse 折叠面板

实现效果&#xff1a; HTML部分 <div class"collapse"><ul><li><div class"header"><h4>一致性 Consistency</h4><span class"iconfont icon-jiantou"></span></div><div class"…...

RM服务器研究(一)

客户端默认端口是10100&#xff1a; MultiPort.dll BOOL sub_10001070() { UINT v0; // esi BOOL result; // eax CHAR KeyName; // [espCh] [ebp-10Ch] DWORD flOldProtect; // [esp10h] [ebp-108h] CHAR Buffer; // [esp14h] [ebp-104h] char v5; // [esp15h] [e…...

云岚到家xxl job 配置

调度中心&#xff1a; 负责管理调度信息&#xff0c;按照调度配置发出调度请求&#xff0c;自身不承担业务代码&#xff1b; 主要职责为执行器管理、任务管理、监控运维、日志管理等 任务执行器&#xff1a; 负责接收调度请求并执行任务逻辑&#xff1b; 主要职责是执行任…...

国内动态短效sk5

HTTP爬虫代理,软件测试&#xff0c; 动态转发IP方案&#xff0c;全高匿名&#xff0c;私密IP&#xff0c;固定网关将您每次请求的HTTP重定向到不同的后端IP&#xff0c;支持API;指路小熊IP https://www.xiaoxiongip.com?fromqkJWgD可测...

【路径规划】路径平滑算法,A星算法拐点的圆弧化处理

摘要 A算法广泛应用于路径规划中&#xff0c;但其生成的路径通常在拐点处呈现不平滑的折线。为了提升路径的平滑性&#xff0c;本文提出了一种基于圆弧的平滑处理方法&#xff0c;用于对A算法产生的路径拐点进行优化。通过在MATLAB中进行仿真验证&#xff0c;该方法能够有效减…...

【寻找one piece的算法之路】——双指针算法!他与她是否会相遇呢?

&#x1f490;个人主页&#xff1a;初晴~ &#x1f4da;相关专栏&#xff1a;寻找one piece的刷题之路 什么是双指针算法 双指针算法是一种常用的编程技巧&#xff0c;尤其在处理数组和字符串问题时非常有效。这种方法的核心思想是使用两个指针来遍历数据结构&#xff0c;这两…...

UFS 3.1架构简介

整个UFS协议栈可以分为三层:应用层(UFS Application Layer(UAP)),传输层(UFS Transport Layer(UTP)),链路层(UIC InterConnect Layer(UIC))。应用层发出SCSI命令(UFS没有自己的命令使用的是简化的SCSI命令),在传输层将SCSI分装为UPIU,再经过链路层将命令发送给Devices。下…...

注册安全分析报告:科研诚信查询平台无验证方式导致安全隐患

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 1. 暴力破解密码&#xff0c;造成用户信息泄露 2. 短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉 3. 带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造…...

04.useTitle

在 React 应用中,动态更新页面标题是提升用户体验的一个重要方面。它可以让用户更清楚地知道当前页面的内容或状态,特别是在单页应用(SPA)中。useTitle 钩子提供了一种简单而有效的方式来管理文档标题。以下是如何实现和使用这个自定义钩子: const useTitle = title =>…...

ROS2中的srv、action、发布订阅三种方式

ROS2中的srv、action、发布订阅三种方式 以下是ROS2中srv、action、发布订阅三种方式的差异和使用场景的表格形式呈现&#xff1a; 特性/方式srv&#xff08;服务&#xff09;action&#xff08;动作&#xff09;发布订阅&#xff08;Publish-Subscribe&#xff09;通信模式请…...

HarmonyOS/OpenHarmony 自定义弹窗页面级层级控制解决方案

关键词&#xff1a;CuntomDialog自定义弹窗、SubWindow子窗口、页面级、弹窗层级控制、鸿蒙、弹窗展示层级异常 问题存在API版本&#xff1a;API10 - API12&#xff08;该问题已反馈&#xff0c;期望后续官方能增加页面级控制能力&#xff09; 在正常的鸿蒙app开发过程中&…...

C/C++进阶(一)--内存管理

更多精彩内容..... &#x1f389;❤️播主の主页✨&#x1f618; Stark、-CSDN博客 本文所在专栏&#xff1a; 学习专栏C语言_Stark、的博客-CSDN博客 其它专栏&#xff1a; 数据结构与算法_Stark、的博客-CSDN博客 ​​​​​​项目实战C系列_Stark、的博客-CSDN博客 座右铭&a…...

docker-compose 快速部署clickhouse集群

在本教程中&#xff0c;我们将学习如何使用 Docker Compose 部署一个带有三节点的 ClickHouse 集群&#xff0c;并使用 ZooKeeper 作为分布式协调服务。 前提条件 注意事项&#xff1a; 镜像版本号注意保持一致 [zookeeper:3.7, clickhouse/clickhouse-server:22.5.4]config…...

闯关训练三:Git 基础知识

任务1: 破冰活动&#xff1a;自我介绍 点击Fork目标项目&#xff0c;创建一个新的Fork 获取仓库链接 在连接好开发机的vscode终端中逐行执行以下代码&#xff1a; git clone https://github.com/KelvinIII/Tutorial.git # 修改为自己frok的仓库 cd Tutorial/ git branch -a g…...

Java--IO基本流

IO流 概述 生活中&#xff0c;你肯定经历过这样的场景。当你编辑一个文本文件&#xff0c;忘记了ctrls &#xff0c;可能文件就白白编辑了。当你电脑上插入一个U盘&#xff0c;可以把一个视频&#xff0c;拷贝到你的电脑硬盘里。那么数据都是在哪些设备上的呢&#xff1f;键盘…...

结合大语言模型的机械臂抓取操作简单介绍

一、大语言模型与机械臂抓取的基本操作 1. 大语言模型简介 大语言模型是基于深度学习技术构建的自然语言处理模型&#xff0c;能够生成、理解和处理文本信息。这些模型通过训练大量的文本数据&#xff0c;学习语法、上下文和常识&#xff0c;能够执行多种任务&#xff0c;如文…...

Vivado - BD(差分时钟、简单分频、RESET、KEY)

目录 1. 简介 1.1 要点 1.2 buffer 介绍 2. vivado 工程 2.1 Block Design 2.2 IBUFDS 2.3 BUFGCE_DIV 2.4 Processor System Reset 2.5 key_mod 2.6 led_drv 3. 编译与调试 3.1 XDC 3.2 Debug 4. 总结 1. 简介 1.1 要点 了解 Utility Buffer v2.2 中的 Buffer…...

7--苍穹外卖-SpringBoot项目中套餐管理 详解(一)

前言 目录 新增套餐 需求分析和设计 代码开发 根据分类id查询菜品 Controller层 Service层 ServiceImpl层 Mapper层 DishMapper.xml 新增套餐 实体类 mapper层 Service层 ServiceImpl层 Mapper层 SetmealMapper.xml setmealDishMapper.xml 套餐分页查询 需求分…...

【尚硅谷】RocketMQ 消息队列学习笔记

RocketMQ 和 Kafka 消息队列概念比较&#xff1f; 好的&#xff01;RocketMQ 和 Kafka 都是分布式消息队列系统&#xff0c;它们的核心概念有很多相似之处&#xff0c;但在具体实现和命名上有所不同。下面我通过一个表格来对比 RocketMQ 和 Kafka 中的五个概念&#xff1a;消息…...

C题(三)芝麻开门 --- strcmp函数应用

场景一&#xff1a;“芝麻开门 ”是通往C语言的大门的暗号&#xff0c;现在你需要说对暗号&#xff0c;大门才会打开。 【分解目标1】字符串的输入 char arr[20] { 0 }; //字符的集合---字符串&#xff08;数组表示&#xff09;//20为预定的数组的大小scanf("%s", a…...

焦作做网站公司/整站优化代理

我们在进行网络编程的时候&#xff0c;经常需要获取本地主机&#xff0c;网卡的信息&#xff0c;我们代码实现如下。#include "../common/initsock.h"#include <windows.h> #include <stdio.h> #include <Iphlpapi.h>#include "protoutils.h&…...

个人网站备案icp/长春seo排名收费

最全的BAT大厂面试题整理 版权声明&#xff1a;本文为博主原创文章&#xff0c;未经博主允许不得转载。https://www.jianshu.com/p/c70989bd5f29 转载请标明出处&#xff1a; https://www.jianshu.com/p/c70989bd5f29 本文出自 AWeiLoveAndroid的博客 临近年关&#xff0c;又…...

网站做产品的审核工作内容/优化标题关键词技巧

提示&#xff1a;这是MyBatis的第六篇&#xff0c;有兴趣的话可以看下&#xff1a; 一&#xff1a; MyBatis复习笔记整理 二&#xff1a; MyBatis的resultMap标签 自定义封装返回值类型 三&#xff1a; MyBatis动态SQL官方文档 四&#xff1a; MyBatis动态SQL学习笔记 五&…...

企业门户网站需求分析/百度网盘登录入口官网

Sublime Text 2报“Decode error - output not utf-8”错误的解决办法 作者&#xff1a;chszs&#xff0c;转载需注明。作者博客主页&#xff1a;http://blog.csdn.net/chszs 正如我在 上一篇博客《Sublime Text 2搭建Java开发环境》中所述&#xff0c;在Sublime Text 2上搭建J…...

忻州 建网站/如何制定会员营销方案

单行函数&#xff1a; 是为了完成某些特定操作的功能支持&#xff0c;方便数据的相关开发&#xff1b; 语法; funcation_name(列|表达式[参数1&#xff0c;参数2]&#xff09; 单行函数&#xff1a; 字符函数&#xff1a;接受数据返回具体的字符信息&#xff1b; 数值函数…...

学生模拟网站开发项目/锦州seo推广

本文系阅读阅读原章节后总结概括得出。由于需要我进行一定的概括提炼&#xff0c;如有不当之处欢迎读者斧正。如果你对内容有任何疑问&#xff0c;欢迎共同交流讨论。 关于什么时候应该用强制类型解封&#xff0c;网上有各种的答案&#xff0c;有人说永远别用&#xff0c;有人说…...