通过TCP/IP实现PC(客户端)远程控制开发板(服务器)上LED灯的实验
程序员文章站
2022-06-08 22:09:54
...
这篇我们来做个小实验,实现开发板上运行服务端,可以直接控制led灯,客户端通过socket连接到服务端,通过发送指令来远程控制服务端的led灯。
我们用平台总线的思想来编写驱动程序,这里分为LED平台驱动程序和LED平台设备程序。
先看看LED平台设备程序plat_led_pdev.c的代码:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
//led4、led5
#define GPF3_CON 0x114001e0
#define GPF3_SIZE 24
//led3
#define GPX1_CON 0x11000C20
#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",
.id= -1,
.num_resources = ARRAY_SIZE(led_res),
.resource = led_res,
};
static int __init plat_led_dev_init(void)
{
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");
再看看LED平台驱动程序plat_led_pdrv.c的代码:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/fs.h>
#include <linux/slab.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 *fops)
{
int ret;
int value;
ret = copy_from_user(&value, buf, count);
if(ret){
printk("copy_from_user error\n");
return -EFAULT;
}
if(value){
//数据寄存器置1
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 = {
.write = led_pdrv_write,
.open = led_pdrv_open,
.release = led_pdrv_close,
};
//实现初始化
int led_pdrv_probe(struct platform_device *pdev)
{
printk("---------%s---------\n",__FUNCTION__);
samsung_led = kzalloc(sizeof(struct led_dev),GFP_KERNEL);
if(samsung_led == NULL){
printk("kzalloc error\n");
return -ENOMEM;
}
samsung_led->dev_major = register_chrdev(0,"led_drv", &led_fops);
samsung_led->cls = class_create(THIS_MODULE,"led_cls");
samsung_led->dev = device_create(samsung_led->cls, NULL, MKDEV(samsung_led->dev_major, 0), NULL,"led0");
//获取资源
samsung_led->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
//地址映射
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)
{
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",0x3333},
{"e3c6410_led",0x2222},
};
struct platform_driver led_pdrv = {
.probe = led_pdrv_probe,
.remove = led_pdrv_remove,
.driver = {
.name = "samsung_led_drv",//可以用于做匹配,也可以不用于做匹配
},
.id_table = led_id_table,
};
static int __init plat_led_drv_init(void)
{
return platform_driver_register(&led_pdrv);
}
static void __exit plat_led_drv_exit(void)
{
platform_driver_unregister(&led_pdrv);
}
module_init(plat_led_drv_init);
module_exit(plat_led_drv_exit);
MODULE_LICENSE("GPL");
接着看服务器led_server.c的代码:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <fcntl.h>
#define SERVER_PORT 5001
int main(int argc,char **argv)
{
int fd;
int newfd;
int len;
int flag = 1;
//设备节点的pfd
int pfd;
int value;
struct sockaddr_in ipv4addr;
struct sockaddr_in clientaddr;
char ipaddr[16];
fd = socket(AF_INET, SOCK_STREAM, 0);
if(fd == -1){
printf("server socket error\n");
exit(1);
}
bzero(&ipv4addr,sizeof(ipv4addr));
ipv4addr.sin_family = AF_INET;
ipv4addr.sin_port = htons(SERVER_PORT);
ipv4addr.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(fd, (struct sockaddr*)&ipv4addr,sizeof(ipv4addr)) < 0){
perror("bind");
exit(1);
}
if(listen(fd,5) < 0){
perror("listen");
exit(1);
}
printf("server starting...\n");
//地址复用
if(setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&flag,sizeof(int))==-1)
{
perror("setsockopt");
exit(1);
}
len = sizeof(clientaddr);
newfd = accept(fd, (struct sockaddr*)&clientaddr,&len);
if(newfd == -1)
{
perror("accept");
exit(1);
}
//打印客户端的ip和port
if(!inet_ntop(AF_INET,(void*)&clientaddr.sin_addr.s_addr,ipaddr,sizeof(clientaddr))){
perror("inet_ntop");
exit(1);
}
printf("client ip:%s,port:%d is connected\n",ipaddr,ntohs(clientaddr.sin_port));
pfd = open("/dev/led0",O_RDWR);
if(pfd < 0){
perror("open");
exit(1);
}
while(1){
//读取客户端通过socket发来的数据
read(newfd,&value,4);
if(value == 1 || value ==0)
//间接控制设备
write(pfd,&value,4);
else{
printf("client quiting...\n");
break;
}
}
close(newfd);
close(fd);
close(pfd);
return 0;
}
最后看客户端led_client.c的代码:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <fcntl.h>
#define SERVER_PORT 5001
//开发板ip地址
#define SERVER_IP "192.168.1.100"
int main(int argc,char **argv)
{
int fd;
int value;
struct sockaddr_in ipv4addr;
fd = socket(AF_INET, SOCK_STREAM, 0);
if(fd == -1){
printf("client socket error\n");
exit(1);
}
bzero(&ipv4addr,sizeof(ipv4addr));
ipv4addr.sin_family = AF_INET;
ipv4addr.sin_port = htons(SERVER_PORT);
if(inet_pton(AF_INET,SERVER_IP,(void *)&ipv4addr.sin_addr.s_addr) != 1){
perror("client inet_pton");
exit(1);
}
printf("client starting...\n");
if(connect(fd,(struct sockaddr *)&ipv4addr,sizeof(ipv4addr)) < 0){
perror("connect");
exit(1);
}
while(1){
//1代表点亮led,0代表熄灭led,其他数字代表客户端退出程序
printf("please select to led on/off(1:on/0:off/other:quit)");
scanf("%d",&value);
if(value == 1 || value == 0)
write(fd,&value,4);
else{
write(fd,&value,4);
printf("client quiting...\n");
break;
}
//吸收垃圾字符
getchar();
}
close(fd);
return 0;
}
加载驱动并在开发板上运行服务器程序:
这里我直接在ubuntu上运行客户端,此时服务器已经连接上客户端,ubuntu上运行客户端如下:
我们在客户端上输入1,代表点亮led,输入0代表熄灭led,其他字符代表客户端退出。这里我们仅操作led4和led5
看看开发板的led情况,输入1时:
输入0时:
上一篇: Apache的fork模式和worker模式判断方法
下一篇: 整理的9个实用的PHP库简介和下载