📄 sony-laptop.c
字号:
} val8 &= 0x07; if (copy_to_user(argp, &val8, sizeof(val8))) ret = -EFAULT; break; case SONYPI_IOCGBLUE: val8 = spic_dev.bluetooth_power; if (copy_to_user(argp, &val8, sizeof(val8))) ret = -EFAULT; break; case SONYPI_IOCSBLUE: if (copy_from_user(&val8, argp, sizeof(val8))) { ret = -EFAULT; break; } __sony_pic_set_bluetoothpower(val8); break; /* FAN Controls */ case SONYPI_IOCGFAN: if (sony_pic_get_fanspeed(&val8)) { ret = -EIO; break; } if (copy_to_user(argp, &val8, sizeof(val8))) ret = -EFAULT; break; case SONYPI_IOCSFAN: if (copy_from_user(&val8, argp, sizeof(val8))) { ret = -EFAULT; break; } if (sony_pic_set_fanspeed(val8)) ret = -EIO; break; /* GET Temperature (useful under APM) */ case SONYPI_IOCGTEMP: if (ec_read(SONYPI_TEMP_STATUS, &val8)) { ret = -EIO; break; } if (copy_to_user(argp, &val8, sizeof(val8))) ret = -EFAULT; break; default: ret = -EINVAL; } mutex_unlock(&spic_dev.lock); return ret;}static const struct file_operations sonypi_misc_fops = { .owner = THIS_MODULE, .read = sonypi_misc_read, .poll = sonypi_misc_poll, .open = sonypi_misc_open, .release = sonypi_misc_release, .fasync = sonypi_misc_fasync, .ioctl = sonypi_misc_ioctl,};static struct miscdevice sonypi_misc_device = { .minor = MISC_DYNAMIC_MINOR, .name = "sonypi", .fops = &sonypi_misc_fops,};static void sonypi_compat_report_event(u8 event){ kfifo_put(sonypi_compat.fifo, (unsigned char *)&event, sizeof(event)); kill_fasync(&sonypi_compat.fifo_async, SIGIO, POLL_IN); wake_up_interruptible(&sonypi_compat.fifo_proc_list);}static int sonypi_compat_init(void){ int error; spin_lock_init(&sonypi_compat.fifo_lock); sonypi_compat.fifo = kfifo_alloc(SONY_LAPTOP_BUF_SIZE, GFP_KERNEL, &sonypi_compat.fifo_lock); if (IS_ERR(sonypi_compat.fifo)) { printk(KERN_ERR DRV_PFX "kfifo_alloc failed\n"); return PTR_ERR(sonypi_compat.fifo); } init_waitqueue_head(&sonypi_compat.fifo_proc_list); if (minor != -1) sonypi_misc_device.minor = minor; error = misc_register(&sonypi_misc_device); if (error) { printk(KERN_ERR DRV_PFX "misc_register failed\n"); goto err_free_kfifo; } if (minor == -1) printk(KERN_INFO DRV_PFX "device allocated minor is %d\n", sonypi_misc_device.minor); return 0;err_free_kfifo: kfifo_free(sonypi_compat.fifo); return error;}static void sonypi_compat_exit(void){ misc_deregister(&sonypi_misc_device); kfifo_free(sonypi_compat.fifo);}#elsestatic int sonypi_compat_init(void) { return 0; }static void sonypi_compat_exit(void) { }static void sonypi_compat_report_event(u8 event) { }#endif /* CONFIG_SONYPI_COMPAT *//* * ACPI callbacks */static acpi_statussony_pic_read_possible_resource(struct acpi_resource *resource, void *context){ u32 i; struct sony_pic_dev *dev = (struct sony_pic_dev *)context; switch (resource->type) { case ACPI_RESOURCE_TYPE_START_DEPENDENT: { /* start IO enumeration */ struct sony_pic_ioport *ioport = kzalloc(sizeof(*ioport), GFP_KERNEL); if (!ioport) return AE_ERROR; list_add(&ioport->list, &dev->ioports); return AE_OK; } case ACPI_RESOURCE_TYPE_END_DEPENDENT: /* end IO enumeration */ return AE_OK; case ACPI_RESOURCE_TYPE_IRQ: { struct acpi_resource_irq *p = &resource->data.irq; struct sony_pic_irq *interrupt = NULL; if (!p || !p->interrupt_count) { /* * IRQ descriptors may have no IRQ# bits set, * particularly those those w/ _STA disabled */ dprintk("Blank IRQ resource\n"); return AE_OK; } for (i = 0; i < p->interrupt_count; i++) { if (!p->interrupts[i]) { printk(KERN_WARNING DRV_PFX "Invalid IRQ %d\n", p->interrupts[i]); continue; } interrupt = kzalloc(sizeof(*interrupt), GFP_KERNEL); if (!interrupt) return AE_ERROR; list_add(&interrupt->list, &dev->interrupts); interrupt->irq.triggering = p->triggering; interrupt->irq.polarity = p->polarity; interrupt->irq.sharable = p->sharable; interrupt->irq.interrupt_count = 1; interrupt->irq.interrupts[0] = p->interrupts[i]; } return AE_OK; } case ACPI_RESOURCE_TYPE_IO: { struct acpi_resource_io *io = &resource->data.io; struct sony_pic_ioport *ioport = list_first_entry(&dev->ioports, struct sony_pic_ioport, list); if (!io) { dprintk("Blank IO resource\n"); return AE_OK; } if (!ioport->io1.minimum) { memcpy(&ioport->io1, io, sizeof(*io)); dprintk("IO1 at 0x%.4x (0x%.2x)\n", ioport->io1.minimum, ioport->io1.address_length); } else if (!ioport->io2.minimum) { memcpy(&ioport->io2, io, sizeof(*io)); dprintk("IO2 at 0x%.4x (0x%.2x)\n", ioport->io2.minimum, ioport->io2.address_length); } else { printk(KERN_ERR DRV_PFX "Unknown SPIC Type, more than 2 IO Ports\n"); return AE_ERROR; } return AE_OK; } default: dprintk("Resource %d isn't an IRQ nor an IO port\n", resource->type); case ACPI_RESOURCE_TYPE_END_TAG: return AE_OK; } return AE_CTRL_TERMINATE;}static int sony_pic_possible_resources(struct acpi_device *device){ int result = 0; acpi_status status = AE_OK; if (!device) return -EINVAL; /* get device status */ /* see acpi_pci_link_get_current acpi_pci_link_get_possible */ dprintk("Evaluating _STA\n"); result = acpi_bus_get_status(device); if (result) { printk(KERN_WARNING DRV_PFX "Unable to read status\n"); goto end; } if (!device->status.enabled) dprintk("Device disabled\n"); else dprintk("Device enabled\n"); /* * Query and parse 'method' */ dprintk("Evaluating %s\n", METHOD_NAME__PRS); status = acpi_walk_resources(device->handle, METHOD_NAME__PRS, sony_pic_read_possible_resource, &spic_dev); if (ACPI_FAILURE(status)) { printk(KERN_WARNING DRV_PFX "Failure evaluating %s\n", METHOD_NAME__PRS); result = -ENODEV; }end: return result;}/* * Disable the spic device by calling its _DIS method */static int sony_pic_disable(struct acpi_device *device){ if (ACPI_FAILURE(acpi_evaluate_object(device->handle, "_DIS", NULL, NULL))) return -ENXIO; dprintk("Device disabled\n"); return 0;}/* * Based on drivers/acpi/pci_link.c:acpi_pci_link_set * * Call _SRS to set current resources */static int sony_pic_enable(struct acpi_device *device, struct sony_pic_ioport *ioport, struct sony_pic_irq *irq){ acpi_status status; int result = 0; /* Type 1 resource layout is: * IO * IO * IRQNoFlags * End * * Type 2 and 3 resource layout is: * IO * IRQNoFlags * End */ struct { struct acpi_resource res1; struct acpi_resource res2; struct acpi_resource res3; struct acpi_resource res4; } *resource; struct acpi_buffer buffer = { 0, NULL }; if (!ioport || !irq) return -EINVAL; /* init acpi_buffer */ resource = kzalloc(sizeof(*resource) + 1, GFP_KERNEL); if (!resource) return -ENOMEM; buffer.length = sizeof(*resource) + 1; buffer.pointer = resource; /* setup Type 1 resources */ if (spic_dev.model == SONYPI_DEVICE_TYPE1) { /* setup io resources */ resource->res1.type = ACPI_RESOURCE_TYPE_IO; resource->res1.length = sizeof(struct acpi_resource); memcpy(&resource->res1.data.io, &ioport->io1, sizeof(struct acpi_resource_io)); resource->res2.type = ACPI_RESOURCE_TYPE_IO; resource->res2.length = sizeof(struct acpi_resource); memcpy(&resource->res2.data.io, &ioport->io2, sizeof(struct acpi_resource_io)); /* setup irq resource */ resource->res3.type = ACPI_RESOURCE_TYPE_IRQ; resource->res3.length = sizeof(struct acpi_resource); memcpy(&resource->res3.data.irq, &irq->irq, sizeof(struct acpi_resource_irq)); /* we requested a shared irq */ resource->res3.data.irq.sharable = ACPI_SHARED; resource->res4.type = ACPI_RESOURCE_TYPE_END_TAG; } /* setup Type 2/3 resources */ else { /* setup io resource */ resource->res1.type = ACPI_RESOURCE_TYPE_IO; resource->res1.length = sizeof(struct acpi_resource); memcpy(&resource->res1.data.io, &ioport->io1, sizeof(struct acpi_resource_io)); /* setup irq resource */ resource->res2.type = ACPI_RESOURCE_TYPE_IRQ; resource->res2.length = sizeof(struct acpi_resource); memcpy(&resource->res2.data.irq, &irq->irq, sizeof(struct acpi_resource_irq)); /* we requested a shared irq */ resource->res2.data.irq.sharable = ACPI_SHARED; resource->res3.type = ACPI_RESOURCE_TYPE_END_TAG; } /* Attempt to set the resource */ dprintk("Evaluating _SRS\n"); status = acpi_set_current_resources(device->handle, &buffer); /* check for total failure */ if (ACPI_FAILURE(status)) { printk(KERN_ERR DRV_PFX "Error evaluating _SRS\n"); result = -ENODEV; goto end; } /* Necessary device initializations calls (from sonypi) */ sony_pic_call1(0x82); sony_pic_call2(0x81, 0xff); sony_pic_call1(compat ? 0x92 : 0x82);end: kfree(resource); return result;}/***************** * * ISR: some event is available * *****************/static irqreturn_t sony_pic_irq(int irq, void *dev_id){ int i, j; u8 ev = 0; u8 data_mask = 0; u8 device_event = 0; struct sony_pic_dev *dev = (struct sony_pic_dev *) dev_id; ev = inb_p(dev->cur_ioport->io1.minimum); if (dev->cur_ioport->io2.minimum) data_mask = inb_p(dev->cur_ioport->io2.minimum); else data_mask = inb_p(dev->cur_ioport->io1.minimum + dev->evport_offset); dprintk("event ([%.2x] [%.2x]) at port 0x%.4x(+0x%.2x)\n", ev, data_mask, dev->cur_ioport->io1.minimum, dev->evport_offset); if (ev == 0x00 || ev == 0xff) return IRQ_HANDLED; for (i = 0; sony_pic_eventtypes[i].model; i++) { if (spic_dev.model != sony_pic_eventtypes[i].model) continue; if ((data_mask & sony_pic_eventtypes[i].data) != sony_pic_eventtypes[i].data) continue; if (!(mask & sony_pic_eventtypes[i].mask)) continue; for (j = 0; sony_pic_eventtypes[i].events[j].event; j++) { if (ev == sony_pic_eventtypes[i].events[j].data) { device_event = sony_pic_eventtypes[i].events[j].event; goto found; } } } return IRQ_HANDLED;found: sony_laptop_report_input_event(device_event); acpi_bus_generate_proc_event(spic_dev.acpi_dev, 1, device_event); sonypi_compat_report_event(device_event); return IRQ_HANDLED;}/***************** * * ACPI driver * *****************/static int sony_pic_remove(struct acpi_device *device, int type){ struct sony_pic_ioport *io, *tmp_io; struct sony_pic_irq *irq, *tmp_irq; if (sony_pic_disable(device)) { printk(KERN_ERR DRV_PFX "Couldn't disable device.\n"); return -ENXIO; } free_irq(spic_dev.cur_irq->irq.interrupts[0], &spic_dev); release_region(spic_dev.cur_ioport->io1.minimum, spic_dev.cur_ioport->io1.address_length); if (spic_dev.cur_ioport->io2.minimum) release_region(spic_dev.cur_ioport->io2.minimum, spic_dev.cur_ioport->io2.address_length); sonypi_compat_exit(); sony_laptop_remove_input(); /* pf attrs */ sysfs_remove_group(&sony_pf_device->dev.kobj, &spic_attribute_group); sony_pf_remove(); list_for_each_entry_safe(io, tmp_io, &spic_dev.ioports, list) { list_del(&io->list); kfree(io); } list_for_each_entry_safe(irq, tmp_irq, &spic_dev.interrupts, list) { list_del(&irq->list); kfree(irq); } spic_dev.cur_ioport = NULL; spic_dev.cur_irq = NULL; dprintk(SONY_PIC_DRIVER_NAME " removed.\n"); return 0;}static int sony_pic_add(struct acpi_device *device){ int result; struct sony_pic_ioport *io, *tmp_io; struct sony_pic_irq *irq, *tmp_irq; printk(KERN_INFO DRV_PFX "%s v%s.\n", SONY_PIC_DRIVER_NAME, SONY_LAPTOP_DRIVER_VERSION); spic_dev.acpi_dev = device; strcpy(acpi_device_class(device), "sony/hotkey"); spic_dev.model = sony_pic_detect_device_type(); mutex_init(&spic_dev.lock); /* model specific characteristics */ switch(spic_dev.model) { case SONYPI_DEVICE_TYPE1: spic_dev.evport_offset = SONYPI_TYPE1_OFFSET; break; case SONYPI_DEVICE_TYP
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -