主頁 >  其他 > linux驅動之I2C子系統

linux驅動之I2C子系統

2021-10-18 15:42:26 其他

目錄

  • 一、I2C基本原理
  • 二、linux內核的I2C子系統詳解
    • 1、linux內核的I2C驅動框架總覽
    • 2、I2C子系統的4個關鍵結構體(kernel/include/linux/i2c.h)
    • 3、關鍵檔案
    • 4、i2c-core.c初步分析(從后向前看)
    • 5、I2C總線的匹配機制(i2c-core.c)
    • 6、核心層開放給其他部分的注冊介面
    • 7、adapter模塊的注冊
    • 8、s3c24xx_i2c_probe函式分析
    • 9、i2c_algorithm
    • 10、i2c_driver的注冊

一、I2C基本原理

(1)三根通信線:SCL、SDA、GND

(2)同步、串行、電平、低速、近距離

(3)總線式結構,支持多個設備掛接在同一條總線上

(4)主從式結構,通信雙方必須一個為主(master)一個為從(slave),主設備掌握每次通信的主動權,從設備按照主設備的節奏被動回應,每個從設備在總線中(某條作業的總線上,并不是所有的總線上都是同一個地址)有唯一的地址(slave address),主設備通過從地址找到自己要通信的從設備(本質是廣播的方式),

??收發訊息都是廣播,總線上的設備都可收到,通過于自己的地址對比確認是否是給自己的資訊

(5)I2C主要用途就是主SoC和外圍設備之間的通信,最大優勢是可以在總線上擴展多個外圍設備的支持,常見的各種物聯網傳感器芯片(如gsensor、溫度、濕度、光強度、酸堿度、煙霧濃度、壓力等)均使用I2C介面和主SoC進行連接,

(6)電容觸摸屏芯片的多個引腳構成2個介面一個介面是I2C的,負責和主SoC連接(本身作為從設備),主SoC通過該介面初始化及控制電容觸摸屏芯片、芯片通過該介面向SoC匯報觸摸事件的資訊(觸摸坐標等),我們使用電容觸摸屏時重點關注的是這個介面;另一個介面是電容觸摸板的管理介面,電容觸摸屏芯片通過該介面來控制觸摸板硬體,該介面是電容觸摸屏公司關心的,他們的觸摸屏芯片內部韌體編程要處理這部分,我們使用電容觸摸屏的人并不關心這里,

二、linux內核的I2C子系統詳解

1、linux內核的I2C驅動框架總覽

(1)I2C驅動框架的主要目標是:讓驅動開發者可以在內核中方便的添加自己的I2C設備的驅動程式,從而可以更容易的在linux下驅動自己的I2C介面硬體

(2)原始碼中I2C相關的驅動均位于:drivers/i2c目錄下,

(3)linux系統提供2種I2C驅動實作方法

??第一種叫i2c-dev,對應drivers/i2c/i2c-dev.c,這種方法只是封裝了主機(I2C master,一般是SoC中內置的I2C控制器)的I2C基本操作,并且向應用層提供相應的操作介面,應用層代碼需要自己去實作對slave的控制和操作,所以這種I2C驅動相當于只是提供給應用層可以訪問slave硬體設備的介面,本身并未對硬體做任何操作,應用需要實作對硬體的操作(操控暫存器進行初始化等),因此寫應用的人必須對硬體非常了解,其實相當于傳統的驅動中干的活兒丟給應用去做了,所以這種I2C驅動又叫做**“應用層驅動”**,這種方式并不主流,它的優勢是把差異化都放在應用中,這樣在設備比較難纏(尤其是slave是非標準I2C時)時不用動驅動,而只需要修改應用就可以實作對各種設備的驅動,這種驅動在驅動層很簡單(就是i2c-dev.c)我們就不分析了,

??第二種I2C驅動是所有的代碼都放在驅動層實作,直接向應用層提供最終結果,應用層甚至不需要知道這里面有I2C存在,譬如電容式觸摸屏驅動,直接向應用層提供/dev/input/event1的操作介面,應用層編程的人根本不知道event1中涉及到了I2C,這種是我們后續分析的重點,

2、I2C子系統的4個關鍵結構體(kernel/include/linux/i2c.h)

210有多個iic介面,每個介面由多個暫存器操控,代表了iic控制器
(1)struct i2c_adapter:用來描述主機的iic控制器(配接器),主控驅動,芯片換了這塊代碼就要變

struct i2c_adapter {
	struct module *owner;
	unsigned int id;
	unsigned int class;		  /* classes to allow probing for */
	const struct i2c_algorithm *algo; /* the algorithm to access the bus */
									//通過函式指標可以使用不同的演算法	
	void *algo_data;

	/* data fields that are valid for all devices	*/
	struct rt_mutex bus_lock;

	int timeout;			/* in jiffies */
	int retries;
	struct device dev;		/* the adapter device */

	int nr;
	char name[48];
	struct completion dev_released;

	struct list_head userspace_clients;
};

(2)struct i2c_algorithm:用來描述I2C演算法,即主從機的通信時序,其被包含在struct i2c_adapter中,同一個主控soc可以有不同的演算法,比如從設備是一個標準的iic設備傳感器,一個不是標準的iic設備

struct i2c_algorithm {
	/* If an adapter algorithm can't do I2C-level access, set master_xfer
	   to NULL. If an adapter algorithm can do SMBus access, set
	   smbus_xfer. If set to NULL, the SMBus protocol is simulated
	   using common I2C messages */
	/* master_xfer should return the number of messages successfully
	   processed, or a negative value on error */
	int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
			   int num);
	int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
			   unsigned short flags, char read_write,
			   u8 command, int size, union i2c_smbus_data *data);

	/* To determine what the adapter supports */
	u32 (*functionality) (struct i2c_adapter *);
};

(3)struct i2c_client:描述I2C(從機)設備資訊

struct i2c_client {
	unsigned short flags;		/* div., see below		*/
	unsigned short addr;		/* chip address - NOTE: 7bit	*/
					/* addresses are stored in the	*/
					/* _LOWER_ 7 bits		*/
	char name[I2C_NAME_SIZE];
	struct i2c_adapter *adapter;	/* the adapter we sit on	*/
	struct i2c_driver *driver;	/* and our access routines	*/
	struct device dev;		/* the device structure		*/
	int irq;			/* irq issued by device		*/
	struct list_head detected;
};

(4)struct i2c_driver:描述I2C(從機)設備驅動

struct i2c_driver {
	unsigned int class;

	/* Notifies the driver that a new bus has appeared or is about to be
	 * removed. You should avoid using this if you can, it will probably
	 * be removed in a near future.
	 */
	int (*attach_adapter)(struct i2c_adapter *);
	int (*detach_adapter)(struct i2c_adapter *);

	/* Standard driver model interfaces */
	int (*probe)(struct i2c_client *, const struct i2c_device_id *);
	int (*remove)(struct i2c_client *);

	/* driver model interfaces that don't relate to enumeration  */
	void (*shutdown)(struct i2c_client *);
	int (*suspend)(struct i2c_client *, pm_message_t mesg);
	int (*resume)(struct i2c_client *);

	/* Alert callback, for example for the SMBus alert protocol.
	 * The format and meaning of the data value depends on the protocol.
	 * For the SMBus alert protocol, there is a single bit of data passed
	 * as the alert response's low bit ("event flag").
	 */
	void (*alert)(struct i2c_client *, unsigned int data);

	/* a ioctl like command that can be used to perform specific functions
	 * with the device.
	 */
	int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);

	struct device_driver driver;
	const struct i2c_device_id *id_table;

	/* Device detection callback for automatic device creation */
	int (*detect)(struct i2c_client *, struct i2c_board_info *);
	const unsigned short *address_list;
	struct list_head clients;
};

??i2c_driver 與 i2c_client在驅動中會進行匹配,當二者匹配成功時,i2c_client就會將自己的硬體資訊交給i2c_driver,類似于平臺總線的驅動和設備進行匹配,

3、關鍵檔案

(1)kernel/drivers/i2c/i2c-core.c
??iic核心檔案,屬于內核開發者實作的那部分,與具體硬體無關,屬于純軟體,但其內部間接性地呼叫了許多和硬體操作相關的函式,通過結構體與函式指標實作,

(2)busses目錄(kernel/drivers/i2c/)
??放了許多主控芯片的iic控制器相關程式,我們要關注的是i2c-s3c2410.c,2410和210的iic控制器這部分的實作相同

(3)kernel/drivers/i2c/algos,實作的一些演算法操作,我們暫時不需要去關注

(4)此外還會涉及到mach-x210.c、kernel/drivers/i2c/i2c-boardinfo.c

4、i2c-core.c初步分析(從后向前看)

(1)smbus代碼略過我們涉及不到(1534行之后的代碼)
?emsp;其是應用于移動PC和桌面PC系統中的低速率通訊,希望通過一條廉價并且功能強大的總線(由兩條線組成),來控制主板上的設備并收集相應的資訊,

(2)1179行代碼

postcore_initcall(i2c_init);
module_exit(i2c_exit);

總線在內核中也是一個模塊,是需要去注冊的,

struct bus_type i2c_bus_type = {
	.name		= "i2c",
	.match		= i2c_device_match,//用于driver和device進行匹配
	.probe		= i2c_device_probe,//當driver和device匹配上之后就會執行該函式
	.remove		= i2c_device_remove,
	.shutdown	= i2c_device_shutdown,
	.pm		= &i2c_device_pm_ops,
};
static int __init i2c_init(void)
{
	int retval;

	retval = bus_register(&i2c_bus_type);//注冊iic,注冊后就可以在/sys/bus/目錄下看到iic
	if (retval)
		return retval;
#ifdef CONFIG_I2C_COMPAT//這個宏應該是沒有的
	i2c_adapter_compat_class = class_compat_register("i2c-adapter");
	if (!i2c_adapter_compat_class) {
		retval = -ENOMEM;
		goto bus_err;
	}
#endif
	retval = i2c_add_driver(&dummy_driver);//dummy_driver是一個空的驅動
	if (retval)
		goto class_err;
	return 0;

class_err:
#ifdef CONFIG_I2C_COMPAT
	class_compat_unregister(i2c_adapter_compat_class);
bus_err:
#endif
	bus_unregister(&i2c_bus_type);
	return retval;
}

當新注冊driver/device和device/driver匹配上后就會執行XX_probe函式進行初始化,否則就沒有驅動,

static void __exit i2c_exit(void)
{
	i2c_del_driver(&dummy_driver);
#ifdef CONFIG_I2C_COMPAT
	class_compat_unregister(i2c_adapter_compat_class);
#endif
	bus_unregister(&i2c_bus_type);
}

5、I2C總線的匹配機制(i2c-core.c)

(1)總線的match函式i2c_device_match

#define to_i2c_driver(d) container_of(d, struct i2c_driver, driver)

static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
	struct i2c_client	*client = i2c_verify_client(dev);
	struct i2c_driver	*driver;

	if (!client)
		return 0;

	driver = to_i2c_driver(drv);//由結構體成員得到結構體
	/* match on an id table if there is one */
	if (driver->id_table)
		return i2c_match_id(driver->id_table, client) != NULL;

	return 0;
}
變數成員組成及型別決議:driver->id_table//一個陣列
struct i2c_driver	*driver;
	const struct i2c_device_id *id_table;
		struct i2c_device_id {
			char name[I2C_NAME_SIZE];
			kernel_ulong_t driver_data	/* Data private to the driver */
					__attribute__((aligned(sizeof(kernel_ulong_t))));
		};

變數成員組成及型別決議:client
struct i2c_client
	char name[I2C_NAME_SIZE];
static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,					const struct i2c_client *client)
{
	while (id->name[0]) {
		if (strcmp(client->name, id->name) == 0)//字串比較進行driver和device匹配
			return id;
		id++;
	}
	return NULL;
}

??讓device和driver進行匹配,不同的總線有它自己的match函式,一般都是通過名字進行匹配的,當二者匹配上時會執行i2c_device_probe中的,總線的probe函式會去調driver的probe函式,

(2)總線的probe函式i2c_device_probe

static int i2c_device_probe(struct device *dev)
{
	struct i2c_client	*client = i2c_verify_client(dev);//i2c_client就是device
	struct i2c_driver	*driver;//i2c_driver就是driver
	int status;

	if (!client)
		return 0;

	driver = to_i2c_driver(dev->driver);//找到驅動
	if (!driver->probe || !driver->id_table)
		return -ENODEV;
	client->driver = driver;
	if (!device_can_wakeup(&client->dev))
		device_init_wakeup(&client->dev,
					client->flags & I2C_CLIENT_WAKE);
	dev_dbg(dev, "probe\n");

	//當driver和device匹配上之后會去執行driver中的probe函式
	status = driver->probe(client, i2c_match_id(driver->id_table, client));
	if (status) {
		client->driver = NULL;
		i2c_set_clientdata(client, NULL);
	}
	return status;
}

總結:I2C總線上有2條分支:i2c_client鏈和i2c_driver鏈,當任何一個driver或者client去注冊時,I2C總線都會呼叫match函式去對client.name和driver.id_table.name進行回圈匹配,如果driver.id_table中所有的id都匹配不上則說明client并沒有找到一個對應的driver,沒了;如果匹配上了則標明client和driver是適用的,那么I2C總線會呼叫自身的probe函式,自身的probe函式又會呼叫driver中提供的probe函式,driver中的probe函式會對設備進行硬體初始化和后續作業,

6、核心層開放給其他部分的注冊介面

(1)i2c_add_adapter/i2c_add_numbered_adapter:注冊adapter(iic配接器,主機控制器)的

int i2c_add_adapter(struct i2c_adapter *adapter)
{
	int	id, res = 0;

retry:
	if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0)
		return -ENOMEM;

	mutex_lock(&core_lock);
	/* "above" here means "above or equal to", sigh */
	res = idr_get_new_above(&i2c_adapter_idr, adapter,
				__i2c_first_dynamic_bus_num, &id);
	mutex_unlock(&core_lock);

	if (res < 0) {
		if (res == -EAGAIN)
			goto retry;
		return res;
	}

	adapter->nr = id;
	return i2c_register_adapter(adapter);
}
int i2c_add_numbered_adapter(struct i2c_adapter *adap)
{
	int	id;
	int	status;

	if (adap->nr & ~MAX_ID_MASK)
		return -EINVAL;

retry:
	if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0)
		return -ENOMEM;

	mutex_lock(&core_lock);
	/* "above" here means "above or equal to", sigh;
	 * we need the "equal to" result to force the result
	 */
	status = idr_get_new_above(&i2c_adapter_idr, adap, adap->nr, &id);
	if (status == 0 && id != adap->nr) {
		status = -EBUSY;
		idr_remove(&i2c_adapter_idr, id);
	}
	mutex_unlock(&core_lock);
	if (status == -EAGAIN)
		goto retry;

	if (status == 0)
		status = i2c_register_adapter(adap);
	return status;
}

(2)i2c_add_driver:注冊driver的

static inline int i2c_add_driver(struct i2c_driver *driver)
{
	return i2c_register_driver(THIS_MODULE, driver);
}

int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
	int res;

	/* Can't register until after driver model init */
	if (unlikely(WARN_ON(!i2c_bus_type.p)))
		return -EAGAIN;

	/* add the driver to the list of i2c drivers in the driver core */
	driver->driver.owner = owner;
	driver->driver.bus = &i2c_bus_type;

	/* When registration returns, the driver core
	 * will have called probe() for all matching-but-unbound devices.
	 */
	res = driver_register(&driver->driver);
	if (res)
		return res;

	pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);

	INIT_LIST_HEAD(&driver->clients);
	/* Walk the adapters that are already present */
	mutex_lock(&core_lock);
	bus_for_each_dev(&i2c_bus_type, NULL, driver, __process_new_driver);
	mutex_unlock(&core_lock);

	return 0;
}

(3)i2c_new_device:注冊client的

struct i2c_client *
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{
	struct i2c_client	*client;
	int			status;

	client = kzalloc(sizeof *client, GFP_KERNEL);
	if (!client)
		return NULL;

	client->adapter = adap;

	client->dev.platform_data = info->platform_data;

	if (info->archdata)
		client->dev.archdata = *info->archdata;

	client->flags = info->flags;
	client->addr = info->addr;
	client->irq = info->irq;

	strlcpy(client->name, info->type, sizeof(client->name));

	/* Check for address validity */
	status = i2c_check_client_addr_validity(client);
	if (status) {
		dev_err(&adap->dev, "Invalid %d-bit I2C address 0x%02hx\n",
			client->flags & I2C_CLIENT_TEN ? 10 : 7, client->addr);
		goto out_err_silent;
	}

	/* Check for address business */
	status = i2c_check_addr_busy(adap, client->addr);
	if (status)
		goto out_err;

	client->dev.parent = &client->adapter->dev;
	client->dev.bus = &i2c_bus_type;
	client->dev.type = &i2c_client_type;
#ifdef CONFIG_OF
	client->dev.of_node = info->of_node;
#endif

	dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),
		     client->addr);
	status = device_register(&client->dev);
	if (status)
		goto out_err;

	dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n",
		client->name, dev_name(&client->dev));

	return client;

out_err:
	dev_err(&adap->dev, "Failed to register i2c client %s at 0x%02x "
		"(%d)\n", client->name, client->addr, status);
out_err_silent:
	kfree(client);
	return NULL;
}

7、adapter模塊的注冊

kernel/drivers/i2c/busses/i2c-s3c2410.c
(1)平臺總線方式注冊

static int __init i2c_adap_s3c_init(void)
{
	return platform_driver_register(&s3c24xx_i2c_driver);
}
static struct platform_driver s3c24xx_i2c_driver = {
	.probe		= s3c24xx_i2c_probe,
	.remove		= s3c24xx_i2c_remove,
	.id_table	= s3c24xx_driver_ids,//用于匹配
	.driver		= {
		.owner	= THIS_MODULE,
		.name	= "s3c-i2c",//也是用于匹配
		.pm	= S3C24XX_DEV_PM_OPS,
	},
};

//kernel/drivers/base/platform.c
static int platform_match(struct device *dev, struct device_driver *drv)
{
	struct platform_device *pdev = to_platform_device(dev);
	struct platform_driver *pdrv = to_platform_driver(drv);

	/* match against the id table first */
	if (pdrv->id_table)//匹配方式1
		return platform_match_id(pdrv->id_table, pdev) != NULL;

	/* fall-back to driver name match */
	return (strcmp(pdev->name, drv->name) == 0);//匹配方式2,若無id table或
												//者id table未匹配上采用方式2
}
//陣列中由兩個元素,表明其支持兩種soc
static struct platform_device_id s3c24xx_driver_ids[] = {
	{
		.name		= "s3c2410-i2c",
		.driver_data	= TYPE_S3C2410,
	}, {
		.name		= "s3c2440-i2c",
		.driver_data	= TYPE_S3C2440,
	}, { },
	/*若要支持新的芯片,在該陣列中添加新元素,其內沒有s5pv210,分析猜測應該是走了
	s3c2410或者s3c2440的路線從而該驅動支持s5pv210,如何確認,查看kernel/arch/
	arm/mach-s5pv210/mach-x210.c中的platform_device陣列*/
};

static struct platform_device *smdkc110_devices[] __initdata 
	&s3c_device_i2c0
		struct platform_device s3c_device_i2c0 = {
			.name		  = "s3c2410-i2c",
			.id		  = 0,
			.num_resources	  = ARRAY_SIZE(s3c_i2c_resource),
			.resource	  = s3c_i2c_resource,
		};
static struct resource s3c_i2c_resource[] = { //iic控制器用到的一些資源
	[0] = {
		.start = S3C_PA_IIC,
		.end   = S3C_PA_IIC + SZ_4K - 1,
		.flags = IORESOURCE_MEM,
	},
	[1] = {
		.start = IRQ_IIC,
		.end   = IRQ_IIC,
		.flags = IORESOURCE_IRQ,
	},
};

(2)找到driver和device,并且確認其配對程序

static struct platform_driver s3c24xx_i2c_driver = {
	.probe		= s3c24xx_i2c_probe,
	.remove		= s3c24xx_i2c_remove,
	.id_table	= s3c24xx_driver_ids,
	.driver		= {
		.owner	= THIS_MODULE,
		.name	= "s3c-i2c",
		.pm	= S3C24XX_DEV_PM_OPS,
	},
};

struct platform_device s3c_device_i2c0 = {
	.name		  = "s3c2410-i2c",
	.id		  = 0,
	.num_resources	  = ARRAY_SIZE(s3c_i2c_resource),
	.resource	  = s3c_i2c_resource,
};

這兩個.name選一個與i2c_client中的.name進行匹配,匹配上后呼叫probe函式

8、s3c24xx_i2c_probe函式分析

static int s3c24xx_i2c_probe(struct platform_device *pdev)
{
	struct s3c24xx_i2c *i2c;
	struct s3c2410_platform_i2c *pdata;
	struct resource *res;
	int ret;

	pdata = pdev->dev.platform_data;
	if (!pdata) {
		dev_err(&pdev->dev, "no platform data\n");
		return -EINVAL;
	}

	i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL);
	if (!i2c) {
		dev_err(&pdev->dev, "no memory for state\n");
		return -ENOMEM;
	}

	strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));
	i2c->adap.owner   = THIS_MODULE;
	i2c->adap.algo    = &s3c24xx_i2c_algorithm;
	i2c->adap.retries = 2;
	i2c->adap.class   = I2C_CLASS_HWMON | I2C_CLASS_SPD;
	i2c->tx_setup     = 50;

	spin_lock_init(&i2c->lock);
	init_waitqueue_head(&i2c->wait);

	/* find the clock and enable it */

	i2c->dev = &pdev->dev;
	i2c->clk = clk_get(&pdev->dev, "i2c");//得到時鐘

	if (IS_ERR(i2c->clk)) {
		dev_err(&pdev->dev, "cannot get clock\n");
		ret = -ENOENT;
		goto err_noclk;
	}

	dev_dbg(&pdev->dev, "clock source %p\n", i2c->clk);

	clk_enable(i2c->clk);//打開時鐘,默認一般都是關閉的,省電

	/* map the registers */

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (res == NULL) {
		dev_err(&pdev->dev, "cannot find IO resource\n");
		ret = -ENOENT;
		goto err_clk;
	}

	i2c->ioarea = request_mem_region(res->start, resource_size(res),
					 pdev->name);

	if (i2c->ioarea == NULL) {
		dev_err(&pdev->dev, "cannot request IO\n");
		ret = -ENXIO;
		goto err_clk;
	}

	i2c->regs = ioremap(res->start, resource_size(res));

	if (i2c->regs == NULL) {
		dev_err(&pdev->dev, "cannot map IO\n");
		ret = -ENXIO;
		goto err_ioarea;
	}

	dev_dbg(&pdev->dev, "registers %p (%p, %p)\n",
		i2c->regs, i2c->ioarea, res);

	/* setup info block for the i2c core */

	i2c->adap.algo_data = i2c;
	i2c->adap.dev.parent = &pdev->dev;

	/* initialise the i2c controller */

	ret = s3c24xx_i2c_init(i2c);
	if (ret != 0)
		goto err_iomap;

	/* find the IRQ for this unit (note, this relies on the init call to
	 * ensure no current IRQs pending
	 */

	i2c->irq = ret = platform_get_irq(pdev, 0);
	if (ret <= 0) {
		dev_err(&pdev->dev, "cannot find IRQ\n");
		goto err_iomap;
	}

	ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED,
			  dev_name(&pdev->dev), i2c);//申請中斷,用于收發訊息,
			  //從而提高cpu的作業效率而非輪詢

	if (ret != 0) {
		dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq);
		goto err_iomap;
	}

	ret = s3c24xx_i2c_register_cpufreq(i2c);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to register cpufreq notifier\n");
		goto err_irq;
	}

	/* Note, previous versions of the driver used i2c_add_adapter()
	 * to add the bus at any number. We now pass the bus number via
	 * the platform data, so if unset it will now default to always
	 * being bus 0.
	 */

	i2c->adap.nr = pdata->bus_num;

	ret = i2c_add_numbered_adapter(&i2c->adap);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to add bus to i2c core\n");
		goto err_cpufreq;
	}

	platform_set_drvdata(pdev, i2c);

	clk_disable(i2c->clk);

	dev_info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev));
	return 0;

 err_cpufreq:
	s3c24xx_i2c_deregister_cpufreq(i2c);

 err_irq:
	free_irq(i2c->irq, i2c);

 err_iomap:
	iounmap(i2c->regs);

 err_ioarea:
	release_resource(i2c->ioarea);
	kfree(i2c->ioarea);

 err_clk:
	clk_disable(i2c->clk);
	clk_put(i2c->clk);

 err_noclk:
	kfree(i2c);
	return ret;
}

(1)填充一個i2c_adapter結構體,并且呼叫介面去注冊之\

struct s3c24xx_i2c {
	spinlock_t		lock;
	wait_queue_head_t	wait;
	unsigned int		suspended:1;

	struct i2c_msg		*msg;
	unsigned int		msg_num;
	unsigned int		msg_idx;
	unsigned int		msg_ptr;

	unsigned int		tx_setup;
	unsigned int		irq;

	enum s3c24xx_i2c_state	state;
	unsigned long		clkrate;

	void __iomem		*regs;
	struct clk		*clk;
	struct device		*dev;
	struct resource		*ioarea;
	struct i2c_adapter	adap;//該成員是我們所需要的

#ifdef CONFIG_CPU_FREQ
	struct notifier_block	freq_transition;
#endif
};
//定義一個包含i2c_adapter結構體的結構體
struct s3c24xx_i2c *i2c;

//進行填充
strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));
i2c->adap.owner   = THIS_MODULE;
i2c->adap.algo    = &s3c24xx_i2c_algorithm;//演算法,即通信時序
i2c->adap.retries = 2;
i2c->adap.class   = I2C_CLASS_HWMON | I2C_CLASS_SPD;
pdata = pdev->dev.platform_data;
if (!pdata) {
	dev_err(&pdev->dev, "no platform data\n");
	return -EINVAL;
}
platform_data是我們從mach-x210.c檔案中放進去的:
static void __init smdkc110_machine_init(void)
	/* i2c */
	s3c_i2c0_set_platdata(NULL);
	s3c_i2c1_set_platdata(NULL);
	s3c_i2c2_set_platdata(NULL);
	
	void __init s3c_i2c0_set_platdata(struct s3c2410_platform_i2c *pd)
	{
		struct s3c2410_platform_i2c *npd;

		if (!pd)
			pd = &default_i2c_data0;

		npd = kmemdup(pd, sizeof(struct s3c2410_platform_i2c), GFP_KERNEL);
		if (!npd)
			printk(KERN_ERR "%s: no memory for platform data\n", __func__);
		else if (!npd->cfg_gpio)
			npd->cfg_gpio = s3c_i2c0_cfg_gpio;

		s3c_device_i2c0.dev.platform_data = npd;
	}

static struct s3c2410_platform_i2c default_i2c_data0 __initdata = {
	.flags		= 0,
	.slave_addr	= 0x10,
	.frequency	= 400*1000,
	.sda_delay	= S3C2410_IICLC_SDA_DELAY15 | S3C2410_IICLC_FILTER_ON,
};	

(2)從platform_device接收硬體資訊,做必要的處理(request_mem_region & ioremap、request_irq等)

	//讀出相關暫存器所對應的物理地址
	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (res == NULL) {
		dev_err(&pdev->dev, "cannot find IO resource\n");
		ret = -ENOENT;
		goto err_clk;
	}

	//動態獲取記憶體進行動態映射
	i2c->ioarea = request_mem_region(res->start, resource_size(res),
					 pdev->name);

	if (i2c->ioarea == NULL) {
		dev_err(&pdev->dev, "cannot request IO\n");
		ret = -ENXIO;
		goto err_clk;
	}

	//建立映射,整體(多個暫存器一塊)進行映射
	i2c->regs = ioremap(res->start, resource_size(res));

	if (i2c->regs == NULL) {
		dev_err(&pdev->dev, "cannot map IO\n");
		ret = -ENXIO;
		goto err_ioarea;
	}

(3)對硬體做初始化(直接操作210內部I2C控制器的暫存器),iic控制器相關的暫存器很多都是8位的

ret = s3c24xx_i2c_init(i2c);

static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c)
{
	unsigned long iicon = S3C2410_IICCON_IRQEN | S3C2410_IICCON_ACKEN;
	struct s3c2410_platform_i2c *pdata;
	unsigned int freq;

	/* get the plafrom data */

	pdata = i2c->dev->platform_data;

	/* inititalise the gpio */

	if (pdata->cfg_gpio)
		pdata->cfg_gpio(to_platform_device(i2c->dev));

	/* write slave address */

	writeb(pdata->slave_addr, i2c->regs + S3C2410_IICADD);//writeb操作8位的暫存器
	//列印除錯資訊,若定義了debug宏,則可以列印出資訊
	dev_dbg(i2c->dev, "slave address 0x%02x\n", pdata->slave_addr);

	writel(iicon, i2c->regs + S3C2410_IICCON);//writel操作32位的暫存器

	/* we need to work out the divisors for the clock... */

	if (s3c24xx_i2c_clockrate(i2c, &freq) != 0) {
		writel(0, i2c->regs + S3C2410_IICCON);
		dev_err(i2c->dev, "cannot meet bus frequency required\n");
		return -EINVAL;
	}

	/* todo - check that the i2c lines aren't being dragged anywhere */

	dev_dbg(i2c->dev, "bus frequency set to %d KHz\n", freq);
	dev_dbg(i2c->dev, "S3C2410_IICCON=0x%02lx\n", iicon);

	dev_dbg(i2c->dev, "S3C2440_IICLC=%08x\n", pdata->sda_delay);
	writel(pdata->sda_delay, i2c->regs + S3C2440_IICLC);

	return 0;
}

9、i2c_algorithm

函式呼叫思路:
s3c24xx_i2c_probe
	i2c->adap.algo    = &s3c24xx_i2c_algorithm;
static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
	.master_xfer		= s3c24xx_i2c_xfer,//負責操作iic主控對外的一些資料傳輸
	.functionality		= s3c24xx_i2c_func,//功能描述
};
struct i2c_algorithm {
	/* If an adapter algorithm can't do I2C-level access, set master_xfer
	   to NULL. If an adapter algorithm can do SMBus access, set
	   smbus_xfer. If set to NULL, the SMBus protocol is simulated
	   using common I2C messages */
	/* master_xfer should return the number of messages successfully
	   processed, or a negative value on error */
	int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
			   int num);
	//smbus相關的		   
	int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
			   unsigned short flags, char read_write,
			   u8 command, int size, union i2c_smbus_data *data);

	/* To determine what the adapter supports */
	u32 (*functionality) (struct i2c_adapter *);//當前的iic支持的一下功能
	/*支持的功能去閱讀kernel/include/linux/i2c.h檔案,520行開始的代碼,
	若想詳細研究需閱讀iic規范*/
};
static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
			struct i2c_msg *msgs, int num)
{
	struct s3c24xx_i2c *i2c = (struct s3c24xx_i2c *)adap->algo_data;
	int retry;
	int ret;

	clk_enable(i2c->clk);

	for (retry = 0; retry < adap->retries; retry++) {//重試,若一次未連接上,
										//會重復幾次嘗試連接
		ret = s3c24xx_i2c_doxfer(i2c, msgs, num);

		if (ret != -EAGAIN)
			goto out;

		dev_dbg(i2c->dev, "Retrying transmission (%d)\n", retry);

		udelay(200);
	}
	ret = -EREMOTEIO;
out:
	clk_disable(i2c->clk);

	return ret;
}

static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c,
			      struct i2c_msg *msgs, int num)
{
	unsigned long timeout;
	int ret;

	if (i2c->suspended)
		return -EIO;

	ret = s3c24xx_i2c_set_master(i2c);//將iic控制器設定為主機模式
	if (ret != 0) {
		dev_err(i2c->dev, "cannot get bus (error %d)\n", ret);
		ret = -EAGAIN;
		goto out;
	}

	spin_lock_irq(&i2c->lock);

	i2c->msg     = msgs;//發送的訊息
	i2c->msg_num = num;//發送的訊息數量
	i2c->msg_ptr = 0;
	i2c->msg_idx = 0;
	i2c->state   = STATE_START;//當前iic在傳輸程序中處于那種狀態

	s3c24xx_i2c_enable_irq(i2c);//打開中斷
	s3c24xx_i2c_message_start(i2c, msgs);//向從機發送起始信號
	spin_unlock_irq(&i2c->lock);
	
	//進入休眠,讓出CPU,主機等待從機的回復,因為從機的速度可能比較慢,同時也可提高
	//效率,其在iic申請(s3c24xx_i2c_probe函式中)的中斷的中斷處理程式s3c24xx_i2c_irq
	//中被喚醒

	timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);

	ret = i2c->msg_idx;

	/* having these next two as dev_err() makes life very
	 * noisy when doing an i2cdetect */

	if (timeout == 0)
		dev_dbg(i2c->dev, "timeout\n");
	else if (ret != num)
		dev_dbg(i2c->dev, "incomplete xfer (%d)\n", ret);

	/* ensure the stop has been through the bus */

	udelay(10);

 out:
	return ret;
}
static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id)
{
	struct s3c24xx_i2c *i2c = dev_id;
	unsigned long status;
	unsigned long tmp;

	status = readl(i2c->regs + S3C2410_IICSTAT);//讀取狀態暫存器

	if (status & S3C2410_IICSTAT_ARBITR) {
		/* deal with arbitration loss */
		dev_err(i2c->dev, "deal with arbitration loss\n");
	}

	if (i2c->state == STATE_IDLE) {
		dev_dbg(i2c->dev, "IRQ: error i2c->state == IDLE\n");

		tmp = readl(i2c->regs + S3C2410_IICCON);
		tmp &= ~S3C2410_IICCON_IRQPEND;
		writel(tmp, i2c->regs +  S3C2410_IICCON);
		goto out;
	}

	/* pretty much this leaves us with the fact that we've
	 * transmitted or received whatever byte we last sent */

	i2c_s3c_irq_nextbyte(i2c, status);//在該函式中喚醒

 out:
	return IRQ_HANDLED;
}

10、i2c_driver的注冊

(1)以gslX680觸摸屏的驅動為例
使用檔案鏈接:鏈接:https://pan.baidu.com/s/1rKDNBwC5vv_25b8gcsxpeg
提取碼:x1ts --來自百度網盤超級會員V5的分享

(2)將驅動添加到內核SI專案中
??將gslX680.c、gslX680.h該觸摸屏的驅動檔案放在kernel/drivers/input/touchscreen

static int __init gsl_ts_init(void)
{
    int ret;
	print_info("==gsl_ts_init==\n");
	ret = i2c_add_driver(&gsl_ts_driver);
	print_info("ret=%d\n",ret);
	return ret;
}
static void __exit gsl_ts_exit(void)
{
	print_info("==gsl_ts_exit==\n");
	i2c_del_driver(&gsl_ts_driver);
	return;
}

(3)i2c_driver的基本分析:name和probe

static inline int i2c_add_driver(struct i2c_driver *driver)
{
	return i2c_register_driver(THIS_MODULE, driver);
}
static const struct i2c_device_id gsl_ts_id[] = {
	{GSLX680_I2C_NAME, 0},
	{}
};//使用其去進行driver和device的匹配
static struct i2c_driver gsl_ts_driver = {
	.driver = {
		.name = GSLX680_I2C_NAME,
		.owner = THIS_MODULE,
	},
#ifndef CONFIG_HAS_EARLYSUSPEND
	.suspend	= gsl_ts_suspend,
	.resume	= gsl_ts_resume,
#endif
	.probe		= gsl_ts_probe,
	.remove		= __devexit_p(gsl_ts_remove),
	.id_table	= gsl_ts_id,
};

進行匹配的主要是i2c-core.c中的i2c_device_match函式:
kernel/drivers/i2c/i2c-core.c65行

static int i2c_device_match(struct device *dev, struct device_driver *drv)
		if (driver->id_table)
			return i2c_match_id(driver->id_table, client) != NULL;
直接選用了.id_table	= gsl_ts_id中的.name進行匹配

13、i2c_client從哪里來
(1)直接來源:i2c_register_board_info

kernel/arch/arm/mach-s5pv210/mach-x210.c
	smdkc110_machine_init  2090行附近
		i2c_register_board_info
		
注冊了三個:
	i2c_register_board_info(0, i2c_devs0, ARRAY_SIZE(i2c_devs0));
	i2c_register_board_info(1, i2c_devs1, ARRAY_SIZE(i2c_devs1));
	i2c_register_board_info(2, i2c_devs2, ARRAY_SIZE(i2c_devs2));

通過開發板原理圖可知LCD使用了i2c1

kernel/arch/arm/mach-s5pv210/mach-x210.c  1626/* I2C1 */
static struct i2c_board_info i2c_devs1[] __initdata = {
#ifdef CONFIG_VIDEO_TV20
	{
		I2C_BOARD_INFO("s5p_ddc", (0x74>>1)),
	},							//i2c設備的從地址
#endif
};
i2c設備的硬體資訊的封裝:
struct i2c_board_info {
	char		type[I2C_NAME_SIZE];			// 設備名
	unsigned short	flags;						// 屬性
	unsigned short	addr;						// 設備從地址
	void		*platform_data;					// 設備私有資料
	struct dev_archdata	*archdata;
#ifdef CONFIG_OF
	struct device_node *of_node;
#endif
	int		irq;		// 設備使用的IRQ號,對應CPU的EINT(按下觸摸
						//屏引發一個外部中斷通知CPU有資料來了)
//這個irq與adapter的irq不同,這個是iic控制器收到iic訊息產生的中斷,
//而這里的這個是210引腳的外部中斷,讓從設備通知主設備210
};
int __init i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned len)
{
	int status;

	down_write(&__i2c_board_lock);

	/* dynamic bus numbers will be assigned after the last static one */
	if (busnum >= __i2c_first_dynamic_bus_num)
		__i2c_first_dynamic_bus_num = busnum + 1;

	for (status = 0; len; len--, info++) {
		struct i2c_devinfo	*devinfo;

		devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);
		if (!devinfo) {
			pr_debug("i2c-core: can't register boardinfo!\n");
			status = -ENOMEM;
			break;
		}

		devinfo->busnum = busnum;
		devinfo->board_info = *info;
		list_add_tail(&devinfo->list, &__i2c_board_list);
	}

	up_write(&__i2c_board_lock);

	return status;
}

(2)實作原理分析
??內核維護一個鏈表 __i2c_board_list,這個鏈表上鏈接的是I2C總線上掛接的所有硬體設備的資訊結構體,也就是說這個鏈表維護的是一個struct i2c_board_info結構體鏈表,真正的需要的struct i2c_client在別的地方由__i2c_board_list鏈表中的各個節點內容來另外構建生成,

函式呼叫層次:
i2c_add_adapter/i2c_add_numbered_adapter//注冊adapter
	i2c_register_adapter//(iic配接器,主機控制器)的
		i2c_scan_static_board_info
			i2c_new_device//注冊client即device
				device_register

總結:I2C總線的i2c_client的提供是內核通過i2c_add_adapter/i2c_add_numbered_adapter介面呼叫時自動生成的,生成的原料是mach-x210.c中的i2c_register_board_info(1, i2c_devs1, ARRAY_SIZE(i2c_devs1));

(3)當gslx680的driver與device添加并匹配上后,最終目的就是:

static int __devinit gsl_ts_probe(struct i2c_client *client,
			const struct i2c_device_id *id)
{
	struct gsl_ts *ts;
	int rc;

	print_info("GSLX680 Enter %s\n", __func__);
	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
		dev_err(&client->dev, "I2C functionality not supported\n");
		return -ENODEV;
	}//檢查是否支持i2c功能
 
	ts = kzalloc(sizeof(*ts), GFP_KERNEL);
	if (!ts)
		return -ENOMEM;
	print_info("==kzalloc success=\n");

	ts->client = client;
	i2c_set_clientdata(client, ts);
	ts->device_id = id->driver_data;

	rc = gslX680_ts_init(client, ts);
	if (rc < 0) {
		dev_err(&client->dev, "GSLX680 init failed\n");
		goto error_mutex_destroy;
	}	

	gsl_client = client;
	
	gslX680_init();
	init_chip(ts->client);
	check_mem_data(ts->client);
	
	rc=  request_irq(client->irq, gsl_ts_irq, IRQF_TRIGGER_RISING, client->name, ts);
	if (rc < 0) {
		print_info( "gsl_probe: request irq failed\n");
		goto error_req_irq_fail;
	}

	/* create debug attribute */
	//rc = device_create_file(&ts->input->dev, &dev_attr_debug_enable);

#ifdef CONFIG_HAS_EARLYSUSPEND
	ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
	//ts->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB + 1;
	ts->early_suspend.suspend = gsl_ts_early_suspend;
	ts->early_suspend.resume = gsl_ts_late_resume;
	register_early_suspend(&ts->early_suspend);
#endif


#ifdef GSL_MONITOR
	print_info( "gsl_ts_probe () : queue gsl_monitor_workqueue\n");

	INIT_DELAYED_WORK(&gsl_monitor_work, gsl_monitor_worker);
	gsl_monitor_workqueue = create_singlethread_workqueue("gsl_monitor_workqueue");
	queue_delayed_work(gsl_monitor_workqueue, &gsl_monitor_work, 1000);
#endif

	print_info("[GSLX680] End %s\n", __func__);

	return 0;

//exit_set_irq_mode:	
error_req_irq_fail:
    free_irq(ts->irq, ts);	

error_mutex_destroy:
	input_free_device(ts->input);
	kfree(ts);
	return rc;
}

注:本資料大部分由朱老師物聯網大講堂課程筆記整理而來并且參考了部分他人博客的內容,如有侵權,聯系洗掉!水平有限,如有錯誤,歡迎各位在評論區交流,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/321433.html

標籤:其他

上一篇:樹莓派4B 使用Onenet 視頻能力

下一篇:STM32CubeMX基于HAL庫點亮LED燈

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more