From: Jean-Christophe DUBOIS This is an example driver that will allow an application to send/receive mailboxes between the host and the Cell. Signed-off-by: Jean-Christophe DUBOIS --- Index: linux/drivers/axon/usr/include/axon_usr_mbx.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux/drivers/axon/usr/include/axon_usr_mbx.h 2006-12-19 18:06:48.000000000 +0100 @@ -0,0 +1,47 @@ +/****************************************************************** + * Copyright (C) 2006 Mercury Computer Systems, Inc. + * 199 Riverneck Road + * Chelmsford, MA 01824-2820 + * (978) 256-1300 + * webinfo@mc.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * http://www.gnu.org/copyleft/gpl.html + ******************************************************************/ + + +#ifndef AXON_USR_MBX_H +#define AXON_USR_MBX_H + +#include + +#include "axon_types.h" + +#define MC_AXON_MBX_MAGIC 'M' +#define MC_AXON_PROCFS_NAME "axon" + + +#define MC_AXON_SUBSCRIBE_CHANNEL _IOW(MC_AXON_MBX_MAGIC, 1, int) +#define MC_AXON_UNSUBSCRIBE_CHANNEL _IOW(MC_AXON_MBX_MAGIC, 2, int) + +#define MC_AXON_MBX_MAXNR 2 + +#define MC_AXON_USR_MIN_CHANNEL 0x06 +#define MC_AXON_USR_MAX_CHANNEL 0x7E + +#define MC_AXON_USR_MSG_LEN 15 + +#endif Index: linux/drivers/axon/usr/mbx/axon_usr_mbx.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux/drivers/axon/usr/mbx/axon_usr_mbx.c 2006-12-19 18:05:31.000000000 +0100 @@ -0,0 +1,596 @@ +/****************************************************************** + * Copyright (C) 2006 Mercury Computer Systems, Inc. + * 199 Riverneck Road + * Chelmsford, MA 01824-2820 + * (978) 256-1300 + * webinfo@mc.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * http://www.gnu.org/copyleft/gpl.html + ******************************************************************/ + + + + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define AXON_USR_MBX_NO_IOCTL_TEST + + +struct axon_usr_mbx_t { + + struct cdev cdev; + + + dev_t cdev_num; + + + struct axon_t *p_axon; + + + int major; + + + u8 id; + + + struct axon_mbox_t *peer_mbox; + + + struct axon_sms_t *sms; +}; + + +struct axon_file_usr_mbx_t { + + u32 mbx_count; + + + u32 read_index; + + + u32 write_index; + + + struct axon_sms_msg_t *buffer; + + + u8 channel; + + + wait_queue_head_t waitq; + + + spinlock_t lock; + + + struct axon_usr_mbx_t *p_axon_usr; +}; + +MODULE_DESCRIPTION("user space MBX driver"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("Jean-Christophe Dubois (jdubois@mc.com)"); + + +#if defined(AXON_DEBUG_MBX) +#define dbg_mbx_log printk(KERN_DEBUG "AXON_MBX:%s=>", __FUNCTION__);printk +#else +#define dbg_mbx_log if (0) printk +#endif + +#define dbg_mbx_err printk(KERN_EMERG "AXON_MBX:%s=>", __FUNCTION__);printk +#define dbg_mbx_inf printk(KERN_INFO "AXON_MBX:%s=>", __FUNCTION__);printk + + +static int axon_usr_devs_count = 0; +static struct axon_usr_mbx_t **pp_axon_usr; + + +static int +axon_mbx_inc_index(int index) +{ + dbg_mbx_log("in %s()\n", __FUNCTION__); + + index++; + if ((index * sizeof(struct axon_sms_msg_t)) > PAGE_SIZE) + index = 0; + + return index; +} + + +static int +axon_mbx_sms_handler(void *context, struct axon_sms_msg_t *msg) +{ + int ret = 0; + struct axon_file_usr_mbx_t *p_usr_file = + (struct axon_file_usr_mbx_t *) (context); + + dbg_mbx_log("in %s()\n", __FUNCTION__); + + + memcpy(p_usr_file->buffer + p_usr_file->write_index, msg, + sizeof(struct axon_sms_msg_t)); + + + spin_lock_bh(&p_usr_file->lock); + + p_usr_file->write_index = + axon_mbx_inc_index(p_usr_file->write_index); + + p_usr_file->mbx_count++; + + spin_unlock_bh(&p_usr_file->lock); + + + wake_up(&p_usr_file->waitq); + + return ret; +} + +static int +axon_mbx_unsubscribe(struct axon_file_usr_mbx_t *p_usr_file) +{ + int ret = 0; + + dbg_mbx_log("in %s()\n", __FUNCTION__); + + if (p_usr_file->channel < AXON_SMS_CHANNEL_NR) { + axon_sms_unsubscribe(p_usr_file->p_axon_usr->sms, + p_usr_file->channel, + axon_mbx_sms_handler); + + p_usr_file->channel = AXON_SMS_CHANNEL_INVALID; + } + + return ret; +} + +static int +axon_mbx_subscribe(struct axon_file_usr_mbx_t *p_usr_file, + unsigned long arg) +{ + int ret = 0; + + dbg_mbx_log("in %s()\n", __FUNCTION__); + dbg_mbx_log("requesting channel %ld\n", arg); + + if (arg >= AXON_SMS_CHANNEL_NR) { + dbg_mbx_err("bad channel number %ld \n", arg); + ret = -EINVAL; + goto out; + } + + if (p_usr_file->channel != AXON_SMS_CHANNEL_INVALID) { + axon_mbx_unsubscribe(p_usr_file); + } + + + + ret = axon_sms_subscribe(p_usr_file->p_axon_usr->sms, arg, + axon_mbx_sms_handler, p_usr_file); + if (ret < 0) { + dbg_mbx_err("Unable to subscribe to channel %d \n", + p_usr_file->channel); + goto out; + } + + p_usr_file->channel = arg; + + out: + return ret; +} + +static int +axon_mbx_subscribe_from_user(struct axon_file_usr_mbx_t *p_usr_file, + unsigned long arg) +{ + int ret = 0; + + dbg_mbx_log("in %s()\n", __FUNCTION__); + dbg_mbx_log("requesting channel %ld\n", arg); + + if ((arg < MC_AXON_USR_MIN_CHANNEL) + || (arg > MC_AXON_USR_MAX_CHANNEL)) { + ret = -EINVAL; + } else { + ret = axon_mbx_subscribe(p_usr_file, arg); + } + + return ret; +} + + +static ssize_t +axon_mbx_read(struct file *file, char __user * buff, size_t len, + loff_t * offset) +{ + int ret = 0; + struct axon_file_usr_mbx_t *p_usr_file = + (struct axon_file_usr_mbx_t *) (file->private_data); + + dbg_mbx_log("in %s()\n", __FUNCTION__); + +#ifdef AXON_USR_MBX_NO_IOCTL_TEST + if (p_usr_file->channel == AXON_SMS_CHANNEL_INVALID) { + ret = + axon_mbx_subscribe(p_usr_file, + AXON_SMS_CHANNEL_USR_MBX); + + if (ret < 0) + return ret; + } +#endif + + if (p_usr_file->channel >= AXON_SMS_CHANNEL_NR) + return -EINVAL; + + + if (len < AXON_SMS_PAYLOAD_SIZE) + return -EINVAL; + + + if (p_usr_file->mbx_count == 0) { + ret = wait_event_interruptible(p_usr_file->waitq, + p_usr_file->mbx_count != 0); + if (ret < 0) { + return ret; + } + } + + + while ((p_usr_file->mbx_count != 0) + && (len >= AXON_SMS_PAYLOAD_SIZE)) { + struct axon_sms_msg_t *p_msg; + + p_msg = p_usr_file->buffer + p_usr_file->read_index; + + + __copy_to_user(buff, &p_msg->payload, + AXON_SMS_PAYLOAD_SIZE); + + + spin_lock_bh(&p_usr_file->lock); + + p_usr_file->mbx_count--; + p_usr_file->read_index = + axon_mbx_inc_index(p_usr_file->read_index); + + spin_unlock_bh(&p_usr_file->lock); + + buff += AXON_SMS_PAYLOAD_SIZE; + ret += AXON_SMS_PAYLOAD_SIZE; + len -= AXON_SMS_PAYLOAD_SIZE; + } + + return ret; +} + + +static ssize_t +axon_mbx_write(struct file *file, const char __user * buff, size_t len, + loff_t * offset) +{ + int ret = 0; + struct axon_file_usr_mbx_t *p_usr_file = + (struct axon_file_usr_mbx_t *) (file->private_data); + struct axon_sms_msg_t msg; + + dbg_mbx_log("in %s()\n", __FUNCTION__); + +#ifdef AXON_USR_MBX_NO_IOCTL_TEST + if (p_usr_file->channel == AXON_SMS_CHANNEL_INVALID) { + ret = + axon_mbx_subscribe(p_usr_file, + AXON_SMS_CHANNEL_USR_MBX); + + if (ret < 0) + return ret; + } +#endif + + if (p_usr_file->channel >= AXON_SMS_CHANNEL_NR) + return -EINVAL; + + + if (len < AXON_SMS_PAYLOAD_SIZE) + return -EINVAL; + + msg.channel = p_usr_file->channel; + + + copy_from_user(msg.payload, buff, AXON_SMS_PAYLOAD_SIZE); + + ret += AXON_SMS_PAYLOAD_SIZE; + + + axon_sms_send(p_usr_file->p_axon_usr->sms, + p_usr_file->p_axon_usr->peer_mbox, &msg); + + return ret; +} + +static int +axon_mbx_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + int ret = 0; + struct axon_file_usr_mbx_t *p_usr_file = + (struct axon_file_usr_mbx_t *) (file->private_data); + + dbg_mbx_log("in %s()\n", __FUNCTION__); + + + if ((_IOC_TYPE(cmd) != MC_AXON_MBX_MAGIC) || + (_IOC_NR(cmd) > MC_AXON_MBX_MAXNR)) { + dbg_mbx_inf("Ioctl wrong command\n"); + ret = -ENOTTY; + } + + if (ret >= 0) { + switch (cmd) { + case MC_AXON_SUBSCRIBE_CHANNEL: + dbg_mbx_log("Ioctl MC_AXON_SUBSCRIBE_CHANNEL:\n"); + ret = + axon_mbx_subscribe_from_user(p_usr_file, arg); + break; + case MC_AXON_UNSUBSCRIBE_CHANNEL: + dbg_mbx_log + ("Ioctl MC_AXON_UNSUBSCRIBE_CHANNEL:\n"); + ret = axon_mbx_unsubscribe(p_usr_file); + break; + default: + dbg_mbx_log("Ioctl unknown ioctl command \n"); + ret = -ENOTTY; + }; + } + return ret; +} + +static int +axon_mbx_release(struct inode *inode, struct file *file) +{ + int ret = 0; + struct axon_file_usr_mbx_t *p_usr_file = + (struct axon_file_usr_mbx_t *) (file->private_data); + + dbg_mbx_log("in %s()\n", __FUNCTION__); + + if (p_usr_file != NULL) { + axon_mbx_unsubscribe(p_usr_file); + + if (p_usr_file->buffer) + kfree(p_usr_file->buffer); + + kfree(p_usr_file); + + file->private_data = NULL; + + } + + module_put(THIS_MODULE); + + return ret; +} + + +static int +axon_mbx_open(struct inode *inode, struct file *file) +{ + int ret = 0; + struct axon_file_usr_mbx_t *p_usr_file = NULL; + + dbg_mbx_log("in %s()\n", __FUNCTION__); + + file->private_data = NULL; + + try_module_get(THIS_MODULE); + + + p_usr_file = + kzalloc(sizeof(struct axon_file_usr_mbx_t), GFP_KERNEL); + + if (p_usr_file != NULL) { + + p_usr_file->p_axon_usr = + container_of(inode->i_cdev, struct axon_usr_mbx_t, + cdev); + + file->private_data = p_usr_file; + + + p_usr_file->buffer = kzalloc(PAGE_SIZE, GFP_KERNEL); + + if (p_usr_file->buffer == NULL) { + ret = -ENOMEM; + dbg_mbx_err + ("Unable to allocate deferred buffer\n"); + goto out; + } + + p_usr_file->channel = AXON_SMS_CHANNEL_INVALID; + + init_waitqueue_head(&p_usr_file->waitq); + + spin_lock_init(&p_usr_file->lock); + + } else { + ret = -ENOMEM; + } + + out: + if (ret) + axon_mbx_release(inode, file); + + return ret; +} + +static struct file_operations fops = { + .read = axon_mbx_read, + .write = axon_mbx_write, + .open = axon_mbx_open, + .release = axon_mbx_release, + .ioctl = axon_mbx_ioctl, +}; + + +static dev_t mbx_dev; + + +static void +axon_mbx_module_cleanup(void) +{ + int i_board; + + dbg_mbx_log("in %s()\n", __FUNCTION__); + + if (pp_axon_usr == NULL) + return; + + + for (i_board = 0; i_board < axon_usr_devs_count; i_board++) { + struct axon_usr_mbx_t *p_axon_usr = pp_axon_usr[i_board]; + + if (p_axon_usr == NULL) + continue; + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9) + class_device_destroy(axon_get_class(), + p_axon_usr->cdev_num); +#else + class_simple_device_remove(p_axon_usr->cdev_num); + +#endif + + + cdev_del(&p_axon_usr->cdev); + + kfree(p_axon_usr); + } + + + kfree(pp_axon_usr); + + unregister_chrdev_region(mbx_dev, axon_usr_devs_count); +} + + +static __init int +axon_mbx_module_init(void) +{ + int ret = 0; + int i_board; + + dbg_mbx_log("in %s()\n", __FUNCTION__); + + axon_usr_devs_count = axon_board_count(); + + dbg_mbx_inf("Found %d board(s) \n", axon_usr_devs_count); + + + if (axon_usr_devs_count <= 0) { + dbg_mbx_inf("No Axon device found\n"); + return -ENODEV; + } + + + pp_axon_usr = + kzalloc(sizeof(struct axon_usr_mbx_t *) * axon_usr_devs_count, + GFP_KERNEL); + + if (pp_axon_usr == NULL) { + dbg_mbx_err("failed to allocate memory\n"); + return -ENOMEM; + } + + + alloc_chrdev_region(&mbx_dev, 0, axon_usr_devs_count, "mbx"); + + + for (i_board = 0; i_board < axon_usr_devs_count && ret == 0; + i_board++) { + struct axon_usr_mbx_t *p_axon_usr; + + + p_axon_usr = + kzalloc(sizeof(struct axon_usr_mbx_t), GFP_KERNEL); + + if (p_axon_usr == NULL) { + dbg_mbx_err("failed to allocate memory\n"); + ret = -ENOMEM; + break; + } + + pp_axon_usr[i_board] = p_axon_usr; + + + p_axon_usr->cdev_num = MKDEV(MAJOR(mbx_dev), i_board); + + cdev_init(&p_axon_usr->cdev, &fops); + + ret = cdev_add(&p_axon_usr->cdev, p_axon_usr->cdev_num, 1); + + if (ret < 0) { + dbg_mbx_err + ("Unable to add user space driver for board 0x%p, on minor %d\n", + p_axon_usr->p_axon, i_board); + break; + } +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9) + class_device_create(axon_get_class(), NULL, + p_axon_usr->cdev_num, NULL, "mbx%d", + i_board); +#else + class_simple_device_add(axon_get_class(), + p_axon_usr->cdev_num, NULL, + "mbx%d", i_board); +#endif + + p_axon_usr->id = i_board; + p_axon_usr->p_axon = axon_board_list()[p_axon_usr->id]; + p_axon_usr->sms = axon_sms_get(p_axon_usr->p_axon); + p_axon_usr->peer_mbox = + axon_peer_mbox_get(p_axon_usr->p_axon); + } + + + if (ret != 0) { + axon_mbx_module_cleanup(); + } + + return ret; +} + +module_init(axon_mbx_module_init); +module_exit(axon_mbx_module_cleanup); --