利用互联网的思想,通过编写客户端和服务端,开发板上运行服务端,可以直接控制led灯,并且等待客户端端的连接,客户端通过
socket连接到服务端,通过发送指令来远程控制服务端的led灯
#include <linux/init.h> #include <linux/module.h> #include <linux/platform_device.h> #define GPIO_BASE_ADDRESS 0x11000000 #define GPX1_CON 0x0C20 #define GPX1_SIZE 24 #define GPX2_CON 0x0C40 #define GPX2_SIZE 24 #define GPF3_CON 0x01E0 #define GPX3_SIZE 24 struct resource led_resource[] = { [0] = { .start = GPIO_BASE_ADDRESS + GPX2_CON, .end = GPIO_BASE_ADDRESS + GPX2_CON + GPX2_SIZE - 1, .name = "exynos4412_dev_led2", .flags = IORESOURCE_MEM, }, [1] = { .start = GPIO_BASE_ADDRESS + GPX1_CON, .end = GPIO_BASE_ADDRESS + GPX1_CON + GPX1_SIZE - 1, .name = "exynos4412_dev_led3", .flags = IORESOURCE_MEM, }, //中断资源描述,这里只是举例,在实际led驱动中并没有使用到该中断 [2] = { .start = 73, //irq中断号 .end = 73, .name = "exynos4412_dev_irq0", .flags = IORESOURCE_IRQ, }, }; struct platform_device dev_led = { .name = "exynos4412_dev_led", .id = -1, .num_resources = ARRAY_SIZE(led_resource), .resource = led_resource, }; static int __init led_dev_init(void) { return platform_device_register(&dev_led); } static void __exit led_dev_exit(void) { platform_device_unregister(&dev_led); } module_init(led_dev_init); module_exit(led_dev_exit); MODULE_LICENSE("GPL");
#include <linux/init.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/mod_devicetable.h> #include <linux/uaccess.h> #include <linux/io.h> #include <linux/fs.h> #include <linux/slab.h> struct led_drv_info { unsigned int major; struct class* pdrv_class; struct device * pdrv_device; struct resource * pdrv_resource; void* reg_base; //表示物理地址映射之后的虚拟地址 }; struct led_drv_info* pdrvinfo; int fops_open (struct inode * inode, struct file * filp) { printk("--------%s----------\n",__FUNCTION__); return 0; } int fops_release (struct inode * inode, struct file * filp) { printk("--------%s----------\n",__FUNCTION__); return 0; } ssize_t fops_read (struct file * filp, char __user * buf, size_t count, loff_t * fpos) { printk("--------%s----------\n",__FUNCTION__); return count; } ssize_t fops_write (struct file * filp, const char __user * buf, size_t count, loff_t * fpos) { int buf_write,ret; printk("--------%s----------\n",__FUNCTION__); ret = copy_from_user(&buf_write, buf, count); if(0 != ret) { printk("KERN:Error to read\n"); return -EFAULT; } else { if(0 == buf_write) { writel(readl(pdrvinfo->reg_base + 4) & ~(0x1<<7) , pdrvinfo->reg_base + 4); } else { writel(readl(pdrvinfo->reg_base + 4) | (0x1<<7) , pdrvinfo->reg_base + 4); } } return count; } struct file_operations fops = { .read = fops_read, .write = fops_write, .open = fops_open, .release = fops_release, }; int exynos4412_led_drv_probe(struct platform_device *pdev) //匹配成功之后被调用的函数 { /* 注册完毕,同时如果和pdev匹配成功,自动调用probe方法: probe方法: 对硬件进行操作 a,注册设备号,并且注册fops--为用户提供一个设备标示,同时提供文件操作io接口 b, 创建设备节点 c, 初始化硬件 ioremap(地址); //地址从pdev需要获取 readl/writle(); d,实现各种io接口: xxx_open, xxx_read, .. */ int irqno; printk("--------%s----------\n",__FUNCTION__); pdrvinfo = (struct led_drv_info*)kzalloc(sizeof(struct led_drv_info), GFP_KERNEL); if(NULL == pdrvinfo) { printk("kzalloc Error\n"); return -ENOMEM; } pdrvinfo->major = register_chrdev(0, "led_drv", &fops); pdrvinfo->pdrv_class = class_create(THIS_MODULE, "my_led_cls"); pdrvinfo->pdrv_device = device_create(pdrvinfo->pdrv_class, NULL, MKDEV(pdrvinfo->major,0), NULL, "LED0"); //获取资源 //参数1:从哪哥pdev中获取资源 //参数2:资源类型 //参数3:表示获取同种资源的第几个(从0开始计数)种类一般分IORESOURCE_MEM 和 IORESOURCE_IRQ 可能还有其他种类 本实验没有涉及到 pdrvinfo->pdrv_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); //获取第0位的IORESOURCE_MEM //pdrvinfo->pdrv_resource = platform_get_resource(pdev, IORESOURCE_MEM, 1); //获取第1位的IORESOURCE_MEM //pdrvinfo->pdrv_resource = platform_get_resource(pdev, IORESOURCE_IRQ, 0); //获取第0位的IORESOURCE_IRQ irqno = platform_get_irq(pdev, 0); //获取第0位的IORESOURCE_IRQ 和上面的写法等同 printk("---------irqno = %d------------\n",irqno); pdrvinfo->reg_base = ioremap(pdrvinfo->pdrv_resource->start,resource_size(pdrvinfo->pdrv_resource)); // resource_size 等效于 pdrvinfo->pdrv_resource->end - pdrvinfo->pdrv_resource->start + 1 //对寄存器进行配置 writel((readl((unsigned int*)pdrvinfo->reg_base) & ~(0xf<<28)) | (0x1<<28),pdrvinfo->reg_base); return 0; } int exynos4412_led_drv_remove(struct platform_device *pdev)//device移除的时候调用的函数 { printk("--------%s----------\n",__FUNCTION__); iounmap(pdrvinfo->reg_base); device_destroy(pdrvinfo->pdrv_class, MKDEV(pdrvinfo->major,0)); class_destroy(pdrvinfo->pdrv_class); unregister_chrdev(pdrvinfo->major, "led_drv"); kfree((void*)pdrvinfo); return 0; } struct platform_device_id id_table[] = { //id列表 将驱动支持的设备都列出来 [0] = { .name = "exynos4412_dev_led", //用于匹配dev的名字 .driver_data = 0x1111, }, [1] = { .name = "exynos2410_dev_led", .driver_data = 0x2222, }, [2] = { .name = "exynos6410_dev_led", .driver_data = 0x3333, }, [3] = { .name = "exynos6412_dev_led", .driver_data = 0x4444, }, }; struct platform_driver pdrv = { .probe = exynos4412_led_drv_probe, .remove = exynos4412_led_drv_remove, .driver = { .name = "SAMSUNG_drv_led", //这个名字是在sys/bus/platform/drivers下显示的驱动名 //也可用于匹配dev 但是优先级低于下面的id_table }, .id_table = id_table, }; int __init exynos4412_led_drv_init(void) { printk("--------%s----------\n",__FUNCTION__); return platform_driver_register(&pdrv); } void __exit exynos4412_led_drv_exit(void) { printk("--------%s----------\n",__FUNCTION__); platform_driver_unregister(&pdrv); } module_init(exynos4412_led_drv_init); module_exit(exynos4412_led_drv_exit); MODULE_LICENSE("GPL");
#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <string.h> #include <sys/stat.h> #include <unistd.h> #include <errno.h> #include <signal.h> #include <fcntl.h> #include <sys/wait.h> #define SERV_PORT 5080 //端口号 根据需要可自行修改 #define SERV_IP_ADDR "192.168.1.160" //IP地址 根据需要可自行修改 void* func_recall(void* arg); void handler(int signo) { //自动回收子进程 防止僵尸进程 if (signo == SIGCHLD) { printf("Recall SIGINT\n"); waitpid(-1, NULL, WNOHANG); } } int main() { int fd = -1; int result = 0; struct sockaddr_in mysockaddr; struct sockaddr_in clientscokaddr; socklen_t clientaddrlen = sizeof(clientscokaddr); static pthread_t nptd; /* 1. 创建socket fd */ fd = socket(AF_INET, SOCK_STREAM, 0); if (fd < 0) { perror("socket"); return -1; } /*优化4: 允许绑定地址快速重用 */ int b_reuse = 1; setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &b_reuse, sizeof(int)); /*2. 绑定 */ /*2.1 填充struct sockaddr_in结构体变量 */ bzero((void*)&mysockaddr, sizeof(mysockaddr)); mysockaddr.sin_family = AF_INET; mysockaddr.sin_port = htons(SERV_PORT);//网络字节序的端口号 mysockaddr.sin_addr.s_addr = htonl(INADDR_ANY); /*2.2 绑定 */ result = bind(fd, (struct sockaddr*)&mysockaddr, sizeof(mysockaddr)); if (result != 0) { perror("bind"); return -1; } /*3. 调用listen()把主动套接字变成被动套接字 */ result = listen(fd, 5); if (result != 0) { perror("listen"); return -1; } /*4. 阻塞等待客户端连接请求 */ /*优化2:通过程序获取刚建立连接的socket的客户端的IP地址和端口号*/ int fd_client; pid_t pid; signal(SIGCHLD, handler); while (1) { bzero((void*)&clientscokaddr, sizeof(clientscokaddr)); fd_client = accept(fd, (struct sockaddr*)&clientscokaddr, &clientaddrlen); if (-1 == fd_client) { perror("accpet"); return 0; } char ipv4_addr[16]; if (!inet_ntop(AF_INET, (void*)&clientscokaddr.sin_addr, ipv4_addr, clientaddrlen)) { perror("inet_ntop"); return -1; } printf("Clinet(%s:%d) is connected!\n", ipv4_addr, ntohs(clientscokaddr.sin_port)); pid = fork(); if (pid < 0) { perror("fork"); return -1; } else if (pid > 0) { //父进程 close(fd_client); } else { //子进程 close(fd); func_recall((void*)&fd_client); //调用读写函数 return 0; } } close(fd); close(fd_client); return 0; } void* func_recall(void* arg) { int result = 0; int newfd = *(int*)arg; int fd,botton; printf("handler thread: newfd =%d\n", newfd); //..和newfd进行数据读写 char buf[BUFSIZ]; fd = open("/dev/LED0",O_RDWR); if(fd < 0) { perror("open"); return; } while (1) { bzero(buf, BUFSIZ); do { result = read(newfd, buf, BUFSIZ - 1); } while (result < 0 && EINTR == errno); if (result < 0) { perror("read"); break; } if (!result) break; //收到开启信息 if (!strcmp(buf, "open")) { botton = 1; write(fd, &botton, 4); } //收到关闭信息 else if(!strcmp(buf, "close")) { botton = 0; write(fd, &botton, 4); } //收到其他信息 else { printf("other message :%s\n", buf); } } close(newfd); close(fd); return 0; }
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#define SERV_PORT 5080
#define SERV_IP_ADDR "192.168.1.160"
int main()
{
int fd = -1;
int result = 0;
struct sockaddr_in mysockaddr;
char MESSAGE[32] = {0};
fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0) {
perror("socket");
return -1;
}
bzero((void*)&mysockaddr, sizeof(mysockaddr));
mysockaddr.sin_family = AF_INET;
mysockaddr.sin_port = htons(SERV_PORT);
#if 0
mysockaddr.sin_addr.s_addr = inet_addr(SERV_IP_ADDR);
#else
result = inet_pton(AF_INET, SERV_IP_ADDR, (void*)&mysockaddr.sin_addr);
if (result != 1) {
perror("inet_pton");
return -1;
}
#endif
result = connect(fd, (struct sockaddr*)&mysockaddr, sizeof(mysockaddr));
if (result != 0) {
perror("connect");
return -1;
}
printf("Client staring...OK!\n");
while (1) {
scanf("%s",MESSAGE);
do {
result = write(fd, (void*)MESSAGE, sizeof(MESSAGE));
} while (result < 0 && EINTR == errno);
}
close(fd);
return 0;
}
ROOTFS_DIR = /home/linux/Level11 APP_NAME = server CROSS_COMPILE = arm-none-linux-gnueabi- CC = $(CROSS_COMPILE)gcc ifeq ($(KERNELRELEASE), ) KERNEL_DIR = /home/linux/Level10/day6/linux-5.4.79 CUR_DIR = $(shell pwd) all : make -C $(KERNEL_DIR) M=$(CUR_DIR) modules -j6 ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi- $(CC) $(APP_NAME).c -o $(APP_NAME) clean : make -C $(KERNEL_DIR) M=$(CUR_DIR) clean rm $(APP_NAME) ./1/$(APP_NAME) clear install: cp -raf *.ko $(APP_NAME) ./1/ #cp -raf *.ko ./1/ else obj-m += exynos4412_led_dev.o obj-m += exynos4412_led_drv.o endif
linux远端 控制台信息:
linux@ubuntu:/mnt/hgfs/myproject/day15homework$ ./client Client staring...OK! close open close open 233333
开发板 控制台信息:
[root@farsight ]# insmod exynos4412_led_dev.ko [ 46.239501] exynos4412_led_dev: loading out-of-tree module taints kernel. [root@farsight ]# insmod exynos4412_led_drv.ko [ 177.116264] --------exynos4412_led_drv_init---------- [ 177.120229] --------exynos4412_led_drv_probe---------- [ 177.125658] ---------irqno = 73------------ [root@farsight ]# ./server Clinet(192.168.1.158:56826) is connected! handler thread: [ 183.952933] --------fops_open---------- newfd =4 [ 193.186926] --------fops_write---------- [ 194.157127] random: crng init done [ 199.872891] --------fops_write---------- [ 204.207945] --------fops_write---------- [ 207.134812] --------fops_write---------- other message :233333
综述:通过linux远端控制台输入open ,close 分别实现打开和关闭开发板的led灯,输入其他信息会打印在开发板的控制台上。