OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [drivers/] [char/] [ipmi/] [ipmi_devintf.c] - Blame information for rev 1774

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1275 phoenix
/*
2
 * ipmi_devintf.c
3
 *
4
 * Linux device interface for the IPMI message handler.
5
 *
6
 * Author: MontaVista Software, Inc.
7
 *         Corey Minyard <minyard@mvista.com>
8
 *         source@mvista.com
9
 *
10
 * Copyright 2002 MontaVista Software Inc.
11
 *
12
 *  This program is free software; you can redistribute it and/or modify it
13
 *  under the terms of the GNU General Public License as published by the
14
 *  Free Software Foundation; either version 2 of the License, or (at your
15
 *  option) any later version.
16
 *
17
 *
18
 *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
19
 *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
20
 *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21
 *  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22
 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
23
 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
24
 *  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25
 *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
26
 *  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
27
 *  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
 *
29
 *  You should have received a copy of the GNU General Public License along
30
 *  with this program; if not, write to the Free Software Foundation, Inc.,
31
 *  675 Mass Ave, Cambridge, MA 02139, USA.
32
 */
33
 
34
#include <linux/config.h>
35
#include <linux/module.h>
36
#include <linux/errno.h>
37
#include <asm/system.h>
38
#include <linux/sched.h>
39
#include <linux/poll.h>
40
#include <linux/spinlock.h>
41
#include <linux/slab.h>
42
#include <linux/devfs_fs_kernel.h>
43
#include <linux/ipmi.h>
44
#include <asm/semaphore.h>
45
#include <linux/init.h>
46
 
47
struct ipmi_file_private
48
{
49
        ipmi_user_t          user;
50
        spinlock_t           recv_msg_lock;
51
        struct list_head     recv_msgs;
52
        struct file          *file;
53
        struct fasync_struct *fasync_queue;
54
        wait_queue_head_t    wait;
55
        struct semaphore     recv_sem;
56
};
57
 
58
static void file_receive_handler(struct ipmi_recv_msg *msg,
59
                                 void                 *handler_data)
60
{
61
        struct ipmi_file_private *priv = handler_data;
62
        int                      was_empty;
63
        unsigned long            flags;
64
 
65
        spin_lock_irqsave(&(priv->recv_msg_lock), flags);
66
 
67
        was_empty = list_empty(&(priv->recv_msgs));
68
        list_add_tail(&(msg->link), &(priv->recv_msgs));
69
 
70
        if (was_empty) {
71
                wake_up_interruptible(&priv->wait);
72
                kill_fasync(&priv->fasync_queue, SIGIO, POLL_IN);
73
        }
74
 
75
        spin_unlock_irqrestore(&(priv->recv_msg_lock), flags);
76
}
77
 
78
static unsigned int ipmi_poll(struct file *file, poll_table *wait)
79
{
80
        struct ipmi_file_private *priv = file->private_data;
81
        unsigned int             mask = 0;
82
        unsigned long            flags;
83
 
84
        poll_wait(file, &priv->wait, wait);
85
 
86
        spin_lock_irqsave(&priv->recv_msg_lock, flags);
87
 
88
        if (! list_empty(&(priv->recv_msgs)))
89
                mask |= (POLLIN | POLLRDNORM);
90
 
91
        spin_unlock_irqrestore(&priv->recv_msg_lock, flags);
92
 
93
        return mask;
94
}
95
 
96
static int ipmi_fasync(int fd, struct file *file, int on)
97
{
98
        struct ipmi_file_private *priv = file->private_data;
99
        int                      result;
100
 
101
        result = fasync_helper(fd, file, on, &priv->fasync_queue);
102
 
103
        return (result);
104
}
105
 
106
static struct ipmi_user_hndl ipmi_hndlrs =
107
{
108
        ipmi_recv_hndl : file_receive_handler
109
};
110
 
111
static int ipmi_open(struct inode *inode, struct file *file)
112
{
113
        int                      if_num = minor(inode->i_rdev);
114
        int                      rv;
115
        struct ipmi_file_private *priv;
116
 
117
 
118
        priv = kmalloc(sizeof(*priv), GFP_KERNEL);
119
        if (!priv)
120
                return -ENOMEM;
121
 
122
        priv->file = file;
123
 
124
        rv = ipmi_create_user(if_num,
125
                              &ipmi_hndlrs,
126
                              priv,
127
                              &(priv->user));
128
        if (rv) {
129
                kfree(priv);
130
                return rv;
131
        }
132
 
133
        file->private_data = priv;
134
 
135
        spin_lock_init(&(priv->recv_msg_lock));
136
        INIT_LIST_HEAD(&(priv->recv_msgs));
137
        init_waitqueue_head(&priv->wait);
138
        priv->fasync_queue = NULL;
139
        sema_init(&(priv->recv_sem), 1);
140
 
141
        return 0;
142
}
143
 
144
static int ipmi_release(struct inode *inode, struct file *file)
145
{
146
        struct ipmi_file_private *priv = file->private_data;
147
        int                      rv;
148
 
149
        rv = ipmi_destroy_user(priv->user);
150
        if (rv)
151
                return rv;
152
 
153
        ipmi_fasync (-1, file, 0);
154
 
155
        /* FIXME - free the messages in the list. */
156
        kfree(priv);
157
 
158
        return 0;
159
}
160
 
161
static int ipmi_ioctl(struct inode  *inode,
162
                      struct file   *file,
163
                      unsigned int  cmd,
164
                      unsigned long data)
165
{
166
        int                      rv = -EINVAL;
167
        struct ipmi_file_private *priv = file->private_data;
168
 
169
        switch (cmd)
170
        {
171
        case IPMICTL_SEND_COMMAND:
172
        {
173
                struct ipmi_req    req;
174
                struct ipmi_addr   addr;
175
                unsigned char msgdata[IPMI_MAX_MSG_LENGTH];
176
 
177
                if (copy_from_user(&req, (void *) data, sizeof(req))) {
178
                        rv = -EFAULT;
179
                        break;
180
                }
181
 
182
                if (req.addr_len > sizeof(struct ipmi_addr))
183
                {
184
                        rv = -EINVAL;
185
                        break;
186
                }
187
 
188
                if (copy_from_user(&addr, req.addr, req.addr_len)) {
189
                        rv = -EFAULT;
190
                        break;
191
                }
192
 
193
                rv = ipmi_validate_addr(&addr, req.addr_len);
194
                if (rv)
195
                        break;
196
 
197
                if (req.msg.data != NULL) {
198
                        if (req.msg.data_len > IPMI_MAX_MSG_LENGTH) {
199
                                rv = -EMSGSIZE;
200
                                break;
201
                        }
202
 
203
                        if (copy_from_user(&msgdata,
204
                                           req.msg.data,
205
                                           req.msg.data_len))
206
                        {
207
                                rv = -EFAULT;
208
                                break;
209
                        }
210
                } else {
211
                        req.msg.data_len = 0;
212
                }
213
 
214
                req.msg.data = msgdata;
215
 
216
                rv = ipmi_request(priv->user,
217
                                  &addr,
218
                                  req.msgid,
219
                                  &(req.msg),
220
                                  0);
221
                break;
222
        }
223
 
224
        case IPMICTL_RECEIVE_MSG:
225
        case IPMICTL_RECEIVE_MSG_TRUNC:
226
        {
227
                struct ipmi_recv      rsp;
228
                int              addr_len;
229
                struct list_head *entry;
230
                struct ipmi_recv_msg  *msg;
231
                unsigned long    flags;
232
 
233
 
234
                rv = 0;
235
                if (copy_from_user(&rsp, (void *) data, sizeof(rsp))) {
236
                        rv = -EFAULT;
237
                        break;
238
                }
239
 
240
                /* We claim a semaphore because we don't want two
241
                   users getting something from the queue at a time.
242
                   Since we have to release the spinlock before we can
243
                   copy the data to the user, it's possible another
244
                   user will grab something from the queue, too.  Then
245
                   the messages might get out of order if something
246
                   fails and the message gets put back onto the
247
                   queue.  This semaphore prevents that problem. */
248
                down(&(priv->recv_sem));
249
 
250
                /* Grab the message off the list. */
251
                spin_lock_irqsave(&(priv->recv_msg_lock), flags);
252
                if (list_empty(&(priv->recv_msgs))) {
253
                        spin_unlock_irqrestore(&(priv->recv_msg_lock), flags);
254
                        rv = -EAGAIN;
255
                        goto recv_err;
256
                }
257
                entry = priv->recv_msgs.next;
258
                msg = list_entry(entry, struct ipmi_recv_msg, link);
259
                list_del(entry);
260
                spin_unlock_irqrestore(&(priv->recv_msg_lock), flags);
261
 
262
                addr_len = ipmi_addr_length(msg->addr.addr_type);
263
                if (rsp.addr_len < addr_len)
264
                {
265
                        rv = -EINVAL;
266
                        goto recv_putback_on_err;
267
                }
268
 
269
                if (copy_to_user(rsp.addr, &(msg->addr), addr_len)) {
270
                        rv = -EFAULT;
271
                        goto recv_putback_on_err;
272
                }
273
                rsp.addr_len = addr_len;
274
 
275
                rsp.recv_type = msg->recv_type;
276
                rsp.msgid = msg->msgid;
277
                rsp.msg.netfn = msg->msg.netfn;
278
                rsp.msg.cmd = msg->msg.cmd;
279
 
280
                if (msg->msg.data_len > 0) {
281
                        if (rsp.msg.data_len < msg->msg.data_len) {
282
                                rv = -EMSGSIZE;
283
                                if (cmd == IPMICTL_RECEIVE_MSG_TRUNC) {
284
                                        msg->msg.data_len = rsp.msg.data_len;
285
                                } else {
286
                                        goto recv_putback_on_err;
287
                                }
288
                        }
289
 
290
                        if (copy_to_user(rsp.msg.data,
291
                                         msg->msg.data,
292
                                         msg->msg.data_len))
293
                        {
294
                                rv = -EFAULT;
295
                                goto recv_putback_on_err;
296
                        }
297
                        rsp.msg.data_len = msg->msg.data_len;
298
                } else {
299
                        rsp.msg.data_len = 0;
300
                }
301
 
302
                if (copy_to_user((void *) data, &rsp, sizeof(rsp))) {
303
                        rv = -EFAULT;
304
                        goto recv_putback_on_err;
305
                }
306
 
307
                up(&(priv->recv_sem));
308
                ipmi_free_recv_msg(msg);
309
                break;
310
 
311
        recv_putback_on_err:
312
                /* If we got an error, put the message back onto
313
                   the head of the queue. */
314
                spin_lock_irqsave(&(priv->recv_msg_lock), flags);
315
                list_add(entry, &(priv->recv_msgs));
316
                spin_unlock_irqrestore(&(priv->recv_msg_lock), flags);
317
                up(&(priv->recv_sem));
318
                break;
319
 
320
        recv_err:
321
                up(&(priv->recv_sem));
322
                break;
323
        }
324
 
325
        case IPMICTL_REGISTER_FOR_CMD:
326
        {
327
                struct ipmi_cmdspec val;
328
 
329
                if (copy_from_user(&val, (void *) data, sizeof(val))) {
330
                        rv = -EFAULT;
331
                        break;
332
                }
333
 
334
                rv = ipmi_register_for_cmd(priv->user, val.netfn, val.cmd);
335
                break;
336
        }
337
 
338
        case IPMICTL_UNREGISTER_FOR_CMD:
339
        {
340
                struct ipmi_cmdspec   val;
341
 
342
                if (copy_from_user(&val, (void *) data, sizeof(val))) {
343
                        rv = -EFAULT;
344
                        break;
345
                }
346
 
347
                rv = ipmi_unregister_for_cmd(priv->user, val.netfn, val.cmd);
348
                break;
349
        }
350
 
351
        case IPMICTL_SET_GETS_EVENTS_CMD:
352
        {
353
                int val;
354
 
355
                if (copy_from_user(&val, (void *) data, sizeof(val))) {
356
                        rv = -EFAULT;
357
                        break;
358
                }
359
 
360
                rv = ipmi_set_gets_events(priv->user, val);
361
                break;
362
        }
363
 
364
        case IPMICTL_SET_MY_ADDRESS_CMD:
365
        {
366
                unsigned int val;
367
 
368
                if (copy_from_user(&val, (void *) data, sizeof(val))) {
369
                        rv = -EFAULT;
370
                        break;
371
                }
372
 
373
                ipmi_set_my_address(priv->user, val);
374
                rv = 0;
375
                break;
376
        }
377
 
378
        case IPMICTL_GET_MY_ADDRESS_CMD:
379
        {
380
                unsigned int val;
381
 
382
                val = ipmi_get_my_address(priv->user);
383
 
384
                if (copy_to_user((void *) data, &val, sizeof(val))) {
385
                        rv = -EFAULT;
386
                        break;
387
                }
388
                rv = 0;
389
                break;
390
        }
391
 
392
        case IPMICTL_SET_MY_LUN_CMD:
393
        {
394
                unsigned int val;
395
 
396
                if (copy_from_user(&val, (void *) data, sizeof(val))) {
397
                        rv = -EFAULT;
398
                        break;
399
                }
400
 
401
                ipmi_set_my_LUN(priv->user, val);
402
                rv = 0;
403
                break;
404
        }
405
 
406
        case IPMICTL_GET_MY_LUN_CMD:
407
        {
408
                unsigned int val;
409
 
410
                val = ipmi_get_my_LUN(priv->user);
411
 
412
                if (copy_to_user((void *) data, &val, sizeof(val))) {
413
                        rv = -EFAULT;
414
                        break;
415
                }
416
                rv = 0;
417
                break;
418
        }
419
 
420
        }
421
 
422
        return rv;
423
}
424
 
425
 
426
static struct file_operations ipmi_fops = {
427
        owner:   THIS_MODULE,
428
        ioctl:   ipmi_ioctl,
429
        open:    ipmi_open,
430
        release: ipmi_release,
431
        fasync:  ipmi_fasync,
432
        poll:    ipmi_poll
433
};
434
 
435
#define DEVICE_NAME     "ipmidev"
436
 
437
static int ipmi_major = 0;
438
MODULE_PARM(ipmi_major, "i");
439
 
440
static devfs_handle_t devfs_handle;
441
 
442
#define MAX_DEVICES 10
443
static devfs_handle_t handles[MAX_DEVICES];
444
 
445
static void ipmi_new_smi(int if_num)
446
{
447
        char name[2];
448
 
449
        if (if_num > MAX_DEVICES)
450
                return;
451
 
452
        name[0] = if_num + '0';
453
        name[1] = '\0';
454
 
455
        handles[if_num] = devfs_register(devfs_handle, name, DEVFS_FL_NONE,
456
                                         ipmi_major, if_num,
457
                                         S_IFCHR | S_IRUSR | S_IWUSR,
458
                                         &ipmi_fops, NULL);
459
}
460
 
461
static void ipmi_smi_gone(int if_num)
462
{
463
        if (if_num > MAX_DEVICES)
464
                return;
465
 
466
        devfs_unregister(handles[if_num]);
467
}
468
 
469
static struct ipmi_smi_watcher smi_watcher =
470
{
471
        new_smi  : ipmi_new_smi,
472
        smi_gone : ipmi_smi_gone
473
};
474
 
475
static __init int init_ipmi_devintf(void)
476
{
477
        int rv;
478
 
479
        if (ipmi_major < 0)
480
                return -EINVAL;
481
 
482
        rv = register_chrdev(ipmi_major, DEVICE_NAME, &ipmi_fops);
483
        if (rv < 0) {
484
                printk(KERN_ERR "ipmi: can't get major %d\n", ipmi_major);
485
                return rv;
486
        }
487
 
488
        if (ipmi_major == 0) {
489
                ipmi_major = rv;
490
        }
491
 
492
        devfs_handle = devfs_mk_dir(NULL, DEVICE_NAME, NULL);
493
 
494
        rv = ipmi_smi_watcher_register(&smi_watcher);
495
        if (rv) {
496
                unregister_chrdev(ipmi_major, DEVICE_NAME);
497
                printk(KERN_WARNING "ipmi: can't register smi watcher");
498
                return rv;
499
        }
500
 
501
        printk(KERN_INFO "ipmi: device interface at char major %d\n",
502
               ipmi_major);
503
 
504
        return 0;
505
}
506
module_init(init_ipmi_devintf);
507
 
508
static __exit void cleanup_ipmi(void)
509
{
510
        ipmi_smi_watcher_unregister(&smi_watcher);
511
        devfs_unregister(devfs_handle);
512
        unregister_chrdev(ipmi_major, DEVICE_NAME);
513
}
514
module_exit(cleanup_ipmi);
515
#ifndef MODULE
516
static __init int ipmi_setup (char *str)
517
{
518
        int x;
519
 
520
        if (get_option (&str, &x)) {
521
                /* ipmi=x sets the major number to x. */
522
                ipmi_major = x;
523
        } else if (!strcmp(str, "off")) {
524
                ipmi_major = -1;
525
        }
526
 
527
        return 1;
528
}
529
#endif
530
 
531
__setup("ipmi=", ipmi_setup);
532
MODULE_LICENSE("GPL");

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.