[PATCH linux v5 17/18] drivers/fsi: Add SCOM FSI client device driver

Jeremy Kerr jk at ozlabs.org
Thu Oct 20 14:27:07 AEDT 2016


Hi Chris,

> diff > +static int scom_probe(struct device *);
> +
> +struct scom_op {
> +	uint64_t	data;
> +	uint32_t	addr;
> +};

You're making this a userspace interface, so this should go into the
uapi headers (and then will form part of the unchangeable Linux ABI).
However, more on the ABI below...

> +struct scom_device {
> +	struct fsi_device *fsi_dev;
> +	struct cdev *cdev;
> +};
> +
> +struct scom_device scom_dev;
> +
> +struct fsi_device_id scom_ids[] = {
> +	{
> +		.engine_type = FSI_ENGID_SCOM,
> +		.version = FSI_VERSION_ANY,
> +	},
> +	{ 0 }
> +};
> +
> +struct fsi_driver scom_drv = {
> +	.id_table = scom_ids,
> +	.drv = {
> +		.name = "scom",
> +		.bus = &fsi_bus_type,
> +		.probe = scom_probe,
> +	}
> +};

Make these static, and put after the code, just before the
module_fsi_driver. Then you won't need the forward definition of
scom_probe.

> > +static ssize_t scom_read(struct file *filep, char __user *buf, size_t len,
> +			loff_t *offset)
> +{
> +	int rc = 0;
> +	struct scom_op op;
> +	struct scom_device *scom = &scom_dev;
> +	struct device *dev = &scom->fsi_dev->dev;
> +
> +	/* todo: Retrieve device to operate on from filep->private_data */

Yep, otherwise you only get one scom interface, and crazy results if you
get probed twice. I'd suggest not leaving this until later.

> +
> +	if (len != sizeof(struct scom_op))
> +		return -EINVAL;
> +
> +	rc = copy_from_user(&op, buf, len);
> +	if (rc) {
> +		dev_dbg(dev, "copy from user failed:%d\n", rc);
> +		return -EINVAL;
> +	}
> +
> +	rc = get_scom(&op.data, op.addr);

I'm not a huge fan of that ABI to userspace. You already have the
address in the offset argument, so you can do this without needing new
uapi headers, using something like:

static ssize_t scom_read(struct file *filep, char __user *buf, size_t len,
			loff_t *offset)
{
	uint64_t val;

	/* todo: can we do 1/2/4/8 byte operations too? */
	if (len != sizeof(uint64_t))
		return -EINVAL;

	rc = get_scom(&val, offset);
	if (rc)
		return rc;

	rc = copy_to_user(&val, buf, len);

	return rc ? rc : len;
}


Also, have you thought about endianness?
	

> +static int scom_probe(struct device *dev)
> +{
> +	struct fsi_device *fsi_dev = to_fsi_dev(dev);
> +	struct scom_device *scom = &scom_dev;
> +	int rc;
> +
> +	scom->fsi_dev = fsi_dev;
> +	scom->cdev = cdev_alloc();
> +	if (!scom->cdev)
> +		return -ENOMEM;
> +
> +	scom->cdev->owner = THIS_MODULE;
> +
> +	rc = register_chrdev(0, "scom", &scom_fops);
> +	if (rc < 0) {
> +		dev_dbg(dev, "scom major allocation failed:%d\n", rc);
> +		return rc;
> +	}
> +	dev_dbg(dev, "scom major allocated:%d\n", rc);

You need to register the actual device too, right? We'll want scom0,
scom1, etc.

Cheers,


Jeremy


More information about the openbmc mailing list