**LINUX 驱动例程总结** **目录** 1.使用主次设备号手动创建设备文件 2. 自动创建设备文件 3. 混杂设备驱动例程 4. 软中断之tasklet去实现软中断 5. 驱动之工作队列例程 6. 内核之timer_list软件定时器 7. 内核竟态之4种解决方法 8.linux 内核之等待队列例程 9. 内核驱动内存映射 10. 无设备树平台框架驱动例程 11. 驱动模型之MMA 8653 驱动 ## 1. 使用主次设备号手动创建设备文件 **首先了解下什么是主设备号和次设备号** **主设备号作用**: 应用程序根据字符设备文件的主设备号在茫茫的内核驱动中找到对应的唯一的设备驱动,一个设备驱动仅有唯一的主设备号。 **次设备号作用:**设备驱动根据次设备号能够找到应用程序要访问的硬件外设个体。 设备号包含主,次设备号linux内核用dev_t(unsigned int)数据类型描述设备号信息设备号的高12位用来描述主设备号设备号的低20位用来描述次设备号设备号和主,次设备号之间的转换操作宏: **设备号=MKDEV(已知的主设备号,已知的次设备号); 主设备号=MAJOR(已知的设备号); 次设备号=MINOR(已知的设备号);** **下面附上一个创建的例程** ```c #include <linux/init.h> #include <linux/module.h> #include <linux/gpio.h> #include <mach/platform.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/uaccess.h> struct led_resource { int gpio; char *name; }; static struct led_resource led_info[] = { { .gpio = PAD_GPIO_C + 12, .name = "LED1" } }; #define LED_ON 0x100001 #define LED_OFF 0x100002 static long led_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { int kindex; copy_from_user(&kindex, (int *)arg, sizeof(kindex)); switch(cmd) { case LED_ON: gpio_set_value(led_info[kindex-1].gpio, 0); break; case LED_OFF: gpio_set_value(led_info[kindex-1].gpio, 1); break; default: return -1; } return 0; } static struct file_operations led_fops = { .unlocked_ioctl = led_ioctl, }; static dev_t dev; static struct cdev led_cdev; static int led_init(void) { int i; alloc_chrdev_region(&dev, 0, 1, "nishishengma"); cdev_init(&led_cdev, &led_fops); cdev_add(&led_cdev, dev, 1); for(i = 0; i < ARRAY_SIZE(led_info); i++) { gpio_request(led_info[i].gpio, led_info[i].name); gpio_direction_output(led_info[i].gpio, 1); } return 0; } static void led_exit(void) { int i; for (i = 0; i < ARRAY_SIZE(led_info); i++) { gpio_set_value(led_info[i].gpio, 1); gpio_free(led_info[i].gpio); } unregister_chrdev_region(dev, 1); cdev_del(&led_cdev); } module_init(led_init); module_exit(led_exit); MODULE_LICENSE("GPL"); ``` ## **2自动创建设备文件** **2.1例程分享** ```c #include <linux/init.h> #include <linux/module.h> #include <linux/gpio.h> #include <mach/platform.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/uaccess.h> #include <linux/device.h> struct led_resource { int gpio; char *name; }; static struct led_resource led_info[] = { { .gpio = PAD_GPIO_C + 12, .name = "LED1" } }; #define LED_ON 0x100001 #define LED_OFF 0x100002 static long led_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { int kindex; copy_from_user(&kindex, (int *)arg, sizeof(kindex)); switch(cmd) { case LED_ON: gpio_set_value(led_info[kindex-1].gpio, 0); break; case LED_OFF: gpio_set_value(led_info[kindex-1].gpio, 1); break; default: return -1; } return 0; } static struct file_operations led_fops = { .unlocked_ioctl = led_ioctl, static dev_t dev; static struct cdev led_cdev; static struct class *cls; static int led_init(void) { int i; alloc_chrdev_region(&dev, 0, 1, "nishishui"); cdev_init(&led_cdev, &led_fops); cdev_add(&led_cdev, dev, 1); for(i = 0; i < ARRAY_SIZE(led_info); i++) { gpio_request(led_info[i].gpio, led_info[i].name); gpio_direction_output(led_info[i].gpio, 1); } cls = class_create(THIS_MODULE, "meimei"); device_create(cls, NULL, dev, NULL, "yourled"); return 0; } static void led_exit(void) { int i; for (i = 0; i < ARRAY_SIZE(led_info); i++) { gpio_set_value(led_info[i].gpio, 1); gpio_free(led_info[i].gpio); } unregister_chrdev_region(dev, 1); cdev_del(&led_cdev); device_destroy(cls, dev); class_destroy(cls); } module_init(led_init); module_exit(led_exit); MODULE_LICENSE("GPL"); ``` ## **3. 混杂设备驱动例程** **3.1混杂设备的其实就是往dev路径下创建驱动文件 3.2 分享例程** ```c #include <linux/init.h> #include <linux/module.h> #include <linux/miscdevice.h> #include <linux/fs.h> #include <linux/uaccess.h> #include <linux/gpio.h> #include <mach/platform.h> struct led_resource { int gpio; char *name; }; static struct led_resource led_info[] = { { .gpio = PAD_GPIO_C + 12, .name = "LED1" } }; #define LED_ON 0x100001 #define LED_OFF 0x100002 static long led_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { int kindex; copy_from_user(&kindex, (int *)arg, sizeof(kindex)); switch(cmd) { case LED_ON: gpio_set_value(led_info[kindex-1].gpio, 0); break; case LED_OFF: gpio_set_value(led_info[kindex-1].gpio, 1); break; default: return -1; } return 0; } static struct file_operations led_fops = { .owner = THIS_MODULE, .unlocked_ioctl = led_ioctl }; static struct miscdevice led_misc = { .minor = MISC_DYNAMIC_MINOR, .name = "myled", .fops = &led_fops }; static int led_init(void) { int i; for(i = 0; i < ARRAY_SIZE(led_info); i++) { gpio_request(led_info[i].gpio, led_info[i].name); gpio_direction_output(led_info[i].gpio, 1); } misc_register(&led_misc); return 0; } static void led_exit(void) { int i; misc_deregister(&led_misc); for (i = 0; i < ARRAY_SIZE(led_info); i++) { gpio_set_value(led_info[i].gpio, 1); gpio_free(led_info[i].gpio); } } module_init(led_init); module_exit(led_exit); MODULE_LICENSE("GPL"); ``` ## 4. 软中断之TASKLET去实现软中断 **4.1 tasklet特点: 4.1.1基于软中断实现 4.1.2延后处理函数不能休眠 4.1.3优先级高于进程低于硬件中断 4.1.4 struct tasklet_struct 4.1.5配置函数:DECLARE_TASKLET //定义初始化 tasklet_schedule //向内核登记,内核会在适当的时候执行 4.2实现例程** ```c #include <linux/init.h> #include <linux/module.h> #include <linux/irq.h> #include <linux/interrupt.h> #include <linux/gpio.h> #include <mach/platform.h> #include <linux/input.h> struct btn_resource { int gpio; char *name; int code; }; static struct btn_resource btn_info[] = { { .gpio = PAD_GPIO_A+28, .name = "KEY_UP", .code = KEY_UP }, { .gpio = PAD_GPIO_B+9, .name = "KEY_DOWN", .code = KEY_DOWN }, }; static struct btn_resource *pdata; static void btn_tasket_function(unsigned long data) { int state; state = gpio_get_value(pdata->gpio); } static int g_data = 0x5555; static DECLARE_TASKLET(btn_tasklet, btn_tasket_function, (unsigned long)&g_data); static irqreturn_t button_isr(int irq, void *dev) { pdata = (struct btn_resource *)dev; tasklet_schedule(&btn_tasklet); return IRQ_HANDLED; } static int btn_init(void) { int i; for (i = 0; i < ARRAY_SIZE(btn_info); i++) { int irq = gpio_to_irq(btn_info[i].gpio); gpio_request(btn_info[i].gpio, btn_info[i].name); request_irq(irq, button_isr, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, btn_info[i].name, &btn_info[i] ); } return 0; } static void btn_exit(void) { int i; for(i = 0; i < ARRAY_SIZE(btn_info); i++) {/ int irq = gpio_to_irq(btn_info[i].gpio); gpio_free(btn_info[i].gpio); free_irq(irq, &btn_info[i]); } } module_init(btn_init); module_exit(btn_exit); MODULE_LICENSE("GPL"); ``` ## 5.驱动之工作队列例程 **5.1工作队列特点 5.1.1基于进程 5.1.2延后处理函数可以进行休眠操作 5.1.3优先级低于软中断低于硬件中断 5.1.4struct work_struct 5.1.5配套函数 INIT_WORK //初始化 work_schedule //登记** ```c #include <linux/init.h> #include <linux/module.h> #include <linux/irq.h> #include <linux/interrupt.h> #include <linux/gpio.h> #include <mach/platform.h> #include <linux/input.h> struct btn_resource { int gpio; char *name; int code; }; static struct btn_resource btn_info[] = { { .gpio = PAD_GPIO_A+28, .name = "KEY_UP", .code = KEY_UP }, { .gpio = PAD_GPIO_B+9, .name = "KEY_DOWN", .code = KEY_DOWN }, }; static struct btn_resource *pdata; static void btn_work_function(struct work_struct *work) { int state; state = gpio_get_value(pdata->gpio); } static struct work_struct btn_work; static irqreturn_t button_isr(int irq, void *dev) { pdata = (struct btn_resource *)dev; schedule_work(&btn_work); return IRQ_HANDLED; } static int btn_init(void) { int i; for (i = 0; i < ARRAY_SIZE(btn_info); i++) { int irq = gpio_to_irq(btn_info[i].gpio); gpio_request(btn_info[i].gpio, btn_info[i].name); request_irq(irq, button_isr, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, btn_info[i].name, &btn_info[i] ); } INIT_WORK(&btn_work, btn_work_function); return 0; } static void btn_exit(void) { int i; for(i = 0; i < ARRAY_SIZE(btn_info); i++) {/ int irq = gpio_to_irq(btn_info[i].gpio); gpio_free(btn_info[i].gpio); free_irq(irq, &btn_info[i]); } } module_init(btn_init); module_exit(btn_exit); MODULE_LICENSE("GPL"); ``` ## 6.内核之TIMER_LIST软件定时器 **6.1实现Linux软件定时器的时候首先了解下内核跟时间相关的概念** **6.2 HZ:是内核的一个全局常量** ARM架构:HZ=100 X86架构:HZ=1000 以ARM架构为例,HZ=100表示硬件定时器一秒钟给CPU发送 100次硬件定时器中断,每发生一次硬件定时器中断的时间 间隔为10ms **6.3 jiffies_64:**是内核的一个全局变量,64位(unsigned long long) 记录自开机以来,硬件定时器给CPU发送的硬件 定时器中断的次数,每发生一次,jiffies_64自动 加1(由硬件定时器的中断处理函数进行加1) 那也就代表时间上加10ms **6.4 jiffies:**也是内核的一个全局变量,32位(unsigned long) 它的值取的是jiffies_64的低32位,也就是每发生 一次定时器中断,jiffis_64加1,那也是jiffies加1 jiffies一般用来记录时间间隔(记录流失时间) 切记切记:将来只要在代码中看到jiffies,那就表示当前时刻的时间 **6.5参考代码: unsinged long timeout = jiffies + 5*HZ;** 说明: jiffies:表示代码执行到这条语句对应的当前时刻的时间 5*HZ=5*100=500:表示500次硬件定时器中断,一次为10ms 所以5*HZ表示5秒钟 timeout:5秒以后的时间 unsigned long timeout = jiffies + 2; 说明: jiffies:表示代码执行到这条语句对应的当前时刻的时间 2:2次硬件定时器中断,共20ms timeout:20ms以后的时间 **6.6 下面附上例程** ```c #include <linux/init.h> #include <linux/module.h> #include <linux/timer.h> #include <linux/gpio.h> #include <mach/platform.h> static struct timer_list mytimer; static void mytimer_function(unsigned long data) { printk("%s: data = %#x/n", __func__, *(int *)data); mod_timer(&mytimer, jiffies+2*HZ); } static int g_data = 0x5555; static int mytimer_init(void) { init_timer(&mytimer); mytimer.expires = jiffies + 2*HZ; mytimer.function = mytimer_function; mytimer.data = (unsigned long)&g_data; add_timer(&mytimer); return 0; } static void mytimer_exit(void) { del_timer(&mytimer); } module_init(mytimer_init); module_exit(mytimer_exit); MODULE_LICENSE("GPL"); ``` ## 7.内核竟态之4种解决方法 **7.1 屏蔽中断例程** ```c #include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/miscdevice.h> static int open_cnt = 1; static int led_open(struct inode *inode, struct file *file) { unsigned long flags; local_irq_save(flags); if(--open_cnt != 0) { open_cnt++; local_irq_restore(flags); return -EBUSY; } local_irq_restore(flags); return 0; } static int led_close(struct inode *inode, struct file *file) { unsigned long flags; local_irq_save(flags); open_cnt++; local_irq_restore(flags); return 0; } static struct file_operations led_fops = { .owner = THIS_MODULE, .open = led_open, .release = led_close }; static struct miscdevice led_misc = { .minor = MISC_DYNAMIC_MINOR, .name = "myled", .fops = &led_fops }; static int led_init(void) { misc_register(&led_misc); return 0; } static void led_exit(void) { misc_deregister(&led_misc); } module_init(led_init); module_exit(led_exit); MODULE_LICENSE("GPL"); ``` **7.2自选锁例程** ```c #include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/miscdevice.h> static int open_cnt = 1; static spinlock_t lock; static int led_open(struct inode *inode, struct file *file) { spin_lock(&lock); if(--open_cnt != 0) { open_cnt++; spin_unlock(&lock); return -EBUSY; } spin_unlock(&lock); return 0; } static int led_close(struct inode *inode, struct file *file) { spin_lock(&lock); open_cnt++; spin_unlock(&lock); return 0; } static struct file_operations led_fops = { .owner = THIS_MODULE, .open = led_open, .release = led_close }; static struct miscdevice led_misc = { .minor = MISC_DYNAMIC_MINOR, .name = "myled", .fops = &led_fops }; static int led_init(void) { misc_register(&led_misc); spin_lock_init(&lock); return 0; } static void led_exit(void) { misc_deregister(&led_misc); } module_init(led_init); module_exit(led_exit); MODULE_LICENSE("GPL"); ``` **7.3 衍生自选锁例程** ```c #include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/miscdevice.h> static int open_cnt = 1; static spinlock_t lock; static int led_open(struct inode *inode, struct file *file) { unsigned long flags; spin_lock_irqsave(&lock, flags); if(--open_cnt != 0) { open_cnt++; spin_unlock_irqrestore(&lock, flags); return -EBUSY; } spin_unlock_irqrestore(&lock, flags); return 0; } static int led_close(struct inode *inode, struct file *file) { unsigned long flags; spin_lock_irqsave(&lock, flags); open_cnt++; spin_unlock_irqrestore(&lock, flags); return 0; } static struct file_operations led_fops = { .owner = THIS_MODULE, .open = led_open, .release = led_close }; static struct miscdevice led_misc = { .minor = MISC_DYNAMIC_MINOR, .name = "myled", .fops = &led_fops }; static int led_init(void) { misc_register(&led_misc); spin_lock_init(&lock); return 0; } static void led_exit(void) { misc_deregister(&led_misc); } module_init(led_init); module_exit(led_exit); MODULE_LICENSE("GPL"); ``` **7.4 信号量例程** ```c #include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/miscdevice.h> static struct semaphore sema; static int led_open(struct inode *inode, struct file *file) { down(&sema); return 0; } static int led_close(struct inode *inode, struct file *file) { up(&sema); return 0; } static struct file_operations led_fops = { .owner = THIS_MODULE, .open = led_open, .release = led_close }; static struct miscdevice led_misc = { .minor = MISC_DYNAMIC_MINOR, .name = "myled", .fops = &led_fops }; static int led_init(void) { misc_register(&led_misc); sema_init(&sema, 1); return 0; } static void led_exit(void) { misc_deregister(&led_misc); } module_init(led_init); module_exit(led_exit); MODULE_LICENSE("GPL"); ``` ## 8.LINUX 内核之等待队列例程 ```c #include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/miscdevice.h> #include <linux/gpio.h> #include <mach/platform.h> #include <linux/uaccess.h> #include <linux/irq.h> #include <linux/interrupt.h> #include <linux/sched.h> static wait_queue_head_t rwq; static ssize_t btn_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { wait_queue_t wait; init_waitqueue_entry(&wait, current); add_wait_queue(&rwq, &wait); set_current_state(TASK_INTERRUPTIBLE); schedule(); set_current_state(TASK_RUNNING); remove_wait_queue(&rwq, &wait); if(signal_pending(current)) { return -ERESTARTSYS; } else { } return count; } static ssize_t btn_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { wake_up(&rwq); return count; } static struct file_operations btn_fops = { .owner = THIS_MODULE, .read = btn_read, .write = btn_write }; static struct miscdevice btn_misc = { .minor = MISC_DYNAMIC_MINOR, .name = "mybtn", .fops = &btn_fops }; static int btn_init(void) { misc_register(&btn_misc); init_waitqueue_head(&rwq); return 0; } static void btn_exit(void) { misc_deregister(&btn_misc); } module_init(btn_init); module_exit(btn_exit); MODULE_LICENSE("GPL"); ``` ## 9.内核驱动内存映射 **9.1 内核驱动内部实现mmap 函数** ```c #include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/miscdevice.h> #include <linux/mm.h> //mmap static int led_mmap(struct file *file, struct vm_area_struct *vma) { remap_pfn_range(vma, vma->vm_start,//起始用户虚拟地址 0xC001C000>>12,//已知起始物理地址>>12 vma->vm_end-vma->vm_start,//大小 vma->vm_page_prot//权限 ); return 0; } static struct file_operations led_fops = { .owner = THIS_MODULE, .mmap = led_mmap static struct miscdevice led_misc = { .minor = MISC_DYNAMIC_MINOR, .name = "myled", .fops = &led_fops }; static int led_init(void) { misc_register(&led_misc); return 0; } static void led_exit(void) { misc_deregister(&led_misc); } module_init(led_init); module_exit(led_exit); MODULE_LICENSE("GPL"); ``` **9.2 应用层实现mmap 函数调用** ```c #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/ioctl.h> #include <fcntl.h> #include <sys/mman.h> int main(int argc, char *argv[]) { int fd; void *gpiobase; unsigned long *gpiocout; unsigned long *gpiocoutenb; unsigned long *gpiocaltfn0; if(argc != 2) { return -1; } fd = open("/dev/myled", O_RDWR); if (fd < 0) { return -1; } gpiobase = mmap(0, 0x100, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); gpiocout = (unsigned long *)(gpiobase + 0x00); gpiocoutenb = (unsigned long *)(gpiobase + 0x04); gpiocaltfn0 = (unsigned long *)(gpiobase + 0x20); *gpiocaltfn0 &= ~(0x3 << 24); *gpiocaltfn0 |= (1 << 24); *gpiocoutenb |= (1 << 12); *gpiocout |= (1 << 12); if(!strcmp(argv[1], "on")) *gpiocout &= ~(1 << 12); else *gpiocout |= (1 << 12); munmap(gpiobase, 0x100); close(fd); return 0; } ``` ## 10.无设备树平台框架驱动例程 **10.1 resource 资源内存驱动例程** ```c #include <linux/init.h> #include <linux/module.h> #include <linux/platform_device.h> struct led_resource { unsigned long phys_address; int size; int pin; }; static struct led_resource led1 = { .phys_address = 0xC001C000, .size = 0x24, .pin = 12 }; static void led_release(struct device *dev) {} static struct platform_device led_dev = { .name = "nishishui", .id = -1, .dev = { .platform_data = &led1, .release = led_release } }; static int led_dev_init(void) { platform_device_register(&led_dev); return 0; } static void led_dev_exit(void) { platform_device_unregister(&led_dev); } module_init(led_dev_init); module_exit(led_dev_exit); MODULE_LICENSE("GPL"); ``` **10.2 内存应用驱动例程** ```c #include <linux/init.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/fs.h> #include <linux/miscdevice.h> #include <linux/io.h> #include <linux/uaccess.h> struct led_resource { unsigned long phys_address; int size; int pin; }; static void *gpiobase; static unsigned long *gpioout, *gpiooutenb, *gpioaltfn0; static int pin1; #define LED_ON 0x100001 #define LED_OFF 0x100002 static long led_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { int kindex; copy_from_user(&kindex, (int *)arg, sizeof(kindex)); switch(cmd) { case LED_ON: if(kindex == 1) *gpioout &= ~(1 << pin1); /*else ... if*/ break; case LED_OFF: if(kindex == 1) *gpioout |= (1 << pin1); /*else ... if*/ break; default: return -1; } return 0; } static struct file_operations led_fops = { .owner = THIS_MODULE, .unlocked_ioctl = led_ioctl }; static struct miscdevice led_misc = { .minor = MISC_DYNAMIC_MINOR, .name = "myled", .fops = &led_fops }; static int led_probe(struct platform_device *pdev) { struct led_resource *pdata = pdev->dev.platform_data; gpiobase = ioremap(pdata->phys_address, pdata->size); gpioout = (unsigned long *)(gpiobase + 0x00); gpiooutenb = (unsigned long *)(gpiobase + 0x04); gpioaltfn0 = (unsigned long *)(gpiobase + 0x20); pin1 = pdata->pin; *gpioaltfn0 &= ~(0x3 << (2*pin1)); *gpioaltfn0 |= (1 << (2*pin1)); *gpiooutenb |= (1 << pin1); *gpioout |= (1 << pin1); misc_register(&led_misc); return 0; } static int led_remove(struct platform_device *pdev) { misc_deregister(&led_misc); *gpioout |= (1 << pin1); iounmap(gpiobase); return 0; } static struct platform_driver led_drv = { .driver = { .name = "nishishui" }, .probe = led_probe, .remove = led_remove, }; static int led_drv_init(void) { platform_driver_register(&led_drv); return 0; } static void led_drv_exit(void) { platform_driver_unregister(&led_drv); } module_init(led_drv_init); module_exit(led_drv_exit); MODULE_LICENSE("GPL"); ``` ## 11.I2C 驱动模型之MMA 8653 驱动 ```c #include <linux/init.h> #include <linux/module.h> #include <linux/i2c.h> #include <linux/fs.h> #include <linux/miscdevice.h> #include <linux/uaccess.h> #include <linux/delay.h> struct mma8653_data { short x; short y; short z; }; //将来用于匹配的对象 static struct i2c_device_id mma8653_id[] = { {"mma8653", 0}, }; static struct i2c_client *g_client; static void mma8653_hw_init(struct i2c_client *client) { int ret = 0; ret = i2c_smbus_read_byte_data(client, 0x0D); printk("%s:addr = %#x, Read ID value is :%#x/n", __func__, client->addr, ret); i2c_smbus_write_byte_data(client, 0x2A, 0); i2c_smbus_write_byte_data(client, 0x0E,0); } static void mma8653_read_data(struct mma8653_data *mma) { unsigned char tmp_data[7]; while(!(i2c_smbus_read_byte_data(g_client, 0x00) & 0x08)) { printk("data is not ready!/n"); } i2c_smbus_read_i2c_block_data(g_client, 0x01, 7, tmp_data); mma->x = ((tmp_data[0] << 8) & 0xff00) | tmp_data[1]; mma->y = ((tmp_data[2] << 8) & 0xff00) | tmp_data[3]; mma->z = ((tmp_data[4] << 8) & 0xff00) | tmp_data[5]; mma->x = (mma->x) >> 6; mma->y = (mma->y) >> 6; mma->z = (mma->z) >> 6; msleep(20); } static void mma8653_config_mode(void) { unsigned char data; data = i2c_smbus_read_byte_data(g_client, 0x2A); data |= 1; i2c_smbus_write_byte_data(g_client, 0x2A, data); } #define GS_MMA8653_GETXYZ_CMD 0x100001 static long mma8653_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct mma8653_data mma; switch(cmd) { case GS_MMA8653_GETXYZ_CMD: mma8653_config_mode(); mma8653_read_data(&mma); copy_to_user((struct mma8653_data *)arg, &mma, sizeof(mma)); break; default: return -1; } return 0; } static struct file_operations mma8653_fops = { .owner = THIS_MODULE, .unlocked_ioctl = mma8653_ioctl }; static struct miscdevice mma8653_misc = { .minor = MISC_DYNAMIC_MINOR, .name = "mma8653", .fops = &mma8653_fops }; static int mma8653_probe(struct i2c_client *client, const struct i2c_device_id *id) { misc_register(&mma8653_misc); g_client = client; mma8653_hw_init(client); return 0; } static int mma8653_remove(struct i2c_client *client) { misc_deregister(&mma8653_misc); return 0; } static struct i2c_driver mma8653_drv = { .driver = { .name = "nishishui" }, .id_table = mma8653_id, .probe = mma8653_probe, .remove = mma8653_remove }; static int mma8653_init(void) { i2c_add_driver(&mma8653_drv); return 0; } static void mma8653_exit(void) { i2c_del_driver(&mma8653_drv); } module_init(mma8653_init); module_exit(mma8653_exit); MODULE_LICENSE("GPL"); ```
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/281073.html