平台总线的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