~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Linux Cross Reference
Linux/kernel/pm.c

Version: ~ [ 2.4.0 ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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 
 21 #include <linux/module.h>
 22 #include <linux/spinlock.h>
 23 #include <linux/slab.h>
 24 #include <linux/pm.h>
 25 
 26 int pm_active;
 27 
 28 static spinlock_t pm_devs_lock = SPIN_LOCK_UNLOCKED;
 29 static LIST_HEAD(pm_devs);
 30 
 31 /**
 32  *      pm_register - register a device with power management
 33  *      @type: device type 
 34  *      @id: device ID
 35  *      @callback: callback function
 36  *
 37  *      Add a device to the list of devices that wish to be notified about
 38  *      power management events. A &pm_dev structure is returned on success,
 39  *      on failure the return is %NULL.
 40  */
 41  
 42 struct pm_dev *pm_register(pm_dev_t type,
 43                            unsigned long id,
 44                            pm_callback callback)
 45 {
 46         struct pm_dev *dev = kmalloc(sizeof(struct pm_dev), GFP_KERNEL);
 47         if (dev) {
 48                 unsigned long flags;
 49 
 50                 memset(dev, 0, sizeof(*dev));
 51                 dev->type = type;
 52                 dev->id = id;
 53                 dev->callback = callback;
 54 
 55                 spin_lock_irqsave(&pm_devs_lock, flags);
 56                 list_add(&dev->entry, &pm_devs);
 57                 spin_unlock_irqrestore(&pm_devs_lock, flags);
 58         }
 59         return dev;
 60 }
 61 
 62 /**
 63  *      pm_unregister -  unregister a device with power management
 64  *      @dev: device to unregister
 65  *
 66  *      Remove a device from the power management notification lists. The
 67  *      dev passed must be a handle previously returned by pm_register.
 68  */
 69  
 70 void pm_unregister(struct pm_dev *dev)
 71 {
 72         if (dev) {
 73                 unsigned long flags;
 74 
 75                 spin_lock_irqsave(&pm_devs_lock, flags);
 76                 list_del(&dev->entry);
 77                 spin_unlock_irqrestore(&pm_devs_lock, flags);
 78 
 79                 kfree(dev);
 80         }
 81 }
 82 
 83 /**
 84  *      pm_unregister_all - unregister all devices with matching callback
 85  *      @callback: callback function pointer
 86  *
 87  *      Unregister every device that would call the callback passed. This
 88  *      is primarily meant as a helper function for loadable modules. It
 89  *      enables a module to give up all its managed devices without keeping
 90  *      its own private list.
 91  */
 92  
 93 void pm_unregister_all(pm_callback callback)
 94 {
 95         struct list_head *entry;
 96 
 97         if (!callback)
 98                 return;
 99 
100         entry = pm_devs.next;
101         while (entry != &pm_devs) {
102                 struct pm_dev *dev = list_entry(entry, struct pm_dev, entry);
103                 entry = entry->next;
104                 if (dev->callback == callback)
105                         pm_unregister(dev);
106         }
107 }
108 
109 /**
110  *      pm_send - send request to a single device
111  *      @dev: device to send to
112  *      @rqst: power management request
113  *      @data: data for the callback
114  *
115  *      Issue a power management request to a given device. The 
116  *      %PM_SUSPEND and %PM_RESUME events are handled specially. The
117  *      data field must hold the intended next state. No call is made
118  *      if the state matches.
119  *
120  *      BUGS: what stops two power management requests occuring in parallel
121  *      and conflicting.
122  */
123  
124 int pm_send(struct pm_dev *dev, pm_request_t rqst, void *data)
125 {
126         int status = 0;
127         int prev_state, next_state;
128         switch (rqst) {
129         case PM_SUSPEND:
130         case PM_RESUME:
131                 prev_state = dev->state;
132                 next_state = (int) data;
133                 if (prev_state != next_state) {
134                         if (dev->callback)
135                                 status = (*dev->callback)(dev, rqst, data);
136                         if (!status) {
137                                 dev->state = next_state;
138                                 dev->prev_state = prev_state;
139                         }
140                 }
141                 else {
142                         dev->prev_state = prev_state;
143                 }
144                 break;
145         default:
146                 if (dev->callback)
147                         status = (*dev->callback)(dev, rqst, data);
148                 break;
149         }
150         return status;
151 }
152 
153 /*
154  * Undo incomplete request
155  */
156 static void pm_undo_all(struct pm_dev *last)
157 {
158         struct list_head *entry = last->entry.prev;
159         while (entry != &pm_devs) {
160                 struct pm_dev *dev = list_entry(entry, struct pm_dev, entry);
161                 if (dev->state != dev->prev_state) {
162                         /* previous state was zero (running) resume or
163                          * previous state was non-zero (suspended) suspend
164                          */
165                         pm_request_t undo = (dev->prev_state
166                                              ? PM_SUSPEND:PM_RESUME);
167                         pm_send(dev, undo, (void*) dev->prev_state);
168                 }
169                 entry = entry->prev;
170         }
171 }
172 
173 /**
174  *      pm_send_all - send request to all managed devices
175  *      @rqst: power management request
176  *      @data: data for the callback
177  *
178  *      Issue a power management request to a all devices. The 
179  *      %PM_SUSPEND events are handled specially. Any device is 
180  *      permitted to fail a suspend by returning a non zero (error)
181  *      value from its callback function. If any device vetoes a 
182  *      suspend request then all other devices that have suspended 
183  *      during the processing of this request are restored to their
184  *      previous state.
185  *
186  *      Zero is returned on success. If a suspend fails then the status
187  *      from the device that vetoes the suspend is returned.
188  *
189  *      BUGS: what stops two power management requests occuring in parallel
190  *      and conflicting.
191  */
192  
193 int pm_send_all(pm_request_t rqst, void *data)
194 {
195         struct list_head *entry = pm_devs.next;
196         while (entry != &pm_devs) {
197                 struct pm_dev *dev = list_entry(entry, struct pm_dev, entry);
198                 if (dev->callback) {
199                         int status = pm_send(dev, rqst, data);
200                         if (status) {
201                                 /* return devices to previous state on
202                                  * failed suspend request
203                                  */
204                                 if (rqst == PM_SUSPEND)
205                                         pm_undo_all(dev);
206                                 return status;
207                         }
208                 }
209                 entry = entry->next;
210         }
211         return 0;
212 }
213 
214 /**
215  *      pm_find  - find a device
216  *      @type: type of device
217  *      @from: where to start looking
218  *
219  *      Scan the power management list for devices of a specific type. The
220  *      return value for a matching device may be passed to further calls
221  *      to this function to find further matches. A %NULL indicates the end
222  *      of the list. 
223  *
224  *      To search from the beginning pass %NULL as the @from value.
225  */
226  
227 struct pm_dev *pm_find(pm_dev_t type, struct pm_dev *from)
228 {
229         struct list_head *entry = from ? from->entry.next:pm_devs.next;
230         while (entry != &pm_devs) {
231                 struct pm_dev *dev = list_entry(entry, struct pm_dev, entry);
232                 if (type == PM_UNKNOWN_DEV || dev->type == type)
233                         return dev;
234                 entry = entry->next;
235         }
236         return 0;
237 }
238 
239 EXPORT_SYMBOL(pm_register);
240 EXPORT_SYMBOL(pm_unregister);
241 EXPORT_SYMBOL(pm_unregister_all);
242 EXPORT_SYMBOL(pm_send);
243 EXPORT_SYMBOL(pm_send_all);
244 EXPORT_SYMBOL(pm_find);
245 EXPORT_SYMBOL(pm_active);
246 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.