15.平台总线专题4

平台总线的probe代码实现

1.在probe方法中注册设备号并设计一个全局的设备对象
在这里插入图片描述在这里插入图片描述在这里插入图片描述

2.初始化硬件
在这里插入图片描述在这里插入图片描述

3.释放资源
在这里插入图片描述

平台总线完成led设备的控制

实现fops

nt val;
	int ret;

	ret = copy_from_user(&val, buf, count);
	if(ret > 0)
	{
		printk("copy_from_user error\n");
		return -EFAULT;
	}

	if(val){ //亮
		writel(readl(samsung_led->reg_base + 4) | (0x3<<4) , samsung_led->reg_base+4);
	}else{
		writel(readl(samsung_led->reg_base + 4) & ~(0x3<<4) , samsung_led->reg_base+4);
	}

	return count;

}


int led_pdrv_open(struct inode *inode, struct file *filp)
{
	printk("-----%s------------\n", __FUNCTION__);
	return 0;

}
int led_pdrv_close(struct inode *inode, struct file *filp)
{
	printk("-----%s------------\n", __FUNCTION__);
	return 0;
}





const struct file_operations led_fops = {
	.open = led_pdrv_open,
	.release = led_pdrv_close,
	.write = led_pdrv_write,

};

完整代码

plat_led_pdev.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>


#define GPIO_REG_BASE 0x11400000

#define GPF3_CON GPIO_REG_BASE + 0x01E0
#define GPF3_SIZE  24

#define GPX1_CON  GPIO_REG_BASE + 0x0C20
#define GPX1_SIZE  24



// 一个设备可能有多个资源: 
struct resource led_res[] = {
	[0] = {
		.start = GPF3_CON,
		.end = GPF3_CON + GPF3_SIZE - 1, 
		.flags = IORESOURCE_MEM,
	},
	[1] = {
		.start = GPX1_CON,
		.end = GPX1_CON + GPX1_SIZE - 1, 
		.flags = IORESOURCE_MEM,
	},

	//有些设备也有中断资源 ,用于说明中断资源的使用,本驱动中是没有太多意义
	[2] = {
		.start =  67,
		.end = 67, 
		.flags = IORESOURCE_IRQ,
	},		

};



struct platform_device led_pdev = {
	.name = "exynos4412_led", // 用于做匹配  /sys/bus/platform/devices/exynos4412_led
	.id = -1,
	.num_resources = ARRAY_SIZE(led_res),
	.resource = led_res,
};



static int __init plat_led_dev_init(void)
{

	//注册一个platform_devices
	return platform_device_register(&led_pdev);
}

static void __exit plat_led_dev_exit(void)
{
	platform_device_unregister(&led_pdev);
}


module_init(plat_led_dev_init);
module_exit(plat_led_dev_exit);
MODULE_LICENSE("GPL");







plat_led_pdrv.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>

#include <asm/io.h>
#include <asm/uaccess.h>


//设计一个全局的设备对象
struct led_dev{
	int dev_major;
	struct class *cls;
	struct device *dev;
	struct resource *res; //获取到的内存资源
	void *reg_base; //表示物理地址映射之后的虚拟地址
};
struct led_dev *samsung_led;


ssize_t led_pdrv_write (struct file *filp, const char __user *buf, size_t count, loff_t *fpos)
{
	int val;
	int ret;

	ret = copy_from_user(&val, buf, count);
	if(ret > 0)
	{
		printk("copy_from_user error\n");
		return -EFAULT;
	}

	if(val){ //亮
		writel(readl(samsung_led->reg_base + 4) | (0x3<<4) , samsung_led->reg_base+4);
	}else{
		writel(readl(samsung_led->reg_base + 4) & ~(0x3<<4) , samsung_led->reg_base+4);
	}

	return count;

}


int led_pdrv_open(struct inode *inode, struct file *filp)
{
	printk("-----%s------------\n", __FUNCTION__);
	return 0;

}
int led_pdrv_close(struct inode *inode, struct file *filp)
{
	printk("-----%s------------\n", __FUNCTION__);
	return 0;
}





