Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include <linux/delay.h>
- #include <linux/platform_device.h>
- #include <linux/input.h>
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/timer.h>
- #include <linux/dmi.h>
- #include <asm/io.h>
- #include <linux/leds.h>
- /* data port used by apple SMC */
- #define APPLESMC_DATA_PORT 0x300
- /* command/status port used by apple SMC */
- #define APPLESMC_CMD_PORT 0x304
- #define APPLESMC_NR_PORTS 5 /* 0x300-0x304 */
- #define APPLESMC_STATUS_MASK 0x0f
- #define APPLESMC_READ_CMD 0x10
- #define APPLESMC_WRITE_CMD 0x11
- #define CLAMSHELL_KEY "MSLD" //r-o length 1 (unused)
- #define FANS_COUNT "FNum" //r-o length 1
- #define FANS_MANUAL "FS! " //r-w length 2
- #define FAN_ACTUAL_SPEED "F0Ac" //r-o length 2
- #define FAN_MIN_SPEED "F0Mn" //r-o length 2
- #define FAN_MAX_SPEED "F0Mx" //r-o length 2
- #define FAN_SAFE_SPEED "F0Sf" //r-o length 2
- #define FAN_TARGET_SPEED "F0Tg" //r-w length 2
- #define INIT_TIMEOUT_MSECS 5000 /* wait up to 5s for device init ... */
- #define INIT_WAIT_MSECS 50 /* ... in 50ms increments */
- #define APPLESMC_POLL_PERIOD (HZ/20) /* poll for input every 1/20s */
- #define APPLESMC_INPUT_FUZZ 4 /* input event threshold */
- #define APPLESMC_INPUT_FLAT 4
- static int debug = 0;
- static struct platform_device *pdev;
- static struct mutex mutex = __MUTEX_INITIALIZER(mutex);
- static DECLARE_MUTEX(applesmc_sem);
- /*
- * __wait_status - Wait up to 100ms for the status port to get a certain value
- * (masked with 0x0f), returning zero if the value is obtained. Callers must
- * hold applesmc_sem.
- */
- static int __wait_status(u8 val)
- {
- unsigned int i;
- val = val & APPLESMC_STATUS_MASK;
- for (i = 0; i < 10000; i++) {
- if ((inb(APPLESMC_CMD_PORT) & APPLESMC_STATUS_MASK) == val)
- return 0;
- udelay(10);
- }
- printk(KERN_WARNING "applesmc: wait status failed: %x != %x\n",
- val, inb(APPLESMC_CMD_PORT));
- return -EIO;
- }
- /*
- * applesmc_read_key - reads len bytes from a given key, and put them in buffer.
- * Returns zero on success or a negative error on failure. Callers must
- * hold applesmc_sem.
- */
- static int applesmc_read_key(const char* key, u8* buffer, u8 len)
- {
- int ret = -EIO;
- int i;
- outb(APPLESMC_READ_CMD, APPLESMC_CMD_PORT);
- if (__wait_status(0x0c))
- goto out;
- for (i = 0; i < 4; i++) {
- outb(key[i], APPLESMC_DATA_PORT);
- if (__wait_status(0x04))
- goto out;
- }
- if (debug) printk(KERN_DEBUG "<%s", key);
- outb(len, APPLESMC_DATA_PORT);
- if (debug) printk(KERN_DEBUG ">%x", len);
- for (i = 0; i < len; i++) {
- if (__wait_status(0x05))
- goto out;
- buffer[i] = inb(APPLESMC_DATA_PORT);
- if (debug) printk(KERN_DEBUG "<%x", buffer[i]);
- }
- if (debug) printk(KERN_DEBUG "\n");
- ret = 0;
- out:
- return ret;
- }
- /*
- * applesmc_write_key - writes len bytes from buffer to a given key.
- * Returns zero on success or a negative error on failure. Callers must
- * hold applesmc_sem.
- */
- static int applesmc_write_key(const char* key, u8* buffer, u8 len)
- {
- int ret = -EIO;
- int i;
- outb(APPLESMC_WRITE_CMD, APPLESMC_CMD_PORT);
- if (__wait_status(0x0c))
- goto out;
- for (i = 0; i < 4; i++) {
- outb(key[i], APPLESMC_DATA_PORT);
- if (__wait_status(0x04))
- goto out;
- }
- outb(len, APPLESMC_DATA_PORT);
- for (i = 0; i < len; i++) {
- if (__wait_status(0x04))
- goto out;
- outb(buffer[i], APPLESMC_DATA_PORT);
- }
- ret = 0;
- out:
- return ret;
- }
- static int applesmc_device_init(void)
- {
- int total, ret = -ENXIO;
- u8 buffer[2];
- mutex_lock(&mutex);
- //down(&applesmc_sem);
- for (total = INIT_TIMEOUT_MSECS; total > 0; total -= INIT_WAIT_MSECS) {
- if (debug) printk(KERN_DEBUG "applesmc try %d\n", total);
- buffer[0] = 0xe0;
- buffer[1] = 0x00;
- msleep(INIT_WAIT_MSECS);
- }
- printk(KERN_WARNING "applesmc: failed to init the device\n");
- out:
- //up(&applesmc_sem);
- mutex_unlock(&mutex);
- return ret;
- }
- /*
- * applesmc_get_fan_count - get the number of fans. Callers must NOT hold
- * applesmc_sem.
- */
- static int applesmc_get_fan_count(void)
- {
- int ret;
- u8 buffer[1];
- //down(&applesmc_sem);
- mutex_lock(&mutex);
- ret = applesmc_read_key(FANS_COUNT, buffer, 1);
- //up(&applesmc_sem);
- mutex_unlock(&mutex);
- if (ret)
- return ret;
- else
- return buffer[0];
- }
- /* Device model stuff */
- static int applesmc_probe(struct platform_device *dev)
- {
- int ret;
- ret = applesmc_device_init();
- if (ret)
- return ret;
- printk(KERN_INFO "applesmc: device successfully initialized.\n");
- return 0;
- }
- static int applesmc_resume(struct platform_device *dev)
- {
- return applesmc_device_init();
- }
- static struct platform_driver applesmc_driver = {
- .probe = applesmc_probe,
- .resume = applesmc_resume,
- .driver = {
- .name = "applesmc",
- .owner = THIS_MODULE,
- },
- };
- static ssize_t applesmc_show_fan_speed(struct device *dev, char *sysfsbuf,
- const char* key, int offset)
- {
- int ret;
- unsigned int speed = 0;
- char newkey[5];
- u8 buffer[2];
- newkey[0] = key[0];
- newkey[1] = '0' + offset;
- newkey[2] = key[2];
- newkey[3] = key[3];
- newkey[4] = 0;
- //down(&applesmc_sem);
- mutex_lock(&mutex);
- ret = applesmc_read_key(newkey, buffer, 2);
- speed = ((buffer[0] << 8 | buffer[1]) >> 2);
- //up(&applesmc_sem);
- mutex_unlock(&mutex);
- if (ret)
- return ret;
- else
- return sprintf(sysfsbuf, "%u\n", speed);
- }
- static ssize_t applesmc_store_fan_speed(struct device *dev,
- const char *sysfsbuf, size_t count,
- const char* key, int offset)
- {
- int ret;
- u32 speed;
- char newkey[5];
- u8 buffer[2];
- speed = simple_strtoul(sysfsbuf, NULL, 10);
- if (speed > 0x4000) /* Bigger than a 14-bit value */
- return -EINVAL;
- newkey[0] = key[0];
- newkey[1] = '0' + offset;
- newkey[2] = key[2];
- newkey[3] = key[3];
- newkey[4] = 0;
- //down(&applesmc_sem);
- mutex_lock(&mutex);
- buffer[0] = (speed >> 6) & 0xff;
- buffer[1] = (speed << 2) & 0xff;
- ret = applesmc_write_key(newkey, buffer, 2);
- //up(&applesmc_sem);
- mutex_unlock(&mutex);
- if (ret)
- return ret;
- else
- return count;
- }
- static ssize_t applesmc_show_fan_manual(struct device *dev, char *sysfsbuf,
- int offset)
- {
- int ret;
- u16 manual = 0;
- u8 buffer[2];
- //down(&applesmc_sem);
- mutex_lock(&mutex);
- ret = applesmc_read_key(FANS_MANUAL, buffer, 2);
- manual = ((buffer[0] << 8 | buffer[1]) >> offset) & 0x01;
- //up(&applesmc_sem);
- mutex_unlock(&mutex);
- if (ret)
- return ret;
- else
- return sprintf(sysfsbuf, "%d\n", manual);
- }
- static ssize_t applesmc_store_fan_manual(struct device *dev,
- const char *sysfsbuf, size_t count, int offset)
- {
- int ret;
- u8 buffer[2];
- u32 input;
- u16 val;
- input = simple_strtoul(sysfsbuf, NULL, 10);
- //down(&applesmc_sem);
- mutex_lock(&mutex);
- ret = applesmc_read_key(FANS_MANUAL, buffer, 2);
- val = (buffer[0] << 8 | buffer[1]);
- if (ret)
- goto out;
- if (input)
- val = val | (0x01 << offset);
- else
- val = val & ~(0x01 << offset);
- buffer[0] = (val >> 8) & 0xFF;
- buffer[1] = val & 0xFF;
- ret = applesmc_write_key(FANS_MANUAL, buffer, 2);
- out:
- //up(&applesmc_sem);
- mutex_unlock(&mutex);
- if (ret)
- return ret;
- else
- return count;
- }
- /*
- * Macro defining helper functions and DEVICE_ATTR for a fan sysfs entries.
- * - show actual speed
- * - show/store minimum speed
- * - show maximum speed
- * - show safe speed
- * - show/store target speed
- * - show/store manual mode
- */
- #define sysfs_fan_speeds_offset(offset) \
- static ssize_t show_fan_actual_speed_##offset (struct device *dev, \
- struct device_attribute *attr, char *buf) \
- { \
- return applesmc_show_fan_speed(dev, buf, FAN_ACTUAL_SPEED, offset); \
- } \
- static DEVICE_ATTR(fan##offset##_actual_speed, S_IRUGO, \
- show_fan_actual_speed_##offset, NULL); \
- \
- static ssize_t show_fan_minimum_speed_##offset (struct device *dev, \
- struct device_attribute *attr, char *buf) \
- { \
- return applesmc_show_fan_speed(dev, buf, FAN_MIN_SPEED, offset); \
- } \
- static ssize_t store_fan_minimum_speed_##offset (struct device *dev, \
- struct device_attribute *attr, const char *buf, size_t count) \
- { \
- return applesmc_store_fan_speed(dev, buf, count, FAN_MIN_SPEED, offset); \
- } \
- static DEVICE_ATTR(fan##offset##_minimum_speed, S_IRUGO | S_IWUSR, \
- show_fan_minimum_speed_##offset, store_fan_minimum_speed_##offset); \
- \
- static ssize_t show_fan_maximum_speed_##offset (struct device *dev, \
- struct device_attribute *attr, char *buf) \
- { \
- return applesmc_show_fan_speed(dev, buf, FAN_MAX_SPEED, offset); \
- } \
- static DEVICE_ATTR(fan##offset##_maximum_speed, S_IRUGO, \
- show_fan_maximum_speed_##offset, NULL); \
- \
- static ssize_t show_fan_safe_speed_##offset (struct device *dev, \
- struct device_attribute *attr, char *buf) \
- { \
- return applesmc_show_fan_speed(dev, buf, FAN_SAFE_SPEED, offset); \
- } \
- static DEVICE_ATTR(fan##offset##_safe_speed, S_IRUGO, \
- show_fan_safe_speed_##offset, NULL); \
- \
- static ssize_t show_fan_target_speed_##offset (struct device *dev, \
- struct device_attribute *attr, char *buf) \
- { \
- return applesmc_show_fan_speed(dev, buf, FAN_TARGET_SPEED, offset); \
- } \
- static ssize_t store_fan_target_speed_##offset (struct device *dev, \
- struct device_attribute *attr, const char *buf, size_t count) \
- { \
- return applesmc_store_fan_speed(dev, buf, count, FAN_TARGET_SPEED, offset); \
- } \
- static DEVICE_ATTR(fan##offset##_target_speed, S_IRUGO | S_IWUSR, \
- show_fan_target_speed_##offset, store_fan_target_speed_##offset); \
- static ssize_t show_fan_manual_##offset (struct device *dev, \
- struct device_attribute *attr, char *buf) \
- { \
- return applesmc_show_fan_manual(dev, buf, offset); \
- } \
- static ssize_t store_fan_manual_##offset (struct device *dev, \
- struct device_attribute *attr, const char *buf, size_t count) \
- { \
- return applesmc_store_fan_manual(dev, buf, count, offset); \
- } \
- static DEVICE_ATTR(fan##offset##_manual, S_IRUGO | S_IWUSR, \
- show_fan_manual_##offset, store_fan_manual_##offset);
- /*
- * Create the needed functions for each fan using the macro defined above
- * (2 fans are supported)
- */
- sysfs_fan_speeds_offset(0);
- sysfs_fan_speeds_offset(1);
- /* Macro creating the sysfs entries for a fan */
- #define device_create_file_fan(ret, client, offset) \
- do { \
- ret = sysfs_create_file(client, &dev_attr_fan##offset##_actual_speed.attr); \
- if (ret) break; \
- ret = sysfs_create_file(client, &dev_attr_fan##offset##_minimum_speed.attr); \
- if (ret) break; \
- ret = sysfs_create_file(client, &dev_attr_fan##offset##_maximum_speed.attr); \
- if (ret) break; \
- ret = sysfs_create_file(client, &dev_attr_fan##offset##_safe_speed.attr); \
- if (ret) break; \
- ret = sysfs_create_file(client, &dev_attr_fan##offset##_target_speed.attr); \
- if (ret) break; \
- ret = sysfs_create_file(client, &dev_attr_fan##offset##_manual.attr); \
- } while (0)
- static int __init applesmc_init(void)
- {
- int ret;
- int count;
- if (!request_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS,
- "applesmc")) {
- ret = -ENXIO;
- goto out;
- }
- ret = platform_driver_register(&applesmc_driver);
- if (ret)
- goto out_region;
- pdev = platform_device_register_simple("applesmc", -1, NULL, 0);
- if (IS_ERR(pdev)) {
- ret = PTR_ERR(pdev);
- goto out_driver;
- }
- /* create fan files */
- count = applesmc_get_fan_count();
- if (count < 0) {
- printk(KERN_ERR "applesmc: Cannot get the number of fans.\n");
- }
- else {
- printk(KERN_INFO "applesmc: %d fans found.\n", count);
- switch (count) {
- default:
- printk(KERN_WARNING "applesmc: More than 2 fans found,"
- " but at most 2 fans are supported"
- " by the driver.\n");
- case 2:
- device_create_file_fan(ret, &pdev->dev.kobj, 1);
- if (ret)
- goto out_device;
- case 1:
- device_create_file_fan(ret, &pdev->dev.kobj, 0);
- if (ret)
- goto out_device;
- case 0:
- ;
- }
- }
- printk(KERN_INFO "applesmc: driver successfully loaded.\n");
- return 0;
- out_device:
- platform_device_unregister(pdev);
- out_driver:
- platform_driver_unregister(&applesmc_driver);
- out_region:
- release_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS);
- out:
- printk(KERN_WARNING "applesmc: driver init failed (ret=%d)!\n", ret);
- return ret;
- }
- static void __exit applesmc_exit(void)
- {
- platform_device_unregister(pdev);
- platform_driver_unregister(&applesmc_driver);
- release_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS);
- printk(KERN_INFO "applesmc: driver unloaded.\n");
- }
- module_init(applesmc_init);
- module_exit(applesmc_exit);
- MODULE_AUTHOR("Videot4pe");
- MODULE_DESCRIPTION("Fan control");
- MODULE_LICENSE("GPL v2");
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement