#define pr_fmt(fmt) "[hui] " fmt #include #include #include #include #include #include #include #include "hui_internal.h" #include #include static int led_open(struct inode *, struct file *); static int led_close(struct inode *, struct file *); static long led_ioctl(struct file *file, unsigned int cmd, unsigned long arg); struct _avm_led_open_data { unsigned int reserved; }; static struct file_operations led_fops = { owner: THIS_MODULE, open: led_open, release: led_close, unlocked_ioctl: led_ioctl, }; struct _avm_led { dev_t device; struct cdev *cdev; struct class *class; struct device *dev; }; static struct _avm_led led_data; int device_init(void) { int ret; ret = alloc_chrdev_region(&led_data.device, 0, 1, "led"); if (ret) { pr_err("register_chrdv_region failed: %d!\n", ret); return ret; } led_data.cdev = cdev_alloc(); if (!led_data.cdev) { unregister_chrdev_region(led_data.device, 1); pr_err("cdev_alloc failed!\n"); return -ENOMEM; } led_data.cdev->owner = led_fops.owner; led_data.cdev->ops = &led_fops; kobject_set_name(&(led_data.cdev->kobj), "led"); ret = cdev_add(led_data.cdev, led_data.device, 1); if (ret) { kobject_put(&led_data.cdev->kobj); unregister_chrdev_region(led_data.device, 1); pr_err("cdev_add failed!\n"); return ret; } pr_debug("major %d (success)\n", MAJOR(led_data.device)); // Create led device led_data.class = class_create(THIS_MODULE, "led"); if (IS_ERR(led_data.class)) return PTR_ERR(led_data.class); led_data.dev = device_create(led_data.class, NULL, led_data.device, NULL, "%s", "led"); if (IS_ERR(led_data.dev)) return PTR_ERR(led_data.dev); return 0; } static int led_open(struct inode *inode __attribute__((unused)), struct file *filp) { struct _avm_led_open_data *open_data; pr_debug("avm_led_open:\n"); open_data = (struct _avm_led_open_data *)kmalloc( sizeof(struct _avm_led_open_data), GFP_KERNEL); if (!open_data) { pr_err("avm_led_open: open malloc failed\n"); return -EFAULT; } memset(open_data, 0, sizeof(*open_data)); filp->private_data = (void *)open_data; pr_debug("avm_led_open: open success flags=0x%x\n", filp->f_flags); return 0; } /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ static int led_close(struct inode *inode __attribute__((unused)), struct file *filp) { pr_debug("avm_led_close:\n"); /*--- achtung auf ind wartende "gefreien" und warten bis alle fertig * ---*/ if (filp->private_data) { kfree(filp->private_data); filp->private_data = NULL; } return 0; } static int led_send(struct led_event *e) { struct _avm_event_led_status *event; int size = sizeof(struct _avm_event_led_status) + e->event_param_size; event = kzalloc(size, GFP_KERNEL); if (!event) return -ENOMEM; event->header.id = avm_event_id_led_status; event->led = e->event; event->state = e->event_value; if (e->event_param_size) { event->param_len = e->event_param_size; memcpy(event->params, e->event_param, e->event_param_size); } return avm_event_source_trigger(event_source, avm_event_id_led_status, size, event); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static long led_ioctl(struct file *file __attribute__((unused)), unsigned int cmd, unsigned long arg) { int ret = 0; struct led_event my_led_event; void *event_param = NULL; pr_debug("cmd = %08x\n", cmd); switch (cmd) { case LED_IOC_EVENT: if (copy_from_user((char *)&my_led_event, (char *)arg, sizeof(struct led_event))) { pr_err("LED_IOC_EVENT copy_from user failed\n"); ret = -EFAULT; break; } my_led_event.event_param_size = 0; led_send(&my_led_event); break; case LED_IOC_EVENT_WITH_PARAM: if (copy_from_user((char *)&my_led_event, (char *)arg, sizeof(struct led_event))) { pr_err("LED_IOC_EVENT_WITH_PARAM copy_from user failed\n"); ret = -EFAULT; break; } if (my_led_event.event_param_size > AVM_LED_STATUS_MAX_PARAMLEN) { pr_err("LED_IOC_EVENT_WITH_PARAM copy_from user failed: param_size=%u exceeds limit of AVM_LED_STATUS_MAX_PARAMLEN!\n", my_led_event.event_param_size); ret = -EFAULT; break; } event_param = kmalloc(my_led_event.event_param_size, GFP_ATOMIC); if (!event_param) { pr_err("[%s]: avm_led_ioctl: LED_IOC_EVENT_WITH_PARAM kmalloc failed\n", "avm_led"); ret = -EFAULT; break; } if (copy_from_user(event_param, (char *)my_led_event.event_param, my_led_event.event_param_size)) { pr_err("LED_IOC_EVENT_WITH_PARAM/event_param copy_from user failed\n"); ret = -EFAULT; kfree(event_param); break; } my_led_event.event_param = event_param; /*--- _WITH_PARAM Event verarbeiten: ---*/ led_send(&my_led_event); kfree(event_param); break; } return ret; }