const struct file_operations led_fops = {
	.open = led_pdrv_open,
	.release = led_pdrv_close,
	.write = led_pdrv_write,

};

//申请资源,总线匹配成功后会自动调用probe方法
int led_pdrv_probe(struct platform_device *pdev)
{
	printk("-----%s------------\n", __FUNCTION__);
	int ret;

	samsung_led = kzalloc(sizeof(struct led_dev), GFP_KERNEL);
	if(samsung_led == NULL)
	{
		printk("kzalloc errorn\n");
		return -ENOMEM;
	}

	/*
		a,注册设备号,并且注册fops--为用户提供一个设备标示,同时提供文件操作io接口
		b, 创建设备节点
		c, 初始化硬件
					ioremap(地址);  //地址从pdev需要获取
					readl/writle();
		d,实现各种io接口: xxx_open, xxx_read, ..

	*/

	samsung_led->dev_major = register_chrdev(0, "led_drv", &led_fops);

	samsung_led->cls = class_create(THIS_MODULE, "led_new_cls");

	samsung_led->dev = device_create(samsung_led->cls, NULL, MKDEV(samsung_led->dev_major, 0),
							NULL, "led0");
	
	//获取资源
	// 参数1: 从哪个pdev中获取资源
	// 参数2:  资源类型
	// 参数3: 表示获取同种资源的第几个
	
	samsung_led->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	
	int irqno = platform_get_irq(pdev, 0);
	// 等同于platform_get_resource(pdev, IORESOURCE_IRQ, 0);
	printk("--------irqno = %d\n", irqno);

	samsung_led->reg_base = ioremap(samsung_led->res->start,  resource_size(samsung_led->res));

	
	//对寄存器进行配置--输出功能
	writel((readl(samsung_led->reg_base) & ~(0xff<<16))| (0x11<<16) , samsung_led->reg_base);

	return 0;
}
//释放资源
int led_pdrv_remove(struct platform_device *pdev)
{
	printk("-----%s------------\n", __FUNCTION__);

	iounmap(samsung_led->reg_base);
	device_destroy(samsung_led->cls, MKDEV(samsung_led->dev_major, 0));
	class_destroy(samsung_led->cls);

	unregister_chrdev(samsung_led->dev_major, "led_drv");
	
		
	kfree(samsung_led);
	
	return 0;
}
	

const struct platform_device_id led_id_table[] = {
		{"exynos4412_led", 0x4444},
		{"s5pv210_led", 0x2222},
		{"s3c2410_led", 0x3333},
		{"s3c6410_led", 0x3333},
};	

struct platform_driver led_pdrv = {
	.probe = led_pdrv_probe,
	.remove = led_pdrv_remove,
	.driver = {
			.name = "samsung_led_drv",
				//可以用于做匹配
				// /sys/bus/platform/drivers/samsung_led_drv
	},
	.id_table = led_id_table,
};


static int __init plat_led_pdrv_init(void)
{
	printk("-----%s------------\n", __FUNCTION__);
	//注册一个pdrv
	
	return platform_driver_register(&led_pdrv);
}


static void __exit plat_led_pdrv_exit(void)
{
	printk("-----%s------------\n", __FUNCTION__);
	platform_driver_unregister(&led_pdrv);

}



module_init(plat_led_pdrv_init);
module_exit(plat_led_pdrv_exit);
MODULE_LICENSE("GPL");


led_test.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>




int main(int argc, char *argv[])
{
	int fd;
	int on =0;
	
	fd = open("/dev/led0", O_RDWR);
	if(fd < 0)
	{
		perror("open");
		exit(1);
	}

	while(1)
	{
		on = 1;
		write(fd, &on, 4);
		sleep(1);

		on = 0;
		write(fd, &on, 4);
		sleep(1);
	}


	close(1);

	return 0;
}

Makefile

ROOTFS_DIR = /opt/4412/rootfs
MODULE_NAME = plat_led_pdev
MODULE_NAME2 = plat_led_pdrv
APP_NAME = led_test

CROSS_COMPILE = /home/george/Linux_4412/toolchain/gcc-4.6.4/bin/arm-none-linux-gnueabi-
CC = $(CROSS_COMPILE)gcc

ifeq ($(KERNELRELEASE), )
KERNEL_DIR = /home/george/Linux_4412/kernel/linux-3.14
CUR_DIR = $(shell pwd)
all :
	$(CC) $(MODULE_NAME).c -o $(MODULE_NAME)
	make -C $(KERNEL_DIR) M=$(CUR_DIR) modules
clean :
	make -C $(KERNEL_DIR) M=$(CUR_DIR) clean

install:
	cp -raf *.ko $(APP_NAME)  $(ROOTFS_DIR)/drv_module
	
else

obj-m += $(MODULE_NAME).o
obj-m += $(MODULE_NAME2).o
#obj-m += $(MODULE_NAME3).o

endif

需求

利用互联网的思想,通过编写客户端和服务端,开发板上运行服务端,可以直接控制led灯,并且等待客户端端的连接,客户端通过
socket连接到服务端,通过发送指令来远程控制服务端的led灯
server.c

#include "net.h"

//定义在net.h中,统一管理fd
struct fds *myfd;

void cli_data_handle (void *arg);

int main (void)
{
	myfd = malloc(sizeof(struct fds));
	if (myfd == NULL)
		return -1;

	//打开设备led0
	myfd->openfd = open("/dev/led0", O_RDWR);
	if(myfd->openfd < 0)
	{
		perror("open");
		exit(1);
	}
	
	myfd->socketfd = -1;
	struct sockaddr_in sin;

	/* 1. 创建socket fd */
	if ((myfd->socketfd = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
		perror ("socket");
		exit (1);
	}

	/* 允许绑定地址快速重用 */
	int b_reuse = 1;
	setsockopt (myfd->socketfd, SOL_SOCKET, SO_REUSEADDR, &b_reuse, sizeof (int));


	/* 绑定 */
	/*填充struct sockaddr_in结构体变量 */
	bzero (&sin, sizeof (sin));
	sin.sin_family = AF_INET;
	sin.sin_port = htons (SERV_PORT);	//网络字节序的端口号

	/* 让服务器程序能绑定在任意的IP上 */
	sin.sin_addr.s_addr = htonl (INADDR_ANY);
	/*2.2 绑定 */
	if (bind (myfd->socketfd, (struct sockaddr *) &sin, sizeof (sin)) < 0) {
		perror ("bind");
		exit (1);
	}

	/* 调用listen()把主动套接字变成被动套接字 */
	if (listen (myfd->socketfd, BACKLOG) < 0) {
		perror ("listen");
		exit (1);
	}
	printf ("Server starting....OK!\n");

	myfd->acceptfd = -1;
	/* 阻塞等待客户端连接请求 */

/*  用多线程处理已经建立号连接的客户端数据 */
	pthread_t tid;

	struct sockaddr_in cin;
	socklen_t addrlen = sizeof (cin);

	while (1) {
		if ((myfd->acceptfd = accept (myfd->socketfd, (struct sockaddr *) &cin, &addrlen)) < 0) {
			perror ("accept");
			exit (1);
		}

		char ipv4_addr[16];
		if (!inet_ntop (AF_INET, (void *) &cin.sin_addr, ipv4_addr, sizeof (cin))) {
			perror ("inet_ntop");
			exit (1);
		}

		printf ("Clinet(%s:%d) is connected!\n", ipv4_addr, htons (cin.sin_port));

		pthread_create (&tid, NULL, (void *) cli_data_handle, (void *) &myfd->acceptfd);
	}

	close (myfd->socketfd);
	close(myfd->openfd);
	return 0;
}

void cli_data_handle (void *arg)
{
	printf ("handler thread: acceptfd =%d\n", myfd->acceptfd);

	//..和acceptfd进行数据读写
	int ret = -1;
	int command;
	while (1) {
		bzero (&command, sizeof(int));
		do {
			ret = read (myfd->acceptfd, &command, sizeof(int));
		} while (ret < 0 && EINTR == errno);
		if (ret < 0) {

			perror ("read");
			exit (1);
		}
		if (!ret) {				//对方已经关闭
			break;
		}
		printf ("Receive data: %d\n", command);
		//控制灯,非零亮,零灭
		write(myfd->openfd, &command, 4);
		
		if (command < 0) {	//用户输入了小于0的数退出
			printf ("Client(fd=%d) is exiting!\n", myfd->acceptfd);
			break;
		}
	}
	close (myfd->acceptfd);

}


client.c


/*./client serv_ip serv_port */
#include "net.h"

//定义在net.h中,统一管理fd
struct fds *myfd;

void usage (char *s)
{
	printf ("\n%s serv_ip serv_port", s);
	printf ("\n\t serv_ip: server ip address");
	printf ("\n\t serv_port: server port(>5000)\n\n");
}

int main (int argc, char **argv)
{
	myfd = malloc(sizeof(struct fds));
	if (myfd == NULL)
		return -1;
	
	myfd->socketfd = -1;

	int port = -1;
	struct sockaddr_in sin;

	if (argc != 3) {
		usage (argv[0]);
		exit (1);
	}
	/* 1. 创建socket fd */
	if ((myfd->socketfd = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
		perror ("socket");
		exit (1);
	}

	port = atoi (argv[2]);
	if (port < 5000) {
		usage (argv[0]);
		exit (1);
	}
	/*2.连接服务器 */

	/*2.1 填充struct sockaddr_in结构体变量 */
	bzero (&sin, sizeof (sin));

	sin.sin_family = AF_INET;
	sin.sin_port = htons (port);	//网络字节序的端口号

	if (inet_pton (AF_INET, argv[1], (void *) &sin.sin_addr) != 1) {
		perror ("inet_pton");
		exit (1);
	}

	if (connect (myfd->socketfd, (struct sockaddr *) &sin, sizeof (sin)) < 0) {
		perror ("connect");
		exit (1);
	}

	printf ("Client starting...OK!\n");
	/*3. 读写数据 */
	int command;
	int ret = -1;
	while (1) {
		bzero (&command, sizeof(int));
		printf("input command:");
		scanf("%d", &command);
		printf("command = %d\n", command);
		if (command == EOF) {
			continue;
		}
		
		do {
			ret = write (myfd->socketfd, &command, sizeof(int));
		} while (ret < 0 && EINTR == errno);

		if (command < 0) {	//用户输入了小于0的数退出
			printf ("Client is exiting!\n");
			break;
		}
	}

	/*4.关闭套接字 */
	close (myfd->socketfd);
}


net.h

#ifndef __MAKEU_NET_H__
#define __MAKEU_NET_H__

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/types.h>			/* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>			/* superset of previous */
#include <pthread.h>
#include <sys/stat.h>
#include <fcntl.h>


#define SERV_PORT 5001
#define SERV_IP_ADDR "192.168.18.100"
#define BACKLOG 5
//统一管理fd
struct fds
{
	int socketfd;
	int acceptfd;
	int openfd;
};

#endif


Makefile

ROOTFS_DIR = /opt/4412/rootfs
MODULE_NAME = plat_led_pdev
MODULE_NAME2 = plat_led_pdrv
APP_NAME = plat_led_server

CROSS_COMPILE = arm-none-linux-gnueabi-
CC = $(CROSS_COMPILE)gcc

ifeq ($(KERNELRELEASE), )
KERNEL_DIR = /home/linux/linux-3.14.10
CUR_DIR = $(shell pwd)
all :
	$(CC) $(APP_NAME).c -lpthread -o $(APP_NAME)
	make -C $(KERNEL_DIR) M=$(CUR_DIR) modules
	
clean :
	make -C $(KERNEL_DIR) M=$(CUR_DIR) clean

install:
	cp -raf *.ko $(APP_NAME)  $(ROOTFS_DIR)/drv_module
	
else

obj-m += $(MODULE_NAME).o
obj-m += $(MODULE_NAME2).o

endif

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值