添加内核驱动模块(3)(mydriver.c+ Konfig+Makefile )

之前在注册SPIDRIVER时,我们注册了SPI的probe函数和remove函数。
我们来编写这两个函数。

static int gpio_pmodoled_spi_probe(struct spi_device *spi) {
	int status = 0;
	struct gpio_pmodoled_device *gpio_pmodoled_dev;
	
	/* We rely on full duplex transfers, mostly to reduce
	 * per transfer overheads (by making few transfers).
	 */
	if (spi->master->flags & SPI_MASTER_HALF_DUPLEX) {
		status = -EINVAL;
		dev_err(&spi->dev, "SPI settings incorrect: %d\n", status);
		goto spi_err;
	}

	/* We must use SPI_MODE_0 */
	spi->mode = SPI_MODE_0;
	spi->bits_per_word = 8;

	status = spi_setup(spi);
	if(status < 0) {
		dev_err(&spi->dev, "needs SPI mode %02x, %d KHz; %d\n",
				spi->mode, spi->max_speed_hz / 1000,
				status);
		goto spi_err;
	}

	/* Get gpio_pmodoled_device structure */
	gpio_pmodoled_dev = (struct gpio_pmodoled_device*) spi->dev.platform_data;
	if(gpio_pmodoled_dev == NULL) {
		dev_err(&spi->dev, "Cannot get gpio_pmodoled_device.\n");
		status = -EINVAL;
		goto spi_platform_data_err;
	}
	
	printk(KERN_INFO SPI_DRIVER_NAME " [%s] SPI Probing\n", gpio_pmodoled_dev->name);

#ifdef CONFIG_PMODS_DEBUG
	printk(KERN_INFO SPI_DRIVER_NAME " [%s] spi_probe: setup char device\n", gpio_pmodoled_dev->name);
#endif
	
	/* Setup char driver */
	status = gpio_pmodoled_setup_cdev(gpio_pmodoled_dev, &(gpio_pmodoled_dev->dev_id), spi);	
	if (status) {
		dev_err(&spi->dev, "spi_probe: Error adding %s device: %d\n", SPI_DRIVER_NAME, status);
		goto cdev_add_err;
	}
	
	/* Initialize Mutex */
	mutex_init(&gpio_pmodoled_dev->mutex);

	/**
 	 * It is important to the OLED's longevity that the lines that 
 	 * control it's power are carefully controlled. This is a good
 	 * time to ensure that the device is ot turned on until it is
 	 * instructed to do so.
 	 */
#ifdef CONFIG_PMODS_DEBUG
	printk(KERN_INFO SPI_DRIVER_NAME " [%s] spi_probe: initialize device\n", gpio_pmodoled_dev->name);
#endif
	
	status = gpio_pmodoled_init_gpio(gpio_pmodoled_dev);
	if(status) {
		dev_err(&spi->dev, "spi_probe: Error initializing GPIO\n");
		goto oled_init_error;
	}

	gpio_pmodoled_disp_init(gpio_pmodoled_dev);

	memset(gpio_pmodoled_dev->disp_buf, 0x00, DISPLAY_BUF_SZ);

	status = screen_buf_to_display(gpio_pmodoled_dev->disp_buf, gpio_pmodoled_dev);
	if(status) {
		dev_err(&spi->dev, "spi_probe: Error sending initial Display String\n");
		goto oled_init_error;
	}
	return status;

oled_init_error:
	if (&gpio_pmodoled_dev->cdev)
		cdev_del(&gpio_pmodoled_dev->cdev);
cdev_add_err:
spi_platform_data_err:
spi_err:
	return(status);
}

当syscall调用该函数时,传递一个参数,它是一个struct spi_device型的指针spi。
定义一系列指针,后续作为句柄使用。

判断SPIMASTER的模式。
spi->master->flags & SPI_MASTER_HALF_DUPLEX
如果是SPI_MASTER_HALF_DUPLEX模式,那么不能使用,打印错误信息,然后进入退出处理栈spi_err。
如果不是SPI_MASTER_HALF_DUPLEX模式,那么可以使用,继续执行。

填充SPIDEVICE描述块的成员。
mode,bits_per_word。
设置SPIDEVICE的配置信息。
spi_setup()负责对一个SPIDEVICE描述块进行设置,并返回状态值。

判断返回的状态值。
如果非法,则打印错误信息,并进入退出处理栈spi_err
如果合法,则继续执行。

利用SPIDEVICE扎到对应的PMOD设备描述块。
将spi->dev.platform_data赋值给gpio_pmodoled_dev,它是一个struct gpio_pmodoled_device型的指针。关联到PMOD的设备资源后,后续可以用gpio_pmodoled_dev作为句柄。

判断gpio_pmodoled_dev指针的合法性。
如果为NULL,则非法。打印错误信息,然后修改status,后面返回值需要使用status。然后进入退出处理栈spi_platform_data_err。
如果不为NULL,则合法。那么继续执行。

配置CDEV设备资源。
gpio_pmodoled_setup_cdev()负责配置PMOD中内含的CDEV设备资源,并关联到SPIDEVICE上。返回状态值。

判断返回的状态值。
如果非法,则打印错误信息,并进入退出处理栈cdev_add_err。
如果合法,则继续执行。

初始化PMOD设备资源中的mutex系统资源。
mutex_init()负责初始化一个mutex描述块。

