-
Notifications
You must be signed in to change notification settings - Fork 11
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
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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 |
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) | ||
|
||
typedef struct MyDeviceState | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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')) | ||
*/ |
There was a problem hiding this comment.
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.