Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create wr-pci-leds example #11

Merged
merged 2 commits into from
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 125 additions & 0 deletions docs/specs/wr-pci-leds.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
=================
Wind River PCI LEDs example device
=================
-------------------------
Sharing how to create a barebone QEMU custom device
-------------------------

Standalone run
============
Run
::

./qemu-system-x86_64 -device wr-pci-leds
>> <QEMU> Hello this is my-device PCI device

Linux run
============
Integrate the example device driver into Kernel image, and during the boot
::

>> <DRIVER> Hello, this is your leds QEMU driver
>> <DRIVER> Found device vid: 0xABCD pid: 0x525

On command shell,
::

# echo 11 > /sys/class/leds/qemu:led0/brightness
>> <QEMU> Write to 0 with 11 4
>> <QEMU> Read from 0 for 4
>> <DRIVER> leds_qemu regs is 11

Linux, drivers/leds/leds-qemu.c
============
.. code:: C
:linenos:

#include <linux/module.h>
#include <linux/pci.h>
#include <linux/leds.h>

#define DRV_NAME "leds_qemu"
#define PCI_VENDOR_ID_QEMU 0xabcd
#define PCI_DEVICE_ID_QEMU 0x0525

static const struct pci_device_id pci_table[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_QEMU, PCI_DEVICE_ID_QEMU), },
{ 0, },
};
MODULE_DEVICE_TABLE(pci, pci_table);

struct leds_qemu_priv {
uint32_t* regs;
struct led_classdev led0_qemu_cdev;
};

static int leds_qemu_set(struct led_classdev *led_cdev, enum led_brightness value)
{
struct leds_qemu_priv* s= container_of(led_cdev, struct leds_qemu_priv, led0_qemu_cdev);
writel((u8)value, s->regs);
int v = readl(s->regs);
printk(KERN_INFO ">>\t<DRIVER> leds_qemu regs is %d\n",v);
return 0;
}

static int leds_qemu_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
int rc = 0;
printk(KERN_INFO ">>\t<DRIVER> Hello, this is your leds QEMU driver\n");

struct leds_qemu_priv* priv = devm_kzalloc(&pdev->dev,sizeof(struct leds_qemu_priv), GFP_KERNEL);
pci_set_drvdata(pdev, priv);

uint16_t vendor, device;
pci_read_config_word(pdev, PCI_VENDOR_ID, &vendor);
pci_read_config_word(pdev, PCI_DEVICE_ID, &device);
printk(KERN_INFO ">>\t<DRIVER> Found device vid: 0x%X pid: 0x%X\n", vendor, device);

rc = pci_enable_device(pdev);
if (rc) return rc;
rc = pci_request_regions(pdev,DRV_NAME);
if (rc) {
pci_disable_device(pdev);
return rc;
}

resource_size_t pciaddr = pci_resource_start(pdev,0);
uint32_t* regs = ioremap(pciaddr,32);
priv->regs = regs;
priv->led0_qemu_cdev.name="qemu:led0";
priv->led0_qemu_cdev.max_brightness=255;
priv->led0_qemu_cdev.brightness_set_blocking=leds_qemu_set;
rc = led_classdev_register(&pdev->dev, &priv->led0_qemu_cdev);

return 0;
}
static void leds_qemu_remove (struct pci_dev *pdev)
{
}

static struct pci_driver leds_qemu_driver = {
.name = DRV_NAME,
.id_table = pci_table,
.probe = leds_qemu_probe,
.remove = leds_qemu_remove,
};

module_pci_driver(leds_qemu_driver);
MODULE_LICENSE("GPL");


Linux, drivers/leds/KConfig
=================
.. code::

config LEDS_QEMU
tristate "LED support for my-device in QEMU"
depends on LEDS_CLASS
depends on PCI


Linux, drivers/leds/Makefile
=================
.. code::

obj-$(CONFIG_LEDS_QEMU) += leds-qemu.o
5 changes: 5 additions & 0 deletions hw/misc/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -213,4 +213,9 @@ config IOSB
config XLNX_VERSAL_TRNG
bool

config WR_PCI_LEDS
bool
default y if PCI_DEVICES
depends on PCI

source macio/Kconfig
3 changes: 3 additions & 0 deletions hw/misc/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -157,3 +157,6 @@ system_ss.add(when: 'CONFIG_SBSA_REF', if_true: files('sbsa_ec.c'))

# HPPA devices
system_ss.add(when: 'CONFIG_LASI', if_true: files('lasi.c'))

# WR PCI LEDs example device
system_ss.add(when: 'CONFIG_WR_PCI_LEDS', if_true: files('wr-pci-leds.c'))
80 changes: 80 additions & 0 deletions hw/misc/wr-pci-leds.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#include "qemu/osdep.h"
#include "hw/pci/pci_device.h"

#define TYPE_MY_DEVICE "wr-pci-leds"
OBJECT_DECLARE_SIMPLE_TYPE(MyDeviceState, MY_DEVICE)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably ok since this is just an example implementation, but since the file name and TYPE_##MODULE_OBJ_NAME are both of wr-pci-leds nomenclature, i think the device state structure should also be named accordingly.


typedef struct MyDeviceState
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment as above, and throughout the file, the device state structure should probably be named consistently with the device type

{
PCIDevice parent_obj;
MemoryRegion bar;

int32_t led0,led1;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should these members be uint64_t to match the assignments in my_device_write()?

} MyDeviceState;
static void my_device_write(void *opaque, hwaddr addr, uint64_t data, unsigned size)
{
printf(">>\t<QEMU> Write to %lu with %lu %u\n",addr, data, size);
MyDeviceState* s = opaque;
switch (addr) {
case 0: s->led0=data; break;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style nit - case body should have new line per statement and indented

case 4: s->led1=data; break;
default: return;
}
}
static uint64_t my_device_read(void *opaque, hwaddr addr, unsigned size)
{
printf(">>\t<QEMU> Read from %lu for %u\n",addr, size);
MyDeviceState* s = opaque;
switch (addr) {
case 0: return s->led0;
case 4: return s->led1;
default: return ~0;
}
}
static const MemoryRegionOps my_device_ops = {
.write = my_device_write,
.read = my_device_read,
};
static void pci_my_device_realize(PCIDevice *dev, Error **errp)
{
printf(">>\t<QEMU> Hello this is my-device PCI device\n");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to prevent these from being interleaved with the driver test output, perhaps these can be qemu log messages that we can redirect to file using "-D " option on the qemu command line, rather than requiring the tab indent

MyDeviceState *s = MY_DEVICE(dev);
memory_region_init_io(&s->bar, OBJECT(s), &my_device_ops, s, "my-device", 32);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/my-device/wr-pci-leds

pci_register_bar(dev, 0 , PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar);

}
static void my_device_class_init(ObjectClass *klass, void *data)
{
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);

k->vendor_id = 0xabcd;
k->device_id = 0x0525;
k->realize = pci_my_device_realize;
k->class_id = PCI_CLASS_OTHERS;
}
static const TypeInfo my_device_info = {
.name = TYPE_MY_DEVICE,
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof(MyDeviceState),
.class_init = my_device_class_init,
.interfaces = (InterfaceInfo[]) {
{ INTERFACE_CONVENTIONAL_PCI_DEVICE },
{ },
},
};
static void my_device_register_types(void)
{
type_register_static(&my_device_info);
}
type_init(my_device_register_types)

/* Notes:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No longer needed notes?

*
// Kconfig
config MY_DEVICE
bool
default y

// mesa.build
system_ss.add(when: 'CONFIG_MY_DEVICE', if_true: files('my-device.c'))
*/