初始化PMOD设备资源中用到的GPIO系统资源。
gpio_pmodoled_init_gpio()负责初始化PMOD设备描述块中使用的GPIO系统资源。返回状态值。
判断返回的状态值的合法性。
如果非法,则打印错误信息,并进入退出处理栈oled_init_error。
如果合法,则继续执行。

初始化PMOD设备资源中用到的显示缓冲资源。
gpio_pmodoled_disp_init()负责初始化PMOD设备描述块中使用的显示资源。

初始化缓冲区。
memset()负责将缓冲区清零。

把初始化好的显示缓冲区刷到硬件上。
screen_buf_to_display()负责把缓冲区flush到硬件上。返回状态值。
判断返回的状态值的合法性。
如果非法,则打印错误信息,并进入退出处理栈oled_init_error。
如果合法,则继续执行。

至此,全部执行完。正常退出,不需要进入退出处理栈。所以直接return。

再编写remove函数。

static int __devexit gpio_pmodoled_spi_remove(struct spi_device *spi) 
{
	int status;
	struct gpio_pmodoled_device *dev;
	uint8_t wr_buf[10];

	dev = (struct gpio_pmodoled_device*) spi->dev.platform_data;

	if(dev == NULL) {
		dev_err(&spi->dev, "spi_remove: Error fetch gpio_pmodoled_device struct\n");
		return -EINVAL; 
	}

#ifdef CONFIG_PMODS_DEBUG
	printk(KERN_INFO SPI_DRIVER_NAME " [%s] spi_remove: Clearing Display\n", dev->name);
#endif	

	/* Clear Display */
	memset(dev->disp_buf, 0, DISPLAY_BUF_SZ);
	status = screen_buf_to_display(dev->disp_buf, dev);
	
	/* Turn off display */
	wr_buf[0] = OLED_DISPLAY_OFF;
	status = spi_write(spi, wr_buf, 1);
	if(status) {
		dev_err(&spi->dev, "oled_spi_remove: Error writing to SPI device\n");
	}
	
	/* Turn off VCC (VBAT) */
	gpio_set_value(dev->iVBAT, 1);
	msleep(100);
	/* TUrn off VDD Power */
	gpio_set_value(dev->iVDD, 1);
#ifdef CONFIG_PMODS_DEBUG
	printk(KERN_INFO SPI_DRIVER_NAME " [%s] spi_remove: Free GPIOs\n", dev->name);
#endif	
	
{	
	struct gpio gpio_pmodoled_ctrl[] = {
		{dev->iVBAT, GPIOF_OUT_INIT_HIGH, "OLED VBat"},
		{dev->iVDD, GPIOF_OUT_INIT_HIGH, "OLED VDD"},
		{dev->iRES, GPIOF_OUT_INIT_HIGH, "OLED_RESET"},
		{dev->iDC, GPIOF_OUT_INIT_HIGH, "OLED_D/C"},
	};

	gpio_free_array(gpio_pmodoled_ctrl, 4);
}
	
	if(&dev->cdev) {
#ifdef CONFIG_PMODS_DEBUG
		printk(KERN_INFO SPI_DRIVER_NAME " [%s] spi_remove: Destroy Char Device\n", dev->name);
#endif	
		device_destroy(gpio_pmodoled_class, dev->dev_id);
		cdev_del(&dev->cdev);
	}

	cur_minor--;
	
	printk(KERN_INFO SPI_DRIVER_NAME " [%s] spi_remove: Device Removed\n", dev->name);
	
	return status;
}

当syscall调用remove函数时,会传递一个参数,它是一个struct spi_device型的指针。
首先定义一系列指针,后续作为句柄使用。

从SPIDEVICE设备描述块中,找到PMOD的描述块。
dev = (struct gpio_pmodoled_device*) spi->dev.platform_data;
将 dev.platform_data赋值给dev,它是一个struct gpio_pmodoled_device型的指针。我们用dev指向了PMOD的描述块,后续用dev作为句柄。

判断dev的指针合法性。
如果为NULL,则非法。打印错误信息,并直接return.
如果不为NULL,则合法。继续执行。

初始化显示缓冲区,memset()完成。
将显示缓冲区的内容flush到硬件上。
screen_buf_to_display()负责将内容flush到硬件上。

配置wr_buf,然后用SPI发送缓冲区字符。
spi_write()负责写操作,它利用一个SPIDEVICE设备资源实现写操作时序。返回状态值。
判断返回的状态值。
如果非法,则打印错误信息。由于不是致命错误。所以打印错误信息后,并不进入退出处理栈。
如果合法,则继续执行。

设置GPIO。
gpio_set_value()负责将一个在系统中注册的合法GPIO编号,设置为配置值。
msleep(),睡眠等待。

定义一个临时数组gpio_pmodoled_ctrl,它是一个struct gpio型的数组。
同是设置多个GPIO的状态。
gpio_free_array()负责将一个GPIO_ARRAY中GPIO系统资源,同时释放掉。

判断PMOD的CDEV的指针合法性。
如果不为NULL,说明CDEV仍占有内核资源,那么释放掉CDEV的系统资源。
device_destroy()负责注销CLASS描述块。它根据dev_id注销CDEV关联的CLASS描述块。
cdev_del()负责注销CDEV设备资源。它根据传入的CDEV的描述块,在设备中进行匹配查找,然后注销掉系统资源,并取消内核和CDEV描述块之间的关联。

我们定义了静态变量cur_minor。它用来记录当前系统中存在的CDEV设备数量。
修改cur_minor。
cur_minor–;

至此,全部完成。直接return status.

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值