ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

xenomai中断事件等api

2022-07-07 21:32:06  阅读:177  来源: 互联网

标签:struct pin 中断 rgc rtdm int api xenomai gpio


RTDM中断请求函数原型:


int rtdm_irq_request (rtdm_irq_t *irq_handle, unsigned int irq_no,

                            rtdm_irq_handler_t handler, unsigned long flags,

                            const char *device_name, void *arg) 

参数介绍:

irq_handle , IRQ句柄

irq_no: IRQ的中断号

handler:中断处理的句柄

flags:注册的标志位,细节查看宏定义 RTDM_IRQTYPE_xxx

device_name:设备名称,将显示在实时IRQ列表中

arg:在调用时传递给中断处理程序的参数指针

注册标志位介绍:

RTDM_IRQ_NONE 未处理的中断。

RTDM_IRQ_HANDLED 表示处理中断.

RTDM_IRQ_DISABLE 退出时请求中断禁用。


int rtdm_irq_enable (rtdm_irq_t *irq_handle) //使能IRQ中断,此服务只能在次要模式使用。

int rtdm_irq_free(rtdm_irq_t * irq_handle) //释放已申请的中断句柄


//获取IRQ句柄参数指针

define rtdm_irq_get_arg ( irq_handle, type ) ((type *)irq_handle->cookie)


//用于设置irq对应的中断的触发类型.

int irq_set_irq_type(unsigned int irq, unsigned int type)

中断方式type定义如下: (linux-2.6.21.7/include/linux/interrupt.h )

define IRQF_TRIGGER_NONE 0x00000000/无触发中断/

#define IRQF_TRIGGER_RISING      0x00000001/*指定中断触发类型:上升沿有效*/

#define IRQF_TRIGGER_FALLING    0x00000002/*中断触发类型:下降沿有效*/

#define IRQF_TRIGGER_HIGH         0x00000004/*指定中断触发类型:高电平有效*/

#define IRQF_TRIGGER_LOW          0x00000008/*指定中断触发类型:低电平有效*/

#define IRQF_TRIGGER_MASK       (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW | 

                                                     IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)

#define IRQF_TRIGGER_PROBE           0x00000010/*触发式检测中断*/

#define IRQF_DISABLED                         0x00000020 //使用这个时为快速中断

#define IRQF_SAMPLE_RANDOM         0x00000040 /*供系统产生随机数使用*/

#define IRQF_SHARED                            0x00000080 /*中断可以在设备之间可共享*/

#define IRQF_PROBE_SHARED            0x00000100/*探测共享中断*/

#define IRQF_TIMER                                0x00000200/*专用于时钟中断*/

#define IRQF_PERCPU                           0x00000400/*每CPU周期执行中断*/

#define IRQF_NOBALANCING               0x00000800/*复位中断*/

#define IRQF_IRQPOLL                           0x00001000/*共享中断中根据注册时间判断*/

#define IRQF_ONESHOT                         0x00002000/*硬件中断处理完后触发*/

##########例程用到的函数介绍##################


//检测是否启动实时内核

static inline int realtime_core_enabled(void) {

return atomic_read(&cobalt_runstate) != COBALT_STATE_DISABLED;

}


int class_create(owner,name)//动态创建设备的逻辑类,创建的逻辑类位于/sys/class/

class_destroy(struct class *cls)//注销类,与create_class配对使用

int rtdm_drv_set_sysclass(struct rtdm_driver *drv, struct class *cls) //设置RTDM驱动程序的内核设备类。

RTDM驱动程序默认属于Linux的RTDM设备类,在/dev/ rtdm上创建设备节点,在/sys/class/ RTDM下创建系统文件节点。


int gpiod_get_raw_value(const struct gpio_desc *desc) //获取设备GPIO实际电平,非逻辑电平

void gpiod_set_raw_value(struct gpio_desc *desc, int value) ///设置设备GPIO实际电平,非逻辑电平


int gpio_request(unsigned gpio, const char *label) //系统分别GPIO,label为引脚的名称定义

int gpio_export(unsigned gpio, bool direction_may_change); //导出gpio到用户空间


int gpio_direction_input(unsigned gpio); //设置io为输入

int gpio_direction_output(unsigned gpio, int value); //设置io为输出,并设置初始值


void rtdm_event_init(rtdm_event_t *event, unsigned long pending) //初始化一个事件,pending非0则挂起

void rtdm_event_signal (rtdm_event_t * event) //产生一个事件信号

void rtdm_event_clear(rtdm_event_t * event) //清空一个事件

void rtdm_event_destroy(rtdm_event_t * event) //销毁一个事件

//将给定的选择器绑定到事件,以便当有事件发生通知到绑定器

int rtdm_event_select(rtdm_event_t *event, rtdm_selector_t *selector,
enum rtdm_selecttype type, unsigned int fd_index)


wait_event_interruptible( waitQueue , condition ) //等待中断事件,并进入休眠


void wake_up(wait_queue_head_t *q) //唤醒一个等待队列

成功地唤醒一个被wait_event_interruptible()的进程,需要同时满足:
1)condition为真的前提下 2) 调用wake_up()。


rtdm_lock_get_irqsave(..) //通过停止头域,获取锁和禁用抢占

rtdm_lock_put_irqrestore(..) //恢复抢占状态。


rtdm_safe_copy_from_user()和rtdm_copy_from_user()的区别:

两者都有类似的功能,将用户空间内存块复制到指定的缓冲区

rtdm_copy_from_user()实现是 __xn_copy_from_user(dst, src, size) ? -EFAULT : 0;

rtdm_safe_copy_from_user()是在上面函数增加access_rok(src, size)地址读权限判断


rtdm_lock_get_irqsave(__lock, __context) //获取锁定和禁用抢占,通过停止头域。

rtdm_lock_put_irqrestore (rtdm_lock_t *lock, rtdm_lockctx_t context) //释放锁并恢复抢占状态



#######gpio-core.c 所使用的中断机制 (xenomai3/kernel/drivers/gpio)##################

点击查看代码
/**
 * @note Copyright (C) 2016 Philippe Gerum <rpm@xenomai.org>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/err.h>
#include "gpio-core.h"
 
struct rtdm_gpio_pin {
	struct rtdm_device dev;
	struct list_head next;
	rtdm_irq_t irqh;
	rtdm_event_t event;
	char *name;
	struct gpio_desc *desc;
};
 
struct rtdm_gpio_chan {
	int requested : 1,
	    has_direction : 1,
	    is_output : 1 ;
};
 
static int gpio_pin_interrupt(rtdm_irq_t *irqh)
{
	struct rtdm_gpio_pin *pin;
 
	pin = rtdm_irq_get_arg(irqh, struct rtdm_gpio_pin);
 
	rtdm_event_signal(&pin->event);
 
	return RTDM_IRQ_HANDLED;
}
 
static int request_gpio_irq(unsigned int gpio, struct rtdm_gpio_pin *pin,
			    struct rtdm_gpio_chan *chan,
			    int trigger)
{
	int ret, irq_trigger;
	unsigned int irq;
 
	if (trigger & ~GPIO_TRIGGER_MASK)
		return -EINVAL;
 
	ret = gpio_request(gpio, pin->name);
	if (ret) {
		if (ret != -EPROBE_DEFER)
			printk(XENO_ERR "cannot request GPIO%d\n", gpio);
		return ret;
	}
 
	ret = gpio_direction_input(gpio);
	if (ret) {
		printk(XENO_ERR "cannot set GPIO%d as input\n", gpio);
		goto fail;
	}
 
	chan->has_direction = true;
	gpio_export(gpio, true);
 
	rtdm_event_clear(&pin->event);
	irq = gpio_to_irq(gpio);
 
	irq_trigger = 0;
	if (trigger & GPIO_TRIGGER_EDGE_RISING)
		irq_trigger |= IRQ_TYPE_EDGE_RISING;
	if (trigger & GPIO_TRIGGER_EDGE_FALLING)
		irq_trigger |= IRQ_TYPE_EDGE_FALLING;
	if (trigger & GPIO_TRIGGER_LEVEL_HIGH)
		irq_trigger |= IRQ_TYPE_LEVEL_HIGH;
	if (trigger & GPIO_TRIGGER_LEVEL_LOW)
		irq_trigger |= IRQ_TYPE_LEVEL_LOW;
 
	if (irq_trigger)
		irq_set_irq_type(irq, irq_trigger);
	
	ret = rtdm_irq_request(&pin->irqh, irq, gpio_pin_interrupt,
			       0, pin->name, pin);
	if (ret) {
		printk(XENO_ERR "cannot request GPIO%d interrupt\n", gpio);
		goto fail;
	}
 
	chan->requested = true;
 
	rtdm_irq_enable(&pin->irqh);
 
	return 0;
fail:
	gpio_free(gpio);
 
	return ret;
}
 
static void release_gpio_irq(unsigned int gpio, struct rtdm_gpio_pin *pin,
			     struct rtdm_gpio_chan *chan)
{
	rtdm_irq_free(&pin->irqh);
	gpio_free(gpio);
	chan->requested = false;
}
 
static int gpio_pin_ioctl_nrt(struct rtdm_fd *fd,
			      unsigned int request, void *arg)
{
	struct rtdm_gpio_chan *chan = rtdm_fd_to_private(fd);
	struct rtdm_device *dev = rtdm_fd_device(fd);
	unsigned int gpio = rtdm_fd_minor(fd);
	int ret = 0, val, trigger;
	struct rtdm_gpio_pin *pin;
	
	pin = container_of(dev, struct rtdm_gpio_pin, dev);
 
	switch (request) {
	case GPIO_RTIOC_DIR_OUT:
		ret = rtdm_safe_copy_from_user(fd, &val, arg, sizeof(val));
		if (ret)
			return ret;
		ret = gpio_direction_output(gpio, val);
		if (ret == 0) {
			chan->has_direction = true;
			chan->is_output = true;
		}
		break;
	case GPIO_RTIOC_DIR_IN:
		ret = gpio_direction_input(gpio);
		if (ret == 0)
			chan->has_direction = true;
		break;
	case GPIO_RTIOC_IRQEN:
		if (chan->requested)
			return -EBUSY;
		ret = rtdm_safe_copy_from_user(fd, &trigger,
				       arg, sizeof(trigger));
		if (ret)
			return ret;
		ret = request_gpio_irq(gpio, pin, chan, trigger);
		break;
	case GPIO_RTIOC_IRQDIS:
		release_gpio_irq(gpio, pin, chan);
		chan->requested = false;
		break;
	default:
		return -EINVAL;
	}
	
	return ret;
}
 
static ssize_t gpio_pin_read_rt(struct rtdm_fd *fd,
				void __user *buf, size_t len)
{
	struct rtdm_gpio_chan *chan = rtdm_fd_to_private(fd);
	struct rtdm_device *dev = rtdm_fd_device(fd);
	struct rtdm_gpio_pin *pin;
	int value, ret;
 
	if (len < sizeof(value))
		return -EINVAL;
 
	if (!chan->has_direction)
		return -EAGAIN;
 
	if (chan->is_output)
		return -EINVAL;
 
	pin = container_of(dev, struct rtdm_gpio_pin, dev);
 
	if (!(fd->oflags & O_NONBLOCK)) {
		ret = rtdm_event_wait(&pin->event);
		if (ret)
			return ret;
	}
 
	value = gpiod_get_raw_value(pin->desc);
	ret = rtdm_safe_copy_to_user(fd, buf, &value, sizeof(value));
	
	return ret ?: sizeof(value);
}
 
static ssize_t gpio_pin_write_rt(struct rtdm_fd *fd,
				 const void __user *buf, size_t len)
{
	struct rtdm_gpio_chan *chan = rtdm_fd_to_private(fd);
	struct rtdm_device *dev = rtdm_fd_device(fd);
	struct rtdm_gpio_pin *pin;
	int value, ret;
 
	if (len < sizeof(value))
		return -EINVAL;
 
	if (!chan->has_direction)
		return -EAGAIN;
 
	if (!chan->is_output)
		return -EINVAL;
 
	ret = rtdm_safe_copy_from_user(fd, &value, buf, sizeof(value));
	if (ret)
		return ret;
 
	pin = container_of(dev, struct rtdm_gpio_pin, dev);
	gpiod_set_raw_value(pin->desc, value);
 
	return sizeof(value);
}
 
static int gpio_pin_select(struct rtdm_fd *fd, struct xnselector *selector,
			   unsigned int type, unsigned int index)
{
	struct rtdm_gpio_chan *chan = rtdm_fd_to_private(fd);
	struct rtdm_device *dev = rtdm_fd_device(fd);
	struct rtdm_gpio_pin *pin;
 
	if (!chan->has_direction)
		return -EAGAIN;
 
	if (chan->is_output)
		return -EINVAL;
 
	pin = container_of(dev, struct rtdm_gpio_pin, dev);
 
	return rtdm_event_select(&pin->event, selector, type, index);
}
 
static void gpio_pin_close(struct rtdm_fd *fd)
{
	struct rtdm_gpio_chan *chan = rtdm_fd_to_private(fd);
	struct rtdm_device *dev = rtdm_fd_device(fd);
	unsigned int gpio = rtdm_fd_minor(fd);
	struct rtdm_gpio_pin *pin;
 
	if (chan->requested) {
		pin = container_of(dev, struct rtdm_gpio_pin, dev);
		release_gpio_irq(gpio, pin, chan);
	}
}
 
static void delete_pin_devices(struct rtdm_gpio_chip *rgc)
{
	struct rtdm_gpio_pin *pin, *n;
	struct rtdm_device *dev;
	rtdm_lockctx_t s;
 
	rtdm_lock_get_irqsave(&rgc->lock, s);
	
	list_for_each_entry_safe(pin, n, &rgc->pins, next) {
		list_del(&pin->next);
		rtdm_lock_put_irqrestore(&rgc->lock, s);
		dev = &pin->dev;
		rtdm_dev_unregister(dev);
		rtdm_event_destroy(&pin->event);
		kfree(dev->label);
		kfree(pin->name);
		kfree(pin);
		rtdm_lock_get_irqsave(&rgc->lock, s);
	}
 
	rtdm_lock_put_irqrestore(&rgc->lock, s);
}
 
static int create_pin_devices(struct rtdm_gpio_chip *rgc)
{
	struct gpio_chip *gc = rgc->gc;
	struct rtdm_gpio_pin *pin;
	struct rtdm_device *dev;
	rtdm_lockctx_t s;
	int n, ret;
 
	for (n = gc->base; n < gc->base + gc->ngpio - 1; n++) {
		ret = -ENOMEM;
		pin = kzalloc(sizeof(*pin), GFP_KERNEL);
		if (pin == NULL)
			goto fail;
		pin->name = kasprintf(GFP_KERNEL, "gpio%d", n);
		if (pin->name == NULL)
			goto fail_name;
		pin->desc = gpio_to_desc(n);
		if (pin->desc == NULL) {
			ret = -ENODEV;
			goto fail_desc;
		}
		dev = &pin->dev;
		dev->driver = &rgc->driver;
		dev->label = kasprintf(GFP_KERNEL, "%s/gpio%%d", gc->label);
		if (dev->label == NULL)
			goto fail_label;
		dev->minor = n;
		dev->device_data = rgc;
		ret = rtdm_dev_register(dev);
		if (ret)
			goto fail_register;
		rtdm_event_init(&pin->event, 0);
		rtdm_lock_get_irqsave(&rgc->lock, s);
		list_add_tail(&pin->next, &rgc->pins);
		rtdm_lock_put_irqrestore(&rgc->lock, s);
	}
 
	return 0;
 
fail_register:
	kfree(dev->label);
fail_desc:
fail_label:
	kfree(pin->name);
fail_name:
	kfree(pin);
fail:
	delete_pin_devices(rgc);
 
	return ret;
}
 
static char *gpio_pin_devnode(struct device *dev, umode_t *mode)
{
	return kasprintf(GFP_KERNEL, "rtdm/%s/%s",
			 dev->class->name,
			 dev_name(dev));
}
 
int rtdm_gpiochip_add(struct rtdm_gpio_chip *rgc,
		      struct gpio_chip *gc, int gpio_subclass)
{
	int ret;
 
	if (!realtime_core_enabled())
		return 0;
 
	rgc->devclass = class_create(gc->owner, gc->label);
	if (IS_ERR(rgc->devclass)) {
		printk(XENO_ERR "cannot create sysfs class\n");
		return PTR_ERR(rgc->devclass);
	}
	rgc->devclass->devnode = gpio_pin_devnode;
 
	rgc->driver.profile_info = (struct rtdm_profile_info)
		RTDM_PROFILE_INFO(rtdm_gpio_chip,
				  RTDM_CLASS_GPIO,
				  gpio_subclass,
				  0);
	rgc->driver.device_flags = RTDM_NAMED_DEVICE|RTDM_FIXED_MINOR;
	rgc->driver.base_minor = gc->base;
	rgc->driver.device_count = gc->ngpio;
	rgc->driver.context_size = sizeof(struct rtdm_gpio_chan);
	rgc->driver.ops = (struct rtdm_fd_ops){
		.close		=	gpio_pin_close,
		.ioctl_nrt	=	gpio_pin_ioctl_nrt,
		.read_rt	=	gpio_pin_read_rt,
		.write_rt	=	gpio_pin_write_rt,
		.select		=	gpio_pin_select,
	};
	
	rtdm_drv_set_sysclass(&rgc->driver, rgc->devclass);
 
	rgc->gc = gc;
	INIT_LIST_HEAD(&rgc->pins);
	rtdm_lock_init(&rgc->lock);
 
	ret = create_pin_devices(rgc);
	if (ret)
		class_destroy(rgc->devclass);
	
	return ret;
}
EXPORT_SYMBOL_GPL(rtdm_gpiochip_add);
 
void rtdm_gpiochip_remove(struct rtdm_gpio_chip *rgc)
{
	if (!realtime_core_enabled())
		return;
 
	delete_pin_devices(rgc);
	class_destroy(rgc->devclass);
}
EXPORT_SYMBOL_GPL(rtdm_gpiochip_remove);
 
static int gpiochip_match_name(struct gpio_chip *chip, void *data)
{
	const char *name = data;
 
	return !strcmp(chip->label, name);
}
 
static struct gpio_chip *find_chip_by_name(const char *name)
{
	return gpiochip_find((void *)name, gpiochip_match_name);
}
 
int rtdm_gpiochip_add_by_name(struct rtdm_gpio_chip *rgc,
			      const char *label, int gpio_subclass)
{
	struct gpio_chip *gc = find_chip_by_name(label);
 
	if (gc == NULL)
		return -EPROBE_DEFER;
 
	return rtdm_gpiochip_add(rgc, gc, gpio_subclass);
}
EXPORT_SYMBOL_GPL(rtdm_gpiochip_add_by_name);
 
#ifdef CONFIG_OF
 
#include <linux/of_platform.h>
 
LIST_HEAD(rtdm_gpio_chips);
 
static DEFINE_MUTEX(chip_lock);
 
static int match_gpio_chip(struct gpio_chip *gc, void *data)
{
	struct device *dev = data;
 
	return gc->dev == dev;
}
 
static int add_gpio_chip(struct gpio_chip *gc, int type)
{
	struct rtdm_gpio_chip *rgc;
	int ret;
 
	rgc = kzalloc(sizeof(*rgc), GFP_KERNEL);
	if (rgc == NULL)
		return -ENOMEM;
 
	ret = rtdm_gpiochip_add(rgc, gc, type);
	if (ret) {
		kfree(rgc);
		return ret;
	}
 
	mutex_lock(&chip_lock);
	list_add(&rgc->next, &rtdm_gpio_chips);
	mutex_unlock(&chip_lock);
 
	return 0;
}
 
int rtdm_gpiochip_scan_of(struct device_node *from, const char *compat,
			  int type)
{
	struct device_node *np = from;
	struct platform_device *pdev;
	struct gpio_chip *gc;
	int ret = -ENODEV;
 
	for (;;) {
		np = of_find_compatible_node(np, NULL, compat);
		if (np == NULL)
			break;
		pdev = of_find_device_by_node(np);
		of_node_put(np);
		if (pdev == NULL)
			break;
		gc = gpiochip_find(&pdev->dev, match_gpio_chip);
		if (gc) {
			ret = add_gpio_chip(gc, type);
			if (ret)
				return ret;
		}
	}
 
	return ret;
}
EXPORT_SYMBOL_GPL(rtdm_gpiochip_scan_of);
 
void rtdm_gpiochip_remove_of(int type)
{
	struct rtdm_gpio_chip *rgc, *n;
 
	list_for_each_entry_safe(rgc, n, &rtdm_gpio_chips, next) {
		if (rgc->driver.profile_info.subclass_id == type) {
			mutex_lock(&chip_lock);
			list_del(&rgc->next);
			mutex_unlock(&chip_lock);
			rtdm_gpiochip_remove(rgc);
			kfree(rgc);
		}
	}
}
EXPORT_SYMBOL_GPL(rtdm_gpiochip_remove_of);
 
#endif /* CONFIG_OF */
####################gpio-core.h########################
点击查看代码
/**
 * @note Copyright (C) 2016 Philippe Gerum <rpm@xenomai.org>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
#ifndef _RTDM_GPIO_CORE_H
#define _RTDM_GPIO_CORE_H
 
#include <linux/list.h>
#include <rtdm/driver.h>
#include <rtdm/uapi/gpio.h>
 
struct class;
struct device_node;
 
struct rtdm_gpio_chip {
	struct gpio_chip *gc;
	struct rtdm_driver driver;
	struct class *devclass;
	struct list_head pins;
	struct list_head next;
	rtdm_lock_t lock;
};
 
int rtdm_gpiochip_add(struct rtdm_gpio_chip *rgc,
		      struct gpio_chip *gc,
		      int gpio_subclass);
 
void rtdm_gpiochip_remove(struct rtdm_gpio_chip *rgc);
 
int rtdm_gpiochip_add_by_name(struct rtdm_gpio_chip *rgc,
			      const char *label, int gpio_subclass);
 
#ifdef CONFIG_OF
 
int rtdm_gpiochip_scan_of(struct device_node *from,
			  const char *compat, int type);
 
void rtdm_gpiochip_remove_of(int type);
 
extern struct list_head rtdm_gpio_chips;
 
#endif
 
#endif /* !_RTDM_GPIO_CORE_H */

————————————————
版权声明:本文为CSDN博主「wabil」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/wabil/article/details/104313726

标签:struct,pin,中断,rgc,rtdm,int,api,xenomai,gpio
来源: https://www.cnblogs.com/wangjirui/p/16456187.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有