正在看韦东山的教程,他写了一个控制led灯的驱动。
我也写了一个。
总结:
1)copy_from_user的用法
//测试程序,即用户空间
char val;
...
write(fd, &val, sizeof(val));
//驱动程序,即内核空间
copy_from_user(&val, (char *)buf, sizeof(char));
注意:要明确val的类型,否则,可能取到的数值不正确。比如用户空间的val是int类型,而驱动部分却误写成了char类型的,就可能得不到正确数值了。(说可能,是因为不 知道目标机器是大端模式,还是小端模式);
2)alloc_chrdev_region(&devno, 0, 4, DEVNAME); 此函数指明设备的个数。然后调用多次cdev_add创建多个字符设备。这些字符设备使用同一套函数,即struct file_operations变量中的函数:
for(i = 1; i <= 4; i++){//register char device
cdev_add(&firstcdev, MKDEV(MAJOR(devno), i - 1), 1);
}
3)为了自动创建字符设备文件,so.....(一个类,多个设备)
myclass = class_create(THIS_MODULE, "ctlled"); //create /sys/class/ctlled
for(i = 1; i <= 4; i++){//create for devices
device_create(myclass, NULL, MKDEV(MAJOR(devno), i - 1), NULL, "led%d", i); //auto create /dev/led1~4
}
源代码:
#include <linux/miscdevice.h> #include <linux/delay.h> #include <asm/irq.h> #include <mach/regs-gpio.h> #include <mach/hardware.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> #include <linux/mm.h> #include <linux/fs.h> #include <linux/types.h> #include <linux/delay.h> #include <linux/moduleparam.h> #include <linux/slab.h> #include <linux/errno.h> #include <linux/ioctl.h> #include <linux/cdev.h> #include <linux/string.h> #include <linux/list.h> #include <linux/pci.h> #include <linux/gpio.h> #include <asm/uaccess.h> #include <asm/atomic.h> #include <asm/unistd.h> #include <linux/device.h> //device name #define DEVNAME "ledctl" //cdev struct cdev firstcdev; dev_t devno; //for auto creata device file struct class *myclass; static int first_drv_open(struct inode *inode, struct file *file) { //printk("first_cdrv_open\n"); int i; int ledno = 0; //int minor = MINOR(file->f_dentry->d_inode->i_rdev); int minor = MINOR(inode->i_rdev); printk("open:minor is %d\n", minor); s3c2410_gpio_cfgpin(S3C2410_GPB(5 + minor), S3C2410_GPIO_OUTPUT); //GPBx out mode s3c2410_gpio_setpin(S3C2410_GPB(5 + minor), 1); //ledx off return 0; } static int first_drv_write(struct file *file, const char __user *buf, size_t len, loff_t *ppos) { //printk("first_cdrv_write\n"); char val; int ledno = 0; int minor = MINOR(file->f_dentry->d_inode->i_rdev); printk("write:minor is %d\n", minor); copy_from_user(&val, (char *)buf, sizeof(char)); printk("val from user is %d\n", val); ledno = minor + 5; //led1's pin number is 5 printk("write:ledno is %d\n", minor); s3c2410_gpio_setpin(S3C2410_GPB(ledno), (val == 0 ? 1 : 0)); //val=1 led1 on return 0; } static struct file_operations first_drv_fops = { .owner = THIS_MODULE, .open = first_drv_open, .write = first_drv_write, }; static int first_drv_init(void) { int i; cdev_init(&firstcdev, &first_drv_fops); //init cdev alloc_chrdev_region(&devno, 0, 4, DEVNAME); //alloc char device number printk("devno = %d\nmajor = %d\nminor = %d\n", devno, MAJOR(devno), MINOR(devno));//devno contains major and minor for(i = 1; i <= 4; i++){//register char device cdev_add(&firstcdev, MKDEV(MAJOR(devno), i - 1), 1); } myclass = class_create(THIS_MODULE, "ctlled"); //create /sys/class/ctlled for(i = 1; i <= 4; i++){//create for devices device_create(myclass, NULL, MKDEV(MAJOR(devno), i - 1), NULL, "led%d", i); //auto create /dev/led1~4 } printk("firstdrvcdev initialized.\n"); return 0; } static void first_drv_exit(void) { int i; unregister_chrdev_region(devno, 1); //release dev number for(i = 0; i < 4; i++){ device_destroy(myclass, MKDEV(MAJOR(devno), i)); //del device } class_destroy(myclass); //del class cdev_del(&firstcdev); //del char device } module_init(first_drv_init); module_exit(first_drv_exit); MODULE_LICENSE("GPL");
测试程序:(与韦老师提供的有点小小的改动)
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> /* * ledtest <dev> <on|off> */ void print_usage(char *file) { printf("Usage:\n"); printf("%s <dev> <on|off>\n",file); printf("eg. \n"); printf("%s /dev/led1 on\n", file); printf("%s /dev/led1 off\n", file); printf("%s /dev/led2 on\n", file); printf("%s /dev/led3 off\n", file); printf("%s /dev/led4 off\n", file); } int main(int argc, char **argv) { int fd; char* filename; char val; if (argc != 3) { print_usage(argv[0]); return 0; } filename = argv[1]; fd = open(filename, O_RDWR); if (fd < 0) { printf("error, can't open %s\n", filename); return 0; } if (0 == strcmp("on", argv[2])) { // ÁÁµÆ val = 1; write(fd, &val, sizeof(val)); } else if (0 == strcmp("off", argv[2])) { // ÃðµÆ val = 0; write(fd, &val, sizeof(val)); } else { print_usage(argv[0]); return 0; } return 0; }