📄 video.c
字号:
active_device_list = kmalloc((1 + dod->package.count) * sizeof(struct acpi_video_enumerated_device), GFP_KERNEL); if (!active_device_list) { status = -ENOMEM; goto out; } count = 0; for (i = 0; i < dod->package.count; i++) { obj = &dod->package.elements[i]; if (obj->type != ACPI_TYPE_INTEGER) { printk(KERN_ERR PREFIX "Invalid _DOD data\n"); active_device_list[i].value.int_val = ACPI_VIDEO_HEAD_INVALID; } active_device_list[i].value.int_val = obj->integer.value; active_device_list[i].bind_info = NULL; ACPI_DEBUG_PRINT((ACPI_DB_INFO, "dod element[%d] = %d\n", i, (int)obj->integer.value)); count++; } active_device_list[count].value.int_val = ACPI_VIDEO_HEAD_END; kfree(video->attached_array); video->attached_array = active_device_list; video->attached_count = count; out: kfree(buffer.pointer); return status;}/* * Arg: * video : video bus device * event : notify event * * Return: * < 0 : error * * 1. Find out the current active output device. * 2. Identify the next output device to switch to. * 3. call _DSS to do actual switch. */static int acpi_video_switch_output(struct acpi_video_bus *video, int event){ struct list_head *node; struct acpi_video_device *dev = NULL; struct acpi_video_device *dev_next = NULL; struct acpi_video_device *dev_prev = NULL; unsigned long state; int status = 0; mutex_lock(&video->device_list_lock); list_for_each(node, &video->video_device_list) { dev = container_of(node, struct acpi_video_device, entry); status = acpi_video_device_get_state(dev, &state); if (state & 0x2) { dev_next = container_of(node->next, struct acpi_video_device, entry); dev_prev = container_of(node->prev, struct acpi_video_device, entry); goto out; } } dev_next = container_of(node->next, struct acpi_video_device, entry); dev_prev = container_of(node->prev, struct acpi_video_device, entry); out: mutex_unlock(&video->device_list_lock); switch (event) { case ACPI_VIDEO_NOTIFY_CYCLE: case ACPI_VIDEO_NOTIFY_NEXT_OUTPUT: acpi_video_device_set_state(dev, 0); acpi_video_device_set_state(dev_next, 0x80000001); break; case ACPI_VIDEO_NOTIFY_PREV_OUTPUT: acpi_video_device_set_state(dev, 0); acpi_video_device_set_state(dev_prev, 0x80000001); default: break; } return status;}static intacpi_video_get_next_level(struct acpi_video_device *device, u32 level_current, u32 event){ int min, max, min_above, max_below, i, l, delta = 255; max = max_below = 0; min = min_above = 255; /* Find closest level to level_current */ for (i = 0; i < device->brightness->count; i++) { l = device->brightness->levels[i]; if (abs(l - level_current) < abs(delta)) { delta = l - level_current; if (!delta) break; } } /* Ajust level_current to closest available level */ level_current += delta; for (i = 0; i < device->brightness->count; i++) { l = device->brightness->levels[i]; if (l < min) min = l; if (l > max) max = l; if (l < min_above && l > level_current) min_above = l; if (l > max_below && l < level_current) max_below = l; } switch (event) { case ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS: return (level_current < max) ? min_above : min; case ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS: return (level_current < max) ? min_above : max; case ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS: return (level_current > min) ? max_below : min; case ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS: case ACPI_VIDEO_NOTIFY_DISPLAY_OFF: return 0; default: return level_current; }}static voidacpi_video_switch_brightness(struct acpi_video_device *device, int event){ unsigned long level_current, level_next; acpi_video_device_lcd_get_level_current(device, &level_current); level_next = acpi_video_get_next_level(device, level_current, event); acpi_video_device_lcd_set_level(device, level_next);}static intacpi_video_bus_get_devices(struct acpi_video_bus *video, struct acpi_device *device){ int status = 0; struct acpi_device *dev; acpi_video_device_enumerate(video); list_for_each_entry(dev, &device->children, node) { status = acpi_video_bus_get_one_device(dev, video); if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, status, "Cant attach device")); continue; } } return status;}static int acpi_video_bus_put_one_device(struct acpi_video_device *device){ acpi_status status; struct acpi_video_bus *video; if (!device || !device->video) return -ENOENT; video = device->video; acpi_video_device_remove_fs(device->dev); status = acpi_remove_notify_handler(device->dev->handle, ACPI_DEVICE_NOTIFY, acpi_video_device_notify); backlight_device_unregister(device->backlight); video_output_unregister(device->output_dev); return 0;}static int acpi_video_bus_put_devices(struct acpi_video_bus *video){ int status; struct acpi_video_device *dev, *next; mutex_lock(&video->device_list_lock); list_for_each_entry_safe(dev, next, &video->video_device_list, entry) { status = acpi_video_bus_put_one_device(dev); if (ACPI_FAILURE(status)) printk(KERN_WARNING PREFIX "hhuuhhuu bug in acpi video driver.\n"); if (dev->brightness) { kfree(dev->brightness->levels); kfree(dev->brightness); } list_del(&dev->entry); kfree(dev); } mutex_unlock(&video->device_list_lock); return 0;}/* acpi_video interface */static int acpi_video_bus_start_devices(struct acpi_video_bus *video){ return acpi_video_bus_DOS(video, 0, 0);}static int acpi_video_bus_stop_devices(struct acpi_video_bus *video){ return acpi_video_bus_DOS(video, 0, 1);}static void acpi_video_bus_notify(acpi_handle handle, u32 event, void *data){ struct acpi_video_bus *video = data; struct acpi_device *device = NULL; struct input_dev *input; int keycode; if (!video) return; device = video->device; input = video->input; switch (event) { case ACPI_VIDEO_NOTIFY_SWITCH: /* User requested a switch, * most likely via hotkey. */ acpi_bus_generate_proc_event(device, event, 0); keycode = KEY_SWITCHVIDEOMODE; break; case ACPI_VIDEO_NOTIFY_PROBE: /* User plugged in or removed a video * connector. */ acpi_video_device_enumerate(video); acpi_video_device_rebind(video); acpi_video_switch_output(video, event); acpi_bus_generate_proc_event(device, event, 0); keycode = KEY_SWITCHVIDEOMODE; break; case ACPI_VIDEO_NOTIFY_CYCLE: /* Cycle Display output hotkey pressed. */ acpi_video_switch_output(video, event); acpi_bus_generate_proc_event(device, event, 0); keycode = KEY_SWITCHVIDEOMODE; break; case ACPI_VIDEO_NOTIFY_NEXT_OUTPUT: /* Next Display output hotkey pressed. */ acpi_video_switch_output(video, event); acpi_bus_generate_proc_event(device, event, 0); keycode = KEY_VIDEO_NEXT; break; case ACPI_VIDEO_NOTIFY_PREV_OUTPUT: /* previous Display output hotkey pressed. */ acpi_video_switch_output(video, event); acpi_bus_generate_proc_event(device, event, 0); keycode = KEY_VIDEO_PREV; break; default: keycode = KEY_UNKNOWN; ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Unsupported event [0x%x]\n", event)); break; } input_report_key(input, keycode, 1); input_sync(input); input_report_key(input, keycode, 0); input_sync(input); return;}static void acpi_video_device_notify(acpi_handle handle, u32 event, void *data){ struct acpi_video_device *video_device = data; struct acpi_device *device = NULL; struct acpi_video_bus *bus; struct input_dev *input; int keycode; if (!video_device) return; device = video_device->dev; bus = video_device->video; input = bus->input; switch (event) { case ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS: /* Cycle brightness */ acpi_video_switch_brightness(video_device, event); acpi_bus_generate_proc_event(device, event, 0); keycode = KEY_BRIGHTNESS_CYCLE; break; case ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS: /* Increase brightness */ acpi_video_switch_brightness(video_device, event); acpi_bus_generate_proc_event(device, event, 0); keycode = KEY_BRIGHTNESSUP; break; case ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS: /* Decrease brightness */ acpi_video_switch_brightness(video_device, event); acpi_bus_generate_proc_event(device, event, 0); keycode = KEY_BRIGHTNESSDOWN; break; case ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS: /* zero brightnesss */ acpi_video_switch_brightness(video_device, event); acpi_bus_generate_proc_event(device, event, 0); keycode = KEY_BRIGHTNESS_ZERO; break; case ACPI_VIDEO_NOTIFY_DISPLAY_OFF: /* display device off */ acpi_video_switch_brightness(video_device, event); acpi_bus_generate_proc_event(device, event, 0); keycode = KEY_DISPLAY_OFF; break; default: keycode = KEY_UNKNOWN; ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Unsupported event [0x%x]\n", event)); break; } input_report_key(input, keycode, 1); input_sync(input); input_report_key(input, keycode, 0); input_sync(input); return;}static int instance;static int acpi_video_bus_add(struct acpi_device *device){ acpi_status status; struct acpi_video_bus *video; struct input_dev *input; int error; video = kzalloc(sizeof(struct acpi_video_bus), GFP_KERNEL); if (!video) return -ENOMEM; /* a hack to fix the duplicate name "VID" problem on T61 */ if (!strcmp(device->pnp.bus_id, "VID")) { if (instance) device->pnp.bus_id[3] = '0' + instance; instance ++; } video->device = device; strcpy(acpi_device_name(device), ACPI_VIDEO_BUS_NAME); strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS); acpi_driver_data(device) = video; acpi_video_bus_find_cap(video); error = acpi_video_bus_check(video); if (error) goto err_free_video; error = acpi_video_bus_add_fs(device); if (error) goto err_free_video; mutex_init(&video->device_list_lock); INIT_LIST_HEAD(&video->video_device_list); acpi_video_bus_get_devices(video, device); acpi_video_bus_start_devices(video); status = acpi_install_notify_handler(device->handle, ACPI_DEVICE_NOTIFY, acpi_video_bus_notify, video); if (ACPI_FAILURE(status)) { ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error installing notify handler\n")); error = -ENODEV; goto err_stop_video; } video->input = input = input_allocate_device(); if (!input) { error = -ENOMEM; goto err_uninstall_notify; } snprintf(video->phys, sizeof(video->phys), "%s/video/input0", acpi_device_hid(video->device)); input->name = acpi_device_name(video->device); input->phys = video->phys; input->id.bustype = BUS_HOST; input->id.product = 0x06; input->dev.parent = &device->dev; input->evbit[0] = BIT(EV_KEY); set_bit(KEY_SWITCHVIDEOMODE, input->keybit); set_bit(KEY_VIDEO_NEXT, input->keybit); set_bit(KEY_VIDEO_PREV, input->keybit); set_bit(KEY_BRIGHTNESS_CYCLE, input->keybit); set_bit(KEY_BRIGHTNESSUP, input->keybit); set_bit(KEY_BRIGHTNESSDOWN, input->keybit); set_bit(KEY_BRIGHTNESS_ZERO, input->keybit); set_bit(KEY_DISPLAY_OFF, input->keybit); set_bit(KEY_UNKNOWN, input->keybit); error = input_register_device(input); if (error) goto err_free_input_dev; printk(KERN_INFO PREFIX "%s [%s] (multi-head: %s rom: %s post: %s)\n", ACPI_VIDEO_DEVICE_NAME, acpi_device_bid(device), video->flags.multihead ? "yes" : "no", video->flags.rom ? "yes" : "no", video->flags.post ? "yes" : "no"); return 0; err_free_input_dev: input_free_device(input); err_uninstall_notify: acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY, acpi_video_bus_notify); err_stop_video: acpi_video_bus_stop_devices(video); acpi_video_bus_put_devices(video); kfree(video->attached_array); acpi_video_bus_remove_fs(device); err_free_video: kfree(video); acpi_driver_data(device) = NULL; return error;}static int acpi_video_bus_remove(struct acpi_device *device, int type){ acpi_status status = 0; struct acpi_video_bus *video = NULL; if (!device || !acpi_driver_data(device)) return -EINVAL; video = acpi_driver_data(device); acpi_video_bus_stop_devices(video); status = acpi_remove_notify_handler(video->device->handle, ACPI_DEVICE_NOTIFY, acpi_video_bus_notify); acpi_video_bus_put_devices(video); acpi_video_bus_remove_fs(device); input_unregister_device(video->input); kfree(video->attached_array); kfree(video); return 0;}static int __init acpi_video_init(void){ int result = 0; /* acpi_dbg_level = 0xFFFFFFFF; acpi_dbg_layer = 0x08000000; */ acpi_video_dir = proc_mkdir(ACPI_VIDEO_CLASS, acpi_root_dir); if (!acpi_video_dir) return -ENODEV; acpi_video_dir->owner = THIS_MODULE; result = acpi_bus_register_driver(&acpi_video_bus); if (result < 0) { remove_proc_entry(ACPI_VIDEO_CLASS, acpi_root_dir); return -ENODEV; } return 0;}static void __exit acpi_video_exit(void){ acpi_bus_unregister_driver(&acpi_video_bus); remove_proc_entry(ACPI_VIDEO_CLASS, acpi_root_dir); return;}module_init(acpi_video_init);module_exit(acpi_video_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -