Updated `README.md` with instructions for building/using the kernel module.
[xeon-phi-kernel-module] / pm_scif / pm_scif.c
CommitLineData
800f879a
AT
1/*
2 * Copyright 2010-2017 Intel Corporation.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License, version 2,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
12 *
13 * Disclaimer: The codes contained in these modules may be specific to
14 * the Intel Software Development Platform codenamed Knights Ferry,
15 * and the Intel product codenamed Knights Corner, and are not backward
16 * compatible with other Intel products. Additionally, Intel will NOT
17 * support the codes or instruction set in future products.
18 *
19 * Intel offers no warranty of any kind regarding the code. This code is
20 * licensed on an "AS IS" basis and Intel is not obligated to provide
21 * any support, assistance, installation, training, or other services
22 * of any kind. Intel is also not obligated to provide any updates,
23 * enhancements or extensions. Intel specifically disclaims any warranty
24 * of merchantability, non-infringement, fitness for any particular
25 * purpose, and any other warranty.
26 *
27 * Further, Intel disclaims all liability of any kind, including but
28 * not limited to liability for infringement of any proprietary rights,
29 * relating to the use of the code, even if Intel is notified of the
30 * possibility of such liability. Except as expressly stated in an Intel
31 * license agreement provided with this code and agreed upon with Intel,
32 * no license, express or implied, by estoppel or otherwise, to any
33 * intellectual property rights is granted herein.
34 */
35
36#include <linux/init.h>
37#include <linux/module.h>
38#include <linux/workqueue.h>
39#include <linux/slab.h>
40#include <linux/cdev.h>
41#include <linux/device.h>
42#include <scif.h>
43#include <mic/mic_pm.h>
44#include <mic/micscif.h>
45#include "pm_scif.h"
46
47#define PM_DB(fmt, ...) printk(KERN_ALERT"[ %s : %d ]:"fmt,__func__, __LINE__, ##__VA_ARGS__)
48#define FUNCTION_ENTRY PM_DB("==> %s\n", __func__)
49#define FUNCTION_EXIT PM_DB("<== %s\n", __func__)
50
51#define PM_SCIF_RETRY_COUNT 5
52
53DEFINE_RWLOCK(pmscif_send);
54
55static atomic_t epinuse = ATOMIC_INIT(0);
56void pm_scif_exit(void);
57
58typedef struct _mic_pm_scif {
59 scif_epd_t ep;
60 int lport;
61 struct scif_portID rport_id;
62 struct workqueue_struct *pm_recvq;
63 struct work_struct pm_recv;
64 PM_CONNECTION_STATE con_state;
65} mic_pm_scif;
66
67mic_pm_scif *pm_scif;
68
69void
70pm_dump(char *buf, size_t len)
71{
72 int i = 0;
73
74 for ( i=0; i < len; i++) {
75
76 if (i % 8)
77 printk(KERN_ALERT"\n");
78 printk(KERN_ALERT"%x ", buf[i]);
79 }
80}
81
82static void pm_handle_open (void *msg, size_t len)
83{
84 FUNCTION_ENTRY;
85 pm_dump((char*)msg, len);
86}
87
88static void pm_handle_test (void *msg, size_t len)
89{
90 FUNCTION_ENTRY;
91 pm_dump((char*)msg, len);
92
93}
94typedef void (*_pm_msg_handler)(void*, size_t);
95
96typedef struct _pm_msg_call {
97 _pm_msg_handler handler;
98 char *name;
99}pm_msg_call;
100
101#define PM_HANDLE_ADD(opcode, function) [(opcode)] = {(function), #function}
102
103pm_msg_call pm_msg_caller[PM_MESSAGE_MAX] = {
104 PM_HANDLE_ADD(PM_MESSAGE_OPEN, pm_handle_open),
105 PM_HANDLE_ADD(PM_MESSAGE_TEST, pm_handle_test)
106};
107
108int
109pm_send_to_host(PM_MESSAGE opcode, void *msg, size_t len)
110{
111// FUNCTION_ENTRY;
112 int err = 0;
113 size_t psize = sizeof(pm_msg_header) + len;
114 char *payload;
115 unsigned long flags;
116
117 if (pm_scif->con_state != PM_CONNECTED) {
118 err = -EINVAL;
119 goto error;
120 }
121
122 if (!(payload = kmalloc(psize, GFP_ATOMIC))) {
123 err = -ENOMEM;
124 goto error;
125 }
126 read_lock_irqsave(&pmscif_send,flags);
127
128 if (atomic_xchg(&epinuse,1) != 0) {
129 read_unlock_irqrestore(&pmscif_send,flags);
130 kfree(payload);
131 return -1;
132 }
133
134 ((pm_msg_header*)payload)->opcode = opcode;
135 ((pm_msg_header*)payload)->len = len;
136 if (len)
137 memcpy((char*)payload + sizeof(pm_msg_header), msg, len);
138
139 //0 for non blocking
140 if ((err = scif_send(pm_scif->ep, payload, psize, 0)) < 0) {
141 PM_DB("scif_recv failed\n");
142 }
143 atomic_set(&epinuse,0);
144 //for (i = 0; i < psize; i++)
145 // printk(KERN_ALERT" buff: %X\n", payload[i]);
146 read_unlock_irqrestore(&pmscif_send,flags);
147 kfree(payload);
148// FUNCTION_EXIT;
149error:
150 return err;
151}
152
153EXPORT_SYMBOL(pm_send_to_host);
154
155static struct mic_pmscif_handle micpmscif = {
156 .pm_scif_uos2host = pm_send_to_host,
157 .pm_scif_host2uos = NULL,
158 .owner = THIS_MODULE,
159};
160
161
162
163static void pm_send_to_uos(pm_msg_header *header, char *msg)
164{
165 if(micpmscif.pm_scif_host2uos) {
166 micpmscif.pm_scif_host2uos(header, msg);
167 }
168}
169
170static void
171pm_recv_from_host(struct work_struct *work)
172{
173 int err = 0;
174 char *msg = NULL;
175 pm_msg_header *header;
176 mic_pm_scif *pm_scif_info = container_of(work, mic_pm_scif, pm_recv);
177
178 FUNCTION_ENTRY;
179 if (pm_scif->con_state != PM_CONNECTED)
180 goto exit;
181
182 header = kmalloc(sizeof(pm_msg_header), GFP_KERNEL);
183
184 if ((err = scif_recv(pm_scif_info->ep, header, sizeof(pm_msg_header),
185 SCIF_RECV_BLOCK)) < 0) {
186 PM_DB("scif_recv failed\n");
187 goto end_con;
188 }
189
190 msg = kmalloc(header->len, GFP_KERNEL);
191
192 if ((err = scif_recv(pm_scif_info->ep, msg, header->len,
193 SCIF_RECV_BLOCK)) < 0) {
194 PM_DB("scif_recv failed\n");
195 goto end_con;
196 }
197 if(header->opcode < PM_MESSAGE_MAX) {
198 if ((header->opcode != PM_MESSAGE_CLOSE) &&
199 (header->opcode != PM_MESSAGE_CLOSE_ACK)) {
200 if(pm_msg_caller[header->opcode].handler)
201 pm_msg_caller[header->opcode].handler(msg, header->len);
202 pm_send_to_uos(header, msg);
203 } else {
204 if (header->opcode == PM_MESSAGE_CLOSE) {
205 pm_send_to_uos(header,msg);
206 pm_send_to_host(PM_MESSAGE_CLOSE_ACK, NULL, 0);
207 }
208 pm_scif->con_state = PM_DISCONNECTING;
209 goto end_con;
210 }
211 }
212 else
213 printk("pm_scif: Recvd scif message with bad opcode %d\n",
214 header->opcode);
215 kfree(header);
216 kfree(msg);
217 queue_work(pm_scif->pm_recvq, &pm_scif->pm_recv);
218 return;
219
220end_con:
221 kfree(header);
222 kfree(msg);
223exit:
224 FUNCTION_EXIT;
225}
226
227#ifdef PM_SCIF_IOCTL
228static int
229spm_ioctl(struct inode *in, struct file *f, unsigned int cmd, unsigned long arg)
230{
231 int i = 0;
232 uint32_t payload = 0xc0de0000;
233
234 FUNCTION_ENTRY;
235 for (i = 0; i < PM_MESSAGE_TEST; i++) {
236 payload++;
237 //PM_DB("sending %s with payload = %x \n",
238 // pm_msg_caller[i].name, payload);
239 pm_send_to_host(i, &payload, sizeof(payload));
240 }
241
242 return 0;
243}
244
245static long
246spm_unlocked_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
247{
248 return (long) spm_ioctl(f->f_path.dentry->d_inode, f, cmd, arg);
249}
250
251static int
252spm_release(struct inode *in, struct file *f)
253{
254 return 0;
255}
256
257static char *
258spm_devnode(struct device *dev, mode_t *mode)
259{
260 return kasprintf(GFP_KERNEL, "spm/%s", dev_name(dev));
261}
262
263
264static int
265spm_open(struct inode *in, struct file *f)
266{
267 return 0;
268}
269
270struct file_operations spm_ops = {
271 .owner = THIS_MODULE,
272 .unlocked_ioctl = spm_unlocked_ioctl,
273 .open = spm_open,
274 .release = spm_release,
275};
276
277int spm_major;
278int spm_minor;
279dev_t spmdev;
280struct cdev spmcdev;
281struct class *spmclass;
282
283static void
284spm_dev_deinit(void)
285{
286 device_destroy(spmclass,spmdev);
287 class_destroy(spmclass);
288 cdev_del(&spmcdev);
289 unregister_chrdev_region(spmdev, 1);
290}
291
292static int
293spm_dev_init(void)
294{
295 int err = 0;
296
297 if (spm_major) {
298 spmdev = MKDEV(spm_major, spm_minor);
299 err = register_chrdev_region(spmdev, 1, "spm");
300 }
301 else {
302 err = alloc_chrdev_region(&spmdev, spm_minor, 1, "spm");
303 spm_major = MAJOR(spmdev);
304 }
305
306 if (err < 0) {
307 unregister_chrdev_region(spmdev, 1);
308 goto done;
309 }
310
311 spmdev = MKDEV(spm_major, spm_minor);
312 cdev_init(&spmcdev, &spm_ops);
313 spmcdev.owner = THIS_MODULE;
314 err = cdev_add(&spmcdev, spmdev, 1);
315
316 if (err)
317 goto err;
318
319 spmclass = class_create(THIS_MODULE, "spm");
320 if (IS_ERR(spmclass)) {
321 err = PTR_ERR(spmclass);
322 goto err;
323 }
324
325 spmclass->devnode = spm_devnode;
326 device_create(spmclass, NULL, spmdev, NULL, "spm");
327 if (IS_ERR(spmclass)) {
328 err = PTR_ERR(spmclass);
329 goto err;
330 }
331done:
332 return err;
333err:
334 spm_dev_deinit();
335 return err;
336}
337#endif
338
339int pm_scif_init(void)
340{
341 int err = 1;
342 int retry = 0;
343
344 FUNCTION_ENTRY;
345 PM_DB("pm_scif insmoded \n");
346#ifdef PM_SCIF_IOCTL
347 if ((err = spm_dev_init())) {
348 PM_DB(" spm_dev_init failed\n");
349 goto done;
350 }
351#endif
352 atomic_set(&epinuse,0);
353 pm_scif = kzalloc(sizeof(mic_pm_scif), GFP_KERNEL);
354
355 if (!pm_scif) {
356 err = -ENOMEM;
357 goto end_con;
358 }
359
360 pm_scif_register(&micpmscif);
361
362 if ((pm_scif->ep = scif_open()) == NULL) {
363 PM_DB(" scif_open failed\n");
364 goto end_con;
365 }
366
367 if ((pm_scif->lport = scif_bind(pm_scif->ep, 0)) < 0) {
368 PM_DB(" scif_bind failed\n");
369 goto end_con;
370 }
371
372 PM_DB(" scif_bind successfull. Local port number = %d, ep = \n",
373 pm_scif->lport);
374 dump_ep(pm_scif->ep, __func__,__LINE__);
375 pm_scif->rport_id.node = 0;
376 pm_scif->rport_id.port = SCIF_PM_PORT_0;
377
378 while ((err = scif_connect(pm_scif->ep, &pm_scif->rport_id)) != 0) {
379 PM_DB(" scif_connect failed with err = %d ep %p\n",err,
380 pm_scif->ep);
381 msleep(1000);
382 if (retry++ > PM_SCIF_RETRY_COUNT)
383 goto end_con;
384 }
385
386 pm_scif->pm_recvq = create_singlethread_workqueue("pm_recvq");
387 INIT_WORK(&pm_scif->pm_recv, pm_recv_from_host);
388 queue_work(pm_scif->pm_recvq, &pm_scif->pm_recv);
389 pm_scif->con_state = PM_CONNECTED;
390 err = 0;
391#ifdef PM_SCIF_IOCTL
392done:
393#endif
394 return err;
395end_con:
396 pm_scif_exit();
397 FUNCTION_EXIT;
398 return err;
399}
400EXPORT_SYMBOL(pm_scif_init);
401
402void pm_scif_exit(void)
403{
404 unsigned long flags;
405
406 FUNCTION_ENTRY;
407 PM_DB("Good Bye!, pm scif \n");
408
409 pm_send_to_host(PM_MESSAGE_CLOSE, NULL, 0);
410 write_lock_irqsave(&pmscif_send,flags);
411 atomic_set(&epinuse,1);
412 write_unlock_irqrestore(&pmscif_send,flags);
413
414 if (pm_scif) {
415 if(pm_scif->pm_recvq) {
416 flush_workqueue(pm_scif->pm_recvq);
417 PM_DB("calling destroy\n");
418 destroy_workqueue(pm_scif->pm_recvq);
419 }
420
421 PM_DB("closing ep \n");
422 if (pm_scif->ep)
423 scif_close(pm_scif->ep);
424
425 pm_scif_unregister(&micpmscif);
426 pm_scif->con_state = PM_DISCONNECTED;
427 kfree(pm_scif);
428 }
429 #ifdef PM_SCIF_IOCTL
430 spm_dev_deinit();
431 #endif
432 FUNCTION_EXIT;
433}
434
435EXPORT_SYMBOL(pm_scif_exit);
436
437module_init(pm_scif_init);
438module_exit(pm_scif_exit);
439MODULE_LICENSE("GPL");