pm.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:8k
源码类别:

Linux/Unix编程

开发平台:

Unix_Linux

  1. /*
  2.  *  pm.c - Power management interface
  3.  *
  4.  *  Copyright (C) 2000 Andrew Henroid
  5.  *
  6.  *  This program is free software; you can redistribute it and/or modify
  7.  *  it under the terms of the GNU General Public License as published by
  8.  *  the Free Software Foundation; either version 2 of the License, or
  9.  *  (at your option) any later version.
  10.  *
  11.  *  This program is distributed in the hope that it will be useful,
  12.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  *  GNU General Public License for more details.
  15.  *
  16.  *  You should have received a copy of the GNU General Public License
  17.  *  along with this program; if not, write to the Free Software
  18.  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  19.  */
  20. #include <linux/module.h>
  21. #include <linux/spinlock.h>
  22. #include <linux/slab.h>
  23. #include <linux/pm.h>
  24. #include <linux/interrupt.h>
  25. int pm_active;
  26. /*
  27.  * Locking notes:
  28.  * pm_devs_lock can be a semaphore providing pm ops are not called
  29.  * from an interrupt handler (already a bad idea so no change here). Each
  30.  * change must be protected so that an unlink of an entry doesn't clash
  31.  * with a pm send - which is permitted to sleep in the current architecture
  32.  *
  33.  * Module unloads clashing with pm events now work out safely, the module 
  34.  * unload path will block until the event has been sent. It may well block
  35.  * until a resume but that will be fine.
  36.  */
  37.  
  38. static DECLARE_MUTEX(pm_devs_lock);
  39. static LIST_HEAD(pm_devs);
  40. /**
  41.  * pm_register - register a device with power management
  42.  * @type: device type 
  43.  * @id: device ID
  44.  * @callback: callback function
  45.  *
  46.  * Add a device to the list of devices that wish to be notified about
  47.  * power management events. A &pm_dev structure is returned on success,
  48.  * on failure the return is %NULL.
  49.  *
  50.  *      The callback function will be called in process context and
  51.  *      it may sleep.
  52.  */
  53.  
  54. struct pm_dev *pm_register(pm_dev_t type,
  55.    unsigned long id,
  56.    pm_callback callback)
  57. {
  58. struct pm_dev *dev = kmalloc(sizeof(struct pm_dev), GFP_KERNEL);
  59. if (dev) {
  60. memset(dev, 0, sizeof(*dev));
  61. dev->type = type;
  62. dev->id = id;
  63. dev->callback = callback;
  64. down(&pm_devs_lock);
  65. list_add(&dev->entry, &pm_devs);
  66. up(&pm_devs_lock);
  67. }
  68. return dev;
  69. }
  70. /**
  71.  * pm_unregister -  unregister a device with power management
  72.  * @dev: device to unregister
  73.  *
  74.  * Remove a device from the power management notification lists. The
  75.  * dev passed must be a handle previously returned by pm_register.
  76.  */
  77.  
  78. void pm_unregister(struct pm_dev *dev)
  79. {
  80. if (dev) {
  81. down(&pm_devs_lock);
  82. list_del(&dev->entry);
  83. up(&pm_devs_lock);
  84. kfree(dev);
  85. }
  86. }
  87. static void __pm_unregister(struct pm_dev *dev)
  88. {
  89. if (dev) {
  90. list_del(&dev->entry);
  91. kfree(dev);
  92. }
  93. }
  94. /**
  95.  * pm_unregister_all - unregister all devices with matching callback
  96.  * @callback: callback function pointer
  97.  *
  98.  * Unregister every device that would call the callback passed. This
  99.  * is primarily meant as a helper function for loadable modules. It
  100.  * enables a module to give up all its managed devices without keeping
  101.  * its own private list.
  102.  */
  103.  
  104. void pm_unregister_all(pm_callback callback)
  105. {
  106. struct list_head *entry;
  107. if (!callback)
  108. return;
  109. down(&pm_devs_lock);
  110. entry = pm_devs.next;
  111. while (entry != &pm_devs) {
  112. struct pm_dev *dev = list_entry(entry, struct pm_dev, entry);
  113. entry = entry->next;
  114. if (dev->callback == callback)
  115. __pm_unregister(dev);
  116. }
  117. up(&pm_devs_lock);
  118. }
  119. /**
  120.  * pm_send - send request to a single device
  121.  * @dev: device to send to
  122.  * @rqst: power management request
  123.  * @data: data for the callback
  124.  *
  125.  * Issue a power management request to a given device. The 
  126.  * %PM_SUSPEND and %PM_RESUME events are handled specially. The
  127.  * data field must hold the intended next state. No call is made
  128.  * if the state matches.
  129.  *
  130.  * BUGS: what stops two power management requests occuring in parallel
  131.  * and conflicting.
  132.  *
  133.  * WARNING: Calling pm_send directly is not generally recommended, in
  134.  * paticular there is no locking against the pm_dev going away. The
  135.  * caller must maintain all needed locking or have 'inside knowledge'
  136.  * on the safety. Also remember that this function is not locked against
  137.  * pm_unregister. This means that you must handle SMP races on callback
  138.  * execution and unload yourself.
  139.  */
  140.  
  141. int pm_send(struct pm_dev *dev, pm_request_t rqst, void *data)
  142. {
  143. int status = 0;
  144. int prev_state, next_state;
  145. if (in_interrupt())
  146. BUG();
  147. switch (rqst) {
  148. case PM_SUSPEND:
  149. case PM_RESUME:
  150. prev_state = dev->state;
  151. next_state = (unsigned long) data;
  152. if (prev_state != next_state) {
  153. if (dev->callback)
  154. status = (*dev->callback)(dev, rqst, data);
  155. if (!status) {
  156. dev->state = next_state;
  157. dev->prev_state = prev_state;
  158. }
  159. }
  160. else {
  161. dev->prev_state = prev_state;
  162. }
  163. break;
  164. default:
  165. if (dev->callback)
  166. status = (*dev->callback)(dev, rqst, data);
  167. break;
  168. }
  169. return status;
  170. }
  171. /*
  172.  * Undo incomplete request
  173.  */
  174. static void pm_undo_all(struct pm_dev *last)
  175. {
  176. struct list_head *entry = last->entry.prev;
  177. while (entry != &pm_devs) {
  178. struct pm_dev *dev = list_entry(entry, struct pm_dev, entry);
  179. if (dev->state != dev->prev_state) {
  180. /* previous state was zero (running) resume or
  181.  * previous state was non-zero (suspended) suspend
  182.  */
  183. pm_request_t undo = (dev->prev_state
  184.      ? PM_SUSPEND:PM_RESUME);
  185. pm_send(dev, undo, (void*) dev->prev_state);
  186. }
  187. entry = entry->prev;
  188. }
  189. }
  190. /**
  191.  * pm_send_all - send request to all managed devices
  192.  * @rqst: power management request
  193.  * @data: data for the callback
  194.  *
  195.  * Issue a power management request to a all devices. The 
  196.  * %PM_SUSPEND events are handled specially. Any device is 
  197.  * permitted to fail a suspend by returning a non zero (error)
  198.  * value from its callback function. If any device vetoes a 
  199.  * suspend request then all other devices that have suspended 
  200.  * during the processing of this request are restored to their
  201.  * previous state.
  202.  *
  203.  * WARNING:  This function takes the pm_devs_lock. The lock is not dropped until
  204.  * the callbacks have completed. This prevents races against pm locking
  205.  * functions, races against module unload pm_unregister code. It does
  206.  * mean however that you must not issue pm_ functions within the callback
  207.  * or you will deadlock and users will hate you.
  208.  *
  209.  * Zero is returned on success. If a suspend fails then the status
  210.  * from the device that vetoes the suspend is returned.
  211.  *
  212.  * BUGS: what stops two power management requests occuring in parallel
  213.  * and conflicting.
  214.  */
  215.  
  216. int pm_send_all(pm_request_t rqst, void *data)
  217. {
  218. struct list_head *entry;
  219. down(&pm_devs_lock);
  220. entry = pm_devs.next;
  221. while (entry != &pm_devs) {
  222. struct pm_dev *dev = list_entry(entry, struct pm_dev, entry);
  223. if (dev->callback) {
  224. int status = pm_send(dev, rqst, data);
  225. if (status) {
  226. /* return devices to previous state on
  227.  * failed suspend request
  228.  */
  229. if (rqst == PM_SUSPEND)
  230. pm_undo_all(dev);
  231. up(&pm_devs_lock);
  232. return status;
  233. }
  234. }
  235. entry = entry->next;
  236. }
  237. up(&pm_devs_lock);
  238. return 0;
  239. }
  240. /**
  241.  * pm_find  - find a device
  242.  * @type: type of device
  243.  * @from: where to start looking
  244.  *
  245.  * Scan the power management list for devices of a specific type. The
  246.  * return value for a matching device may be passed to further calls
  247.  * to this function to find further matches. A %NULL indicates the end
  248.  * of the list. 
  249.  *
  250.  * To search from the beginning pass %NULL as the @from value.
  251.  *
  252.  * The caller MUST hold the pm_devs_lock lock when calling this 
  253.  * function. The instant that the lock is dropped all pointers returned
  254.  * may become invalid.
  255.  */
  256.  
  257. struct pm_dev *pm_find(pm_dev_t type, struct pm_dev *from)
  258. {
  259. struct list_head *entry = from ? from->entry.next:pm_devs.next;
  260. while (entry != &pm_devs) {
  261. struct pm_dev *dev = list_entry(entry, struct pm_dev, entry);
  262. if (type == PM_UNKNOWN_DEV || dev->type == type)
  263. return dev;
  264. entry = entry->next;
  265. }
  266. return 0;
  267. }
  268. EXPORT_SYMBOL(pm_register);
  269. EXPORT_SYMBOL(pm_unregister);
  270. EXPORT_SYMBOL(pm_unregister_all);
  271. EXPORT_SYMBOL(pm_send);
  272. EXPORT_SYMBOL(pm_send_all);
  273. EXPORT_SYMBOL(pm_find);
  274. EXPORT_SYMBOL(pm_active);