We will first show the relevant code for the read method – this is how a user space process (or thread) can read in the secret information housed within our driver (in its context structure):
static ssize_t
read_miscdrv_rdwr(struct file *filp, char __user *ubuf, size_t count, loff_t *off)
{
int ret = count, secret_len = strlen(ctx->oursecret);
struct device *dev = ctx->dev;
char tasknm[TASK_COMM_LEN];
PRINT_CTX();
dev_info(dev, "%s wants to read (upto) %zd bytes\n", get_task_comm(tasknm, current), count);
ret = -EINVAL;
if (count < MAXBYTES) {
[...] << we don't display some validity checks here >>
/* In a 'real' driver, we would now actually read the content of the
* [...]
* Returns 0 on success, i.e., non-zero return implies an I/O fault).
* Here, we simply copy the content of our context structure's
* 'secret' member to userspace. */
ret = -EFAULT;
if (copy_to_user(ubuf, ctx->oursecret, secret_len)) {
dev_warn(dev, "copy_to_user() failed\n");
goto out_notok;
}
ret = secret_len;
// Update stats
ctx->tx += secret_len; // our 'transmit' is wrt this driver
dev_info(dev, " %d bytes read, returning... (stats: tx=%d, rx=%d)\n",
secret_len, ctx->tx, ctx->rx);
out_notok:
return ret;
}
The copy_to_user() routine does its job – it copies the ctx->oursecret source buffer to the destination pointer, the ubuf user space buffer, for secret_len bytes, thus transferring the secret to the user space app. Now, let's check out the driver's write method.