Skip to content

Commit a2435dc

Browse files
arnopofourmone
authored andcommitted
mailbox: Add mailbox character device driver for coprocessor communication
This commit introduces a new mailbox character device driver that facilitates communication with a coprocessor using a mailbox framework and a shared memory region. The driver creates a character device that can be accessed from user space to send and receive messages to and from the coprocessor. Change-Id: Ib6f2837dfdbb13fd936d6845fd7f103e21eaa0f3 Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com> Reviewed-on: https://gerrit.st.com/c/mpu/oe/st/linux-stm32/+/405531 Domain-Review: Arnaud POULIQUEN <arnaud.pouliquen@st.com> Reviewed-by: Arnaud POULIQUEN <arnaud.pouliquen@st.com> Tested-by: Arnaud POULIQUEN <arnaud.pouliquen@st.com> Tested-by: Gwenael TREUVEUR <gwenael.treuveur@st.com> ACI: CIBUILD <MDG-smet-aci-builds@list.st.com> Reviewed-by: Gwenael TREUVEUR <gwenael.treuveur@st.com>
1 parent 53da215 commit a2435dc

File tree

3 files changed

+311
-0
lines changed

3 files changed

+311
-0
lines changed

drivers/mailbox/Kconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,4 +295,13 @@ config QCOM_IPCC
295295
acts as an interrupt controller for receiving interrupts from clients.
296296
Say Y here if you want to build this driver.
297297

298+
299+
config MAILBOX_CDEV
300+
tristate "mailbox client char device"
301+
help
302+
Say Y here to export mailboxes as a character device files, usually
303+
found in /dev/mailbox<%x>. They make it possible for ser-space
304+
programs to send and receive message through this interface.
305+
306+
It's safe to say N if you don't want to use this interface.
298307
endif

drivers/mailbox/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,4 @@ obj-$(CONFIG_SPRD_MBOX) += sprd-mailbox.o
6262
obj-$(CONFIG_QCOM_IPCC) += qcom-ipcc.o
6363

6464
obj-$(CONFIG_APPLE_MAILBOX) += apple-mailbox.o
65+
obj-$(CONFIG_MAILBOX_CDEV) += mailbox-client_cdev.o
Lines changed: 301 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,301 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Copyright (C) STMicroelectronics 2024
4+
*/
5+
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
6+
7+
#include <linux/cdev.h>
8+
#include <linux/device.h>
9+
#include <linux/io.h>
10+
#include <linux/kernel.h>
11+
#include <linux/mailbox_client.h>
12+
#include <linux/module.h>
13+
#include <linux/of_reserved_mem.h>
14+
#include <linux/platform_device.h>
15+
#include <linux/slab.h>
16+
#include <linux/uaccess.h>
17+
18+
#define MBOX_DEV_MAX (MINORMASK + 1)
19+
20+
#define cdev_to_mbxdev(i_cdev) container_of(i_cdev, struct mbox_cdev_ddata, cdev)
21+
22+
static struct class *mbox_cl_class;
23+
static dev_t mbox_major;
24+
25+
enum mbox_cdev_request_state {
26+
NO_REQ,
27+
REQ_SENT,
28+
REQ_ANSWERED,
29+
};
30+
31+
/**
32+
* struct mbox_cdev_mbox - Mailbox client structure
33+
* @name: Name of the mailbox channel
34+
* @chan: Mailbox channel
35+
* @client: Mailbox client
36+
*/
37+
struct mbox_cdev_mbox {
38+
const unsigned char name[10];
39+
struct mbox_chan *chan;
40+
struct mbox_client client;
41+
};
42+
43+
/**
44+
* struct mbox_cdev_ddata - Mailbox character device data
45+
* @dev: Device structure
46+
* @cdev: Character device structure
47+
* @mb: Mailbox client structure
48+
* @resm: Pointer to the mapped memory region
49+
* @resm_size: Size of the mapped memory region
50+
* @req_state: request state
51+
*/
52+
struct mbox_cdev_ddata {
53+
struct device dev;
54+
struct cdev cdev;
55+
struct mbox_cdev_mbox mb;
56+
void __iomem *resm;
57+
size_t resm_size;
58+
unsigned int req_state;
59+
};
60+
61+
static void mbox_cdev_mb_callback(struct mbox_client *cl, void *data)
62+
{
63+
struct mbox_cdev_mbox *mb = container_of(cl, struct mbox_cdev_mbox, client);
64+
struct mbox_cdev_ddata *mbxdev = container_of(mb, struct mbox_cdev_ddata, mb);
65+
66+
dev_dbg(&mbxdev->dev, "Answer received\n");
67+
mbxdev->req_state = REQ_ANSWERED;
68+
}
69+
70+
static const struct mbox_cdev_mbox rx_tx_mbox = {
71+
.name = "rx-tx",
72+
.client = {
73+
.rx_callback = mbox_cdev_mb_callback,
74+
.tx_block = true,
75+
.tx_done = NULL,
76+
.tx_tout = 500, /* 500 ms time out */
77+
.knows_txdone = false,
78+
},
79+
};
80+
81+
static ssize_t mbox_cdev_read(struct file *filep, char *buffer, size_t len, loff_t *offset)
82+
{
83+
struct mbox_cdev_ddata *mbxdev = cdev_to_mbxdev(filep->f_inode->i_cdev);
84+
85+
if (len > sizeof(mbxdev->resm_size))
86+
return -EINVAL;
87+
88+
if (mbxdev->req_state == NO_REQ)
89+
return -EPERM;
90+
91+
if (mbxdev->req_state == REQ_SENT)
92+
return -EBUSY;
93+
94+
if (copy_to_user(buffer, mbxdev->resm, min(len, mbxdev->resm_size)))
95+
return -EFAULT;
96+
97+
mbxdev->req_state = NO_REQ;
98+
99+
return mbxdev->resm_size;
100+
}
101+
102+
static ssize_t mbox_cdev_write(struct file *filep, const char *buffer, size_t len, loff_t *offset)
103+
{
104+
struct mbox_cdev_ddata *mbxdev = cdev_to_mbxdev(filep->f_inode->i_cdev);
105+
int ret;
106+
107+
if (len > sizeof(mbxdev->resm_size))
108+
return -EINVAL;
109+
110+
if (mbxdev->req_state == REQ_SENT)
111+
dev_warn(&mbxdev->dev, "Previous request not answered\n");
112+
113+
if (copy_from_user(mbxdev->resm, buffer, len))
114+
return -EFAULT;
115+
116+
ret = mbox_send_message(mbxdev->mb.chan, mbxdev->resm);
117+
if (ret < 0) {
118+
dev_err(&mbxdev->dev, "Failed to send message via mailbox\n");
119+
return ret;
120+
}
121+
122+
dev_dbg(&mbxdev->dev, "Request sent\n");
123+
mbxdev->req_state = REQ_SENT;
124+
125+
return len;
126+
}
127+
128+
static const struct file_operations mbox_cdev_fops = {
129+
.read = mbox_cdev_read,
130+
.write = mbox_cdev_write,
131+
};
132+
133+
static int mbox_cdev_request_mbox(struct device *dev, struct mbox_cdev_ddata *mbxdev)
134+
{
135+
struct mbox_cdev_mbox *mb = &mbxdev->mb;
136+
struct mbox_client *cl;
137+
138+
memcpy(mb, &rx_tx_mbox, sizeof(*mb));
139+
140+
cl = &mb->client;
141+
cl->dev = dev;
142+
143+
mb->chan = mbox_request_channel_byname(cl, mb->name);
144+
if (IS_ERR(mb->chan)) {
145+
dev_err_probe(dev, PTR_ERR(mb->chan), "Failed to request mailbox %s\n", mb->name);
146+
return PTR_ERR(mb->chan);
147+
}
148+
149+
return 0;
150+
}
151+
152+
static int mbox_cdev_get_memory_region(struct device *dev, struct mbox_cdev_ddata *mbxdev)
153+
{
154+
struct device_node *np = dev->of_node;
155+
struct device_node *res_node;
156+
struct reserved_mem *rmem;
157+
158+
res_node = of_parse_phandle(np, "memory-region", 0);
159+
if (!res_node) {
160+
dev_err(dev, "Unable to acquire memory region\n");
161+
return -ENODEV;
162+
}
163+
164+
rmem = of_reserved_mem_lookup(res_node);
165+
if (!rmem) {
166+
of_node_put(res_node);
167+
dev_err(dev, "unable to acquire memory-region\n");
168+
return -EINVAL;
169+
}
170+
171+
mbxdev->resm = devm_ioremap_wc(dev, rmem->base, rmem->size);
172+
if (IS_ERR(mbxdev->resm)) {
173+
dev_err(dev, "unable to map memory region\n");
174+
return PTR_ERR(mbxdev->resm);
175+
}
176+
mbxdev->resm_size = rmem->size;
177+
178+
return 0;
179+
}
180+
181+
static int mbxdev_char_device_add(struct platform_device *pdev, struct mbox_cdev_ddata *mbxdev)
182+
{
183+
struct device *dev;
184+
int ret;
185+
186+
dev = &mbxdev->dev;
187+
device_initialize(dev);
188+
dev->parent = &pdev->dev;
189+
190+
cdev_init(&mbxdev->cdev, &mbox_cdev_fops);
191+
mbxdev->cdev.owner = THIS_MODULE;
192+
193+
dev->devt = MKDEV(MAJOR(mbox_major), 0);
194+
cdev_set_parent(&mbxdev->cdev, &dev->kobj);
195+
ret = cdev_add(&mbxdev->cdev, dev->devt, 1);
196+
if (ret < 0)
197+
dev_err(&mbxdev->dev, "Failed to add char dev\n");
198+
199+
dev = device_create(mbox_cl_class, &pdev->dev, dev->devt, NULL, "mailbox%d",
200+
MINOR(dev->devt));
201+
if (IS_ERR(dev)) {
202+
cdev_del(&mbxdev->cdev);
203+
dev_err(&mbxdev->dev, "Failed to create device node\n");
204+
return PTR_ERR(dev);
205+
}
206+
return ret;
207+
}
208+
209+
static void mbox_cdev_driver_remove(struct platform_device *pdev)
210+
{
211+
struct mbox_cdev_ddata *mbxdev = platform_get_drvdata(pdev);
212+
213+
mbox_free_channel(mbxdev->mb.chan);
214+
iounmap(mbxdev->resm);
215+
cdev_del(&mbxdev->cdev);
216+
}
217+
218+
static int mbox_cdev_driver_probe(struct platform_device *pdev)
219+
{
220+
struct device *dev = &pdev->dev;
221+
struct mbox_cdev_ddata *mbxdev;
222+
int ret;
223+
224+
mbxdev = devm_kzalloc(&pdev->dev, sizeof(*mbxdev), GFP_KERNEL);
225+
if (!mbxdev)
226+
return -ENOMEM;
227+
228+
ret = mbox_cdev_get_memory_region(dev, mbxdev);
229+
if (ret) {
230+
dev_err(dev, "Unable to acquire memory region\n");
231+
return ret;
232+
}
233+
234+
// Initialize mailbox client
235+
ret = mbox_cdev_request_mbox(dev, mbxdev);
236+
if (ret)
237+
goto unmap;
238+
239+
ret = mbxdev_char_device_add(pdev, mbxdev);
240+
if (ret)
241+
goto free_mbx;
242+
243+
platform_set_drvdata(pdev, mbxdev);
244+
245+
return 0;
246+
247+
free_mbx:
248+
mbox_free_channel(mbxdev->mb.chan);
249+
unmap:
250+
iounmap(mbxdev->resm);
251+
252+
return ret;
253+
}
254+
255+
static const struct of_device_id mbox_cdev_match[] = {
256+
{ .compatible = "mbox-cdev", },
257+
{},
258+
};
259+
MODULE_DEVICE_TABLE(of, mbox_cdev_match);
260+
261+
static struct platform_driver mbox_cdev_driver = {
262+
.probe = mbox_cdev_driver_probe,
263+
.remove_new = mbox_cdev_driver_remove,
264+
.driver = {
265+
.name = "mbox-cdev",
266+
.of_match_table = mbox_cdev_match,
267+
},
268+
};
269+
270+
static int __init mbox_cdev_init(void)
271+
{
272+
int ret;
273+
274+
ret = alloc_chrdev_region(&mbox_major, 0, MBOX_DEV_MAX, "mailbox");
275+
if (ret < 0) {
276+
pr_err("failed to allocate char dev region\n");
277+
return ret;
278+
}
279+
280+
mbox_cl_class = class_create("mailbox");
281+
if (IS_ERR(mbox_cl_class)) {
282+
unregister_chrdev_region(mbox_major, MBOX_DEV_MAX);
283+
pr_err("Failed to create class\n");
284+
return PTR_ERR(mbox_cl_class);
285+
}
286+
287+
return platform_driver_register(&mbox_cdev_driver);
288+
}
289+
290+
static void __exit mbox_cdev_exit(void)
291+
{
292+
platform_driver_unregister(&mbox_cdev_driver);
293+
class_destroy(mbox_cl_class);
294+
unregister_chrdev_region(mbox_major, MBOX_DEV_MAX);
295+
}
296+
297+
module_init(mbox_cdev_init);
298+
module_exit(mbox_cdev_exit);
299+
300+
MODULE_LICENSE("GPL");
301+
MODULE_DESCRIPTION("A simple char driver for communication using a mailbox and a shared memory");

0 commit comments

Comments
 (0)