tiny4412 驱动 (7)led

本文承接hello驱动的模板, 这里先看下原理图和硬件操作方法

原理图:

tiny4412 驱动 (7)led_第1张图片

对应的GPIO:

可知led1~4分别对应的GPIO是GPM4_0, GPM4_1,GPM4_2,GPM4_3,从datasheet可以抽象为如下结构

/*
* resource
*
* led1   gpm4 0
* led2   gpm4 1
* led3   gpm4 2
* led4   gpm4 3
*
* Note(s) : here use 'led1'
*/

struct _gpio
{
	unsigned long con;          /* GPM0 configuration register */
	unsigned long data;         /* GPM0 data register */
	unsigned long pud;          /* GPM0 pull-up/ pull-down register */
	unsigned long drv;          /* GPM0 drive strength control register */
	unsigned long conpdn;       /* GPM0 power down mode configuration register */
	unsigned long pudpdn;       /* GPM0 power down mode pull-up/ pull-down register 0x000 */
};

#define LED_GPIOM_CON_ADDR		0x110002E0

static struct _gpio *led_gpio = NULL;

地址映射

led_gpio = (struct _gpio *)ioremap(LED_GPIOM_CON_ADDR, 0x20);

完整代码:

/*
* led driver on linux-4.19.27(without dt)
* arm-linux-gcc-6.2.1
*/

#include 

#include 
#include 

#include 

#include 
#include 

#include 
#include 

#include 
#include    /* kmalloc, */
#include 

#include  /* copy_from_usr, */
#include        /* ioremap, */


#define DEV_NAME		"LED"

struct led_dev
{
	struct cdev *cdev;
	dev_t dev;
	int major;
	char name[32];
	struct class *led_class;
};


static struct led_dev *led_dev = NULL;

/*
* resource
*
* led1   gpm4 0
* led2   gpm4 1
* led3   gpm4 2
* led4   gpm4 3
*
* Note(s) : here use 'led1'
*/

struct _gpio
{
	unsigned long con;          /* GPM0 configuration register */
	unsigned long data;         /* GPM0 data register */
	unsigned long pud;          /* GPM0 pull-up/ pull-down register */
	unsigned long drv;          /* GPM0 drive strength control register */
	unsigned long conpdn;       /* GPM0 power down mode configuration register */
	unsigned long pudpdn;       /* GPM0 power down mode pull-up/ pull-down register 0x000 */
};

#define LED_GPIOM_CON_ADDR		0x110002E0

static struct _gpio *led_gpio = NULL;

static int led_open (struct inode *inode, struct file *file)
{
	printk(KERN_INFO "open \n");	

	/* set GPM4_0 as output */
	led_gpio->con |= (1 << 0);
	
	return 0;
}

static int led_close (struct inode *inode, struct file *file)
{
	printk(KERN_INFO "close \n");	
	
	return 0;
}

static ssize_t led_write (struct file *file, const char __user *usrbuf, size_t len, loff_t *off)
{
	char cmd,opt;
	char rev_buf[8] = {0};
	
	printk(KERN_INFO "write \n");		

	if(copy_from_user(rev_buf,usrbuf,8))
		return -EFAULT;

	cmd = rev_buf[0];
	opt = rev_buf[1];

	printk(KERN_NOTICE "cmd : %d opt : %d \n", cmd, opt);

	if(cmd == 0)
	{
		// off
		led_gpio->data |= (1<<0);
	}
	else if(cmd == 1)
	{
		// on
		led_gpio->data &= ~(1<<0);
	}
	else
	{
		// do nothing.
	}
	
	return 0;
}

static long led_ioctl (struct file *file, unsigned int cmd, unsigned long arg)
{
	printk(KERN_INFO "ioctl \n");	

	return 0;
}

const struct file_operations led_ops = 
{
	.owner = THIS_MODULE,
	.open = led_open,
	.release = led_close,
	.unlocked_ioctl = led_ioctl,
	.write = led_write,
};


static int led_drv_init(void)
{
	int ret = 0;
	
	printk(KERN_INFO "led drv init.\n");

	led_dev = kmalloc(sizeof(struct led_dev),GFP_KERNEL);
	if(!led_dev)
		return -ENOMEM;

	strcpy(led_dev->name, DEV_NAME);

	ret = alloc_chrdev_region(&led_dev->dev, 0, 1, led_dev->name);
	led_dev->major = MAJOR(led_dev->dev);

	if(ret < 0)
	{
		kfree(led_dev);
		return ret;
	}

	led_dev->cdev = cdev_alloc();
	if(!led_dev->cdev)
	{
		unregister_chrdev_region(led_dev->dev,1);
		kfree(led_dev);
		return -EFAULT;
	}
	cdev_init(led_dev->cdev,&led_ops);
	led_dev->cdev->owner = THIS_MODULE;
	led_dev->cdev->ops = &led_ops;
	cdev_add(led_dev->cdev,led_dev->dev,1);

	led_dev->led_class = class_create(THIS_MODULE,led_dev->name);
	ret = PTR_ERR(led_dev->led_class);
	if (IS_ERR(led_dev->led_class))
	{
		cdev_del(led_dev->cdev);
		unregister_chrdev_region(led_dev->dev,1);
		kfree(led_dev);
		return -EFAULT;
	}

	device_create(led_dev->led_class,NULL,led_dev->dev,NULL,led_dev->name,led_dev);

	led_gpio = (struct _gpio *)ioremap(LED_GPIOM_CON_ADDR, 0x20);
	
	return 0;
}


static void led_drv_exit(void)
{
	printk(KERN_INFO "led drv exit.\n");

	iounmap(led_gpio);

	device_destroy(led_dev->led_class, led_dev->dev);
	class_destroy(led_dev->led_class);
	cdev_del(led_dev->cdev);
	unregister_chrdev_region(led_dev->dev,1);
	kfree(led_dev);
}


module_init(led_drv_init);
module_exit(led_drv_exit);
MODULE_LICENSE("GPL");

Makefile

TARGET  := led

obj-m   += $(TARGET).o

ROOTFS = /home/flinn/tmp/rootfs
KERNEL = /home/flinn/tiny4412-SDK/linux-4.19.27

all:
        make -C $(KERNEL) M=`pwd` modules

clean:
        make -C $(KERNEL) M=`pwd` clean
        
install:
        #make -C $(KERNEL) M=`pwd` modules_install INSTALL_MOD_PATH=$(ROOTFS)
        sudo cp $(TARGET).ko $(ROOTFS)

此外,这里还写了一个工具:

driver_test_tool.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
 
 
static char device_name[32] = "/dev/xxx";
 
#define MAX_ARGS			8
 
static int cmd_args[MAX_ARGS];
 
static char *short_options = "c:o:hv";
static struct option long_options[] =
{
	{ "open",  		required_argument, 	NULL, 'o' },
	{ "close", 		required_argument, 	NULL, 'c' },
	{ "Help", 		no_argument, 		NULL, 'H' },
	{ "version", 	no_argument, 		NULL, 'V' },
	{ 0, 0, 0, 0 }
};
 
 
static int g_help_flag = 0;
static int g_version_flag = 0;
 
static int major = 0;
static int minor = 1;
static void version()
{
	 printf("version-%d.%d \r\n", major, minor);
}
 
 
static void usage()
{
  fprintf(stderr,
	"Usage: driver_test_tool  [Device] [cmd] [data] ...\n\n"
	"Options:\n"
	"  -o  --open               open the device of offset, \n"
	"  -c  --close              close   		       \n"
 
    "  -h, --help               help information \n"
    "  -v, --version            version information \n"
    "example: \n"
    " driver_test_tool  %s  -o 0 1   (cmd 0, args : 1)  \n\n" , device_name);
}
 
 
static int drv_parse_cls(int argc , char **argv)
{
	char *dev_name = device_name;
	while(1)
	{
		int c;
 
    	c = getopt_long(argc,argv,short_options,long_options,NULL);
		if(c == -1)
      		break;
		switch(c)
		{
			case 'h':
			case 'H':
			case '?':
				g_help_flag = 1;
				return 0;
 
			case 'v':
				g_version_flag = 1;
				return 0;
 
			case 'o':
				sscanf(optarg,"%x",&cmd_args[0]);
				break;
 
			case 'c':
				sscanf(optarg,"%x",&cmd_args[0]);
				break;			
		}
	}
 
	if(optind >= argc)
 	{
    	printf("No device found \n");
    	return -1;
	}
 
	memcpy(dev_name, argv[optind], strlen(argv[optind]));
	++optind;
 
	int count = 1;
	while(optind < argc)
	{
		char *opt = argv[optind++];
 
		char *endptr;
		int cmd = strtol(opt,&endptr,16);
 
		if(*opt == '\0')
		{
			printf("Invalid command byte '%s'\n",opt);
			return -1;
		}
 
		if(count >= MAX_ARGS)
		{
			break;
		}
 
    	cmd_args[count] = cmd;
    	++count;
  }
 
  return 0;
}
 
int main(int argc , char **argv)
{
	int ret ;
	int fd = -1;
 
	ret = drv_parse_cls(argc, argv);
	if(ret != 0)
	{
		usage();
		return -1;
	}
 
	if(1 == g_help_flag)
	{
		usage();
		return 0;
	}
	else if(1 == g_version_flag)
	{
		version();
		return 0;
	}
 
	fd = open(device_name, O_RDWR);
	if(fd < 0)
	{
		printf("open %s fail . fd = %d \n", device_name, fd);
		return -1;
	}
 
	write(fd, cmd_args , MAX_ARGS);
 
	close(fd);
	
	return 0;
}
 

使用如下:

tiny4412 驱动 (7)led_第2张图片

例如:

./driver_test_tool /dev/LED -o 1 0      # 表示点亮LED

注意,对于driver_test_tool工具来说,使用arm-linux-gcc-4.3.2(低版本比较好)

你可能感兴趣的:(tiny4412,tiny4412)