* Copyright 2010-2017 Intel Corporation.
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,
* as published by the Free Software Foundation.
* 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.
* Disclaimer: The codes contained in these modules may be specific to
* the Intel Software Development Platform codenamed Knights Ferry,
* and the Intel product codenamed Knights Corner, and are not backward
* compatible with other Intel products. Additionally, Intel will NOT
* support the codes or instruction set in future products.
* Intel offers no warranty of any kind regarding the code. This code is
* licensed on an "AS IS" basis and Intel is not obligated to provide
* any support, assistance, installation, training, or other services
* of any kind. Intel is also not obligated to provide any updates,
* enhancements or extensions. Intel specifically disclaims any warranty
* of merchantability, non-infringement, fitness for any particular
* purpose, and any other warranty.
* Further, Intel disclaims all liability of any kind, including but
* not limited to liability for infringement of any proprietary rights,
* relating to the use of the code, even if Intel is notified of the
* possibility of such liability. Except as expressly stated in an Intel
* license agreement provided with this code and agreed upon with Intel,
* no license, express or implied, by estoppel or otherwise, to any
* intellectual property rights is granted herein.
#include <linux/module.h>
#include <linux/workqueue.h>
#include <linux/device.h>
#define PM_DB(fmt, ...) printk(KERN_ALERT"[ %s : %d ]:"fmt,__func__, __LINE__, ##__VA_ARGS__)
#define FUNCTION_ENTRY PM_DB("==> %s\n", __func__)
#define FUNCTION_EXIT PM_DB("<== %s\n", __func__)
#define PM_SCIF_RETRY_COUNT 5
DEFINE_RWLOCK(pmscif_send
);
static atomic_t epinuse
= ATOMIC_INIT(0);
typedef struct _mic_pm_scif
{
struct scif_portID rport_id
;
struct workqueue_struct
*pm_recvq
;
struct work_struct pm_recv
;
PM_CONNECTION_STATE con_state
;
pm_dump(char *buf
, size_t len
)
for ( i
=0; i
< len
; i
++) {
printk(KERN_ALERT
"%x ", buf
[i
]);
static void pm_handle_open (void *msg
, size_t len
)
pm_dump((char*)msg
, len
);
static void pm_handle_test (void *msg
, size_t len
)
pm_dump((char*)msg
, len
);
typedef void (*_pm_msg_handler
)(void*, size_t);
typedef struct _pm_msg_call
{
#define PM_HANDLE_ADD(opcode, function) [(opcode)] = {(function), #function}
pm_msg_call pm_msg_caller
[PM_MESSAGE_MAX
] = {
PM_HANDLE_ADD(PM_MESSAGE_OPEN
, pm_handle_open
),
PM_HANDLE_ADD(PM_MESSAGE_TEST
, pm_handle_test
)
pm_send_to_host(PM_MESSAGE opcode
, void *msg
, size_t len
)
size_t psize
= sizeof(pm_msg_header
) + len
;
if (pm_scif
->con_state
!= PM_CONNECTED
) {
if (!(payload
= kmalloc(psize
, GFP_ATOMIC
))) {
read_lock_irqsave(&pmscif_send
,flags
);
if (atomic_xchg(&epinuse
,1) != 0) {
read_unlock_irqrestore(&pmscif_send
,flags
);
((pm_msg_header
*)payload
)->opcode
= opcode
;
((pm_msg_header
*)payload
)->len
= len
;
memcpy((char*)payload
+ sizeof(pm_msg_header
), msg
, len
);
if ((err
= scif_send(pm_scif
->ep
, payload
, psize
, 0)) < 0) {
PM_DB("scif_recv failed\n");
//for (i = 0; i < psize; i++)
// printk(KERN_ALERT" buff: %X\n", payload[i]);
read_unlock_irqrestore(&pmscif_send
,flags
);
EXPORT_SYMBOL(pm_send_to_host
);
static struct mic_pmscif_handle micpmscif
= {
.pm_scif_uos2host
= pm_send_to_host
,
.pm_scif_host2uos
= NULL
,
static void pm_send_to_uos(pm_msg_header
*header
, char *msg
)
if(micpmscif
.pm_scif_host2uos
) {
micpmscif
.pm_scif_host2uos(header
, msg
);
pm_recv_from_host(struct work_struct
*work
)
mic_pm_scif
*pm_scif_info
= container_of(work
, mic_pm_scif
, pm_recv
);
if (pm_scif
->con_state
!= PM_CONNECTED
)
header
= kmalloc(sizeof(pm_msg_header
), GFP_KERNEL
);
if ((err
= scif_recv(pm_scif_info
->ep
, header
, sizeof(pm_msg_header
),
PM_DB("scif_recv failed\n");
msg
= kmalloc(header
->len
, GFP_KERNEL
);
if ((err
= scif_recv(pm_scif_info
->ep
, msg
, header
->len
,
PM_DB("scif_recv failed\n");
if(header
->opcode
< PM_MESSAGE_MAX
) {
if ((header
->opcode
!= PM_MESSAGE_CLOSE
) &&
(header
->opcode
!= PM_MESSAGE_CLOSE_ACK
)) {
if(pm_msg_caller
[header
->opcode
].handler
)
pm_msg_caller
[header
->opcode
].handler(msg
, header
->len
);
pm_send_to_uos(header
, msg
);
if (header
->opcode
== PM_MESSAGE_CLOSE
) {
pm_send_to_uos(header
,msg
);
pm_send_to_host(PM_MESSAGE_CLOSE_ACK
, NULL
, 0);
pm_scif
->con_state
= PM_DISCONNECTING
;
printk("pm_scif: Recvd scif message with bad opcode %d\n",
queue_work(pm_scif
->pm_recvq
, &pm_scif
->pm_recv
);
spm_ioctl(struct inode
*in
, struct file
*f
, unsigned int cmd
, unsigned long arg
)
uint32_t payload
= 0xc0de0000;
for (i
= 0; i
< PM_MESSAGE_TEST
; i
++) {
//PM_DB("sending %s with payload = %x \n",
// pm_msg_caller[i].name, payload);
pm_send_to_host(i
, &payload
, sizeof(payload
));
spm_unlocked_ioctl(struct file
*f
, unsigned int cmd
, unsigned long arg
)
return (long) spm_ioctl(f
->f_path
.dentry
->d_inode
, f
, cmd
, arg
);
spm_release(struct inode
*in
, struct file
*f
)
spm_devnode(struct device
*dev
, mode_t
*mode
)
return kasprintf(GFP_KERNEL
, "spm/%s", dev_name(dev
));
spm_open(struct inode
*in
, struct file
*f
)
struct file_operations spm_ops
= {
.unlocked_ioctl
= spm_unlocked_ioctl
,
device_destroy(spmclass
,spmdev
);
unregister_chrdev_region(spmdev
, 1);
spmdev
= MKDEV(spm_major
, spm_minor
);
err
= register_chrdev_region(spmdev
, 1, "spm");
err
= alloc_chrdev_region(&spmdev
, spm_minor
, 1, "spm");
spm_major
= MAJOR(spmdev
);
unregister_chrdev_region(spmdev
, 1);
spmdev
= MKDEV(spm_major
, spm_minor
);
cdev_init(&spmcdev
, &spm_ops
);
spmcdev
.owner
= THIS_MODULE
;
err
= cdev_add(&spmcdev
, spmdev
, 1);
spmclass
= class_create(THIS_MODULE
, "spm");
spmclass
->devnode
= spm_devnode
;
device_create(spmclass
, NULL
, spmdev
, NULL
, "spm");
PM_DB("pm_scif insmoded \n");
if ((err
= spm_dev_init())) {
PM_DB(" spm_dev_init failed\n");
pm_scif
= kzalloc(sizeof(mic_pm_scif
), GFP_KERNEL
);
pm_scif_register(&micpmscif
);
if ((pm_scif
->ep
= scif_open()) == NULL
) {
PM_DB(" scif_open failed\n");
if ((pm_scif
->lport
= scif_bind(pm_scif
->ep
, 0)) < 0) {
PM_DB(" scif_bind failed\n");
PM_DB(" scif_bind successfull. Local port number = %d, ep = \n",
dump_ep(pm_scif
->ep
, __func__
,__LINE__
);
pm_scif
->rport_id
.node
= 0;
pm_scif
->rport_id
.port
= SCIF_PM_PORT_0
;
while ((err
= scif_connect(pm_scif
->ep
, &pm_scif
->rport_id
)) != 0) {
PM_DB(" scif_connect failed with err = %d ep %p\n",err
,
if (retry
++ > PM_SCIF_RETRY_COUNT
)
pm_scif
->pm_recvq
= create_singlethread_workqueue("pm_recvq");
INIT_WORK(&pm_scif
->pm_recv
, pm_recv_from_host
);
queue_work(pm_scif
->pm_recvq
, &pm_scif
->pm_recv
);
pm_scif
->con_state
= PM_CONNECTED
;
EXPORT_SYMBOL(pm_scif_init
);
PM_DB("Good Bye!, pm scif \n");
pm_send_to_host(PM_MESSAGE_CLOSE
, NULL
, 0);
write_lock_irqsave(&pmscif_send
,flags
);
write_unlock_irqrestore(&pmscif_send
,flags
);
flush_workqueue(pm_scif
->pm_recvq
);
PM_DB("calling destroy\n");
destroy_workqueue(pm_scif
->pm_recvq
);
pm_scif_unregister(&micpmscif
);
pm_scif
->con_state
= PM_DISCONNECTED
;
EXPORT_SYMBOL(pm_scif_exit
);
module_init(pm_scif_init
);
module_exit(pm_scif_exit
);