I am writing a dummy PHY driver as a LKM. This is a platform driver
and I can't call the driver probe since it depends on the compatible string.
How can I make my driver invoke probe in my host machine, which is running
Ubuntu 18.04, kernel version - 5.3.0-40-generic. My machine is X86. I am installing this phy driver on my X86 machine.
Here is my code:
static int phy_platform_probe(struct platform_device *pdev) {
struct custom_port_phy_platform_local *lp;
int ret;
printk(KERN_ALERT "abc..phy_platform_probe..Invoked\r\n");
lp = devm_kzalloc(&pdev->dev,sizeof(struct custom_port_phy_platform_local),GFP_KERNEL);
if (!lp) {
dev_err(&(pdev->dev),"Failed to allocatate platform_local\n");
return -ENOMEM;
}
platform_set_drvdata(pdev, lp);
lp->dev = &pdev->dev;
ret = custom_port_phy_mdio_setup(lp, pdev->dev.of_node);
if (ret < 0) {
dev_err(&(pdev->dev),"Failed to setup MDIO bus\n");
return ret;
}
return 0;
}
static int phy_platform_remove(struct platform_device *pdev) {
struct custom_port_phy_platform_local *lp = platform_get_drvdata(pdev);
dev_info(&pdev->dev,"%s Enter\n",__func__);
custom_port_phy_mdio_teardown(lp);
return 0;
}
static struct phy_driver custom_phy_driver = {
.phy_id = 0x00000002,
.phy_id_mask = 0xffffffff,
.name = "custom_port_phy",
};
static struct platform_driver custom_phy_platform_driver = {
.probe = phy_platform_probe,
.remove = phy_platform_remove,
#if 1
.driver = {
.name = "custom_port_phy"
.of_match_table = port_phy_of_match,
}
#endif
};
#if 1
/* Match table for of_platform binding */
static struct of_device_id port_phy_of_match[] = {
{ .compatible = "custom,custom-port-phy", },
{},
};
#endif
static int __init phy_init(void)
{
int ret = 0;
printk(KERN_ALERT "abc >>> phy_driver_register started\r\n");
ret = phy_driver_register(&custom_phy_driver, THIS_MODULE);
printk(KERN_ALERT "abc >>> phy_driver_register END\r\n");
if(ret < 0) {
printk(KERN_ALERT "custom phy driver registration failed\r\n");
return ret;
}
ret = platform_driver_register(&custom_phy_platform_driver);
if(ret < 0) {
phy_driver_unregister(&custom_phy_driver);
printk("%s: Failed to register as Platform Driver\r\n", __func__);
return ret;
}
return ret;
}
static void __exit phy_exit(void)
{
pr_info("Goodbye phy_driver .\n");
phy_driver_unregister(&custom_phy_driver);
platform_driver_unregister(&custom_phy_platform_driver);
}
module_init(phy_init);
module_exit(phy_exit);
MODULE_LICENSE("GPL");
My code doesn't do probe since it can't find compatible flag.
How can I make it work on my host machine? I know I need dtb file. Even if
I have dtb file, how do I supply it and how to write it that way?
Related
I'm working on a device driver that needs to preform mem to mem copies via dma on a Beaglebone Black (ARM) with Linux Kernel 5.4.106. So far I've managed to successfully request a mem to mem compatible channel (and release it when I'm done), but I'm unable to perform the actual transfer.
#include <linux/module.h>
#include <linux/init.h>
#include <linux/dmaengine.h>
struct dma_chan *chan;
void *src;
void *dst;
static int __init mod_init(void)
{
dma_cap_mask_t mask;
int ret;
struct dma_async_tx_descriptor *tx = NULL;
dma_cookie_t cookie;
int *writer;
enum dma_status status;
printk("mod_init called\n");
dma_cap_zero(mask);
dma_cap_set(DMA_MEMCPY, mask);
chan = dma_request_channel(mask, NULL, NULL);
if(!chan){
printk("no mem2mem channels available");
ret = -EAGAIN;
goto fail_chan;
}
printk("requested channel");
src = kzalloc(16,GFP_KERNEL);
if(src == NULL){
ret = -ENOMEM;
goto fail_m1;
}
dst = kzalloc(16,GFP_KERNEL);
if(dst == NULL){
ret = -ENOMEM;
goto fail_m2;
}
writer = (int *)src;
*writer = 20;
tx = chan->device->device_prep_dma_memcpy(chan, virt_to_phys(dst), virt_to_phys(src), 16, DMA_CTRL_ACK | DMA_PREP_INTERRUPT);
if (!tx) {
printk("prep error");
}
printk("slave configured");
cookie = tx->tx_submit(tx);
if (dma_submit_error(cookie)) {
printk("submit error");
}
dma_async_issue_pending(chan);
status = dma_async_is_tx_complete(chan, cookie, NULL, NULL);
if(status != DMA_COMPLETE){
printk("something went wrong");
}
printk("dst: %d, src: %d", *(int *)dst, *(int *)src);
printk("done");
return 0;
fail_m2:
kfree(src);
fail_m1:
dma_release_channel(chan);
fail_chan:
return ret;
}
static void __exit mod_exit(void)
{
printk("mod_exit called\n");
dma_release_channel(chan);
printk("dst: %d, src: %d", *(int *)dst, *(int *)src);
kfree(src);
kfree(dst);
printk("released channel");
}
module_init( mod_init );
module_exit( mod_exit );
MODULE_AUTHOR("Me");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("DMA engine test.");
This kernel module compiles without any issues and when installed does not cause any errors. Eventho the cookie returns a DMA_COMPLETE status, the value in dst stays 0 (it should become 20).
Most of the code is based on the dmatest driver, which preforms flawlessly, but my copy is missing the mark.
What could be the issue here? Am I missing a step?
Try adding GFP_DMA flag to your kzalloc() calls - the memory you allocate should be suitable for DMA transfers, that is platform-dependent.
I've been building a RTDM UART driver by using the Linux version as an example. The UART base address is supposed to be 0x80070000, and when using the linux driver there is no problem as dmesg show:
80070000.serial: ttyAPP3 at MMIO 0x80070000 (irq = 234, base_baud = 1500000) is a 80070000.serial
mxs-auart 80070000.serial: Found APPUART 3.1.0
However, when I'm using the driver I'm builing I get a different adress each time I'm loading the module (such as 0xc8df6000) and none of them are correct.
Here's the probe function I'm using:
static int rt_mxs_auart_probe(struct platform_device *pdev)
{
const struct of_device_id *of_id =
of_match_device(mxs_auart_dt_ids, &pdev->dev);
int ret;
struct resource *r;
struct rtdm_device *dev;
struct rt_mxs_auart_port *s;
//allocate managed kernel memory
s = devm_kzalloc(&pdev->dev, sizeof(*s), GFP_KERNEL);
if (!s)
return -ENOMEM;
ret = rt_serial_mxs_probe_dt(s, pdev);
if (ret < 0){
return ret;
}
if (of_id) {
pdev->id_entry = of_id->data;
s->devtype = pdev->id_entry->driver_data;
}
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!r)
return -ENXIO;
//get irq line
s->irq = platform_get_irq(pdev, 0);
if (s->irq < 0)
return -ENODEV;
//base memory
s->membase = ioremap(r->start, resource_size(r));
if (IS_ERR(s->membase))
return PTR_ERR(s->membase);
/*define the rtdm_device*/
dev = &s->rtdm_dev;
dev->driver = &ida_driver;
dev->label = "xeno_ida";
dev->device_data = s;
ret = mxs_get_clks(s, pdev); //activate clocks
if (ret)
return ret;
s->uartclk = clk_get_rate(s->clk);
s->fifosize = MXS_AUART_FIFO_SIZE;
mxs_init_regs(s);
ret = rtdm_dev_register(dev); //register driver in RTDM space
if (ret)
return ret;
platform_set_drvdata(pdev, s); //add device-related data to device struct
pr_info("Probe: Successful\n");
pr_info("%s on IMX UART%d: membase=0x%p irq=%d uartclk=%d\n",
dev->name, pdev->id, s->membase, s->irq, s->uartclk);
return 0;
}
which is based on mxs-auart.c and rt_imx_uart.c.
For membase I'm using the same code as mxs-auart.c which is why I don't understand why I'm having this issue. My driver also freezes after some time when I try to rmmod it so I wonder if it is caused by this.
Would appreciate any help!
The return value of ioremap is a virtual address, not a physical one. It could be anywhere in the virtual address space.
The physical address is stored in r->start (the input of ioremap).
I'm studying Linux network device driver with Linux 4.12.14, and here is what I've learnt:
In device driver, it sets some variables when the device is opened:
dev->watchdog_timeo = TX_TIMEOUT;
...
.ndo_tx_timeout = el3_tx_timeout,
Linux kernel activates a timer when the device is opened:
static void dev_watchdog(unsigned long arg)
{
struct net_device *dev = (struct net_device *)arg;
netif_tx_lock(dev);
if (!qdisc_tx_is_noop(dev)) {
if (netif_device_present(dev) &&
netif_running(dev) &&
netif_carrier_ok(dev)) {
int some_queue_timedout = 0;
unsigned int i;
unsigned long trans_start;
for (i = 0; i < dev->num_tx_queues; i++) {
struct netdev_queue *txq;
txq = netdev_get_tx_queue(dev, i);
trans_start = txq->trans_start;
if (netif_xmit_stopped(txq) &&
time_after(jiffies, (trans_start +
dev->watchdog_timeo))) {
some_queue_timedout = 1;
txq->trans_timeout++;
break;
}
}
if (some_queue_timedout) {
WARN_ONCE(1, KERN_INFO "NETDEV WATCHDOG: %s (%s): transmit queue %u timed out\n",
dev->name, netdev_drivername(dev), i);
dev->netdev_ops->ndo_tx_timeout(dev);
}
if (!mod_timer(&dev->watchdog_timer,
round_jiffies(jiffies +
dev->watchdog_timeo)))
dev_hold(dev);
}
}
netif_tx_unlock(dev);
dev_put(dev);
}
So, after kernel transfers a skb, it updates txq->trans_start:
static inline netdev_tx_t netdev_start_xmit(struct sk_buff *skb, struct net_device *dev,
struct netdev_queue *txq, bool more)
{
const struct net_device_ops *ops = dev->netdev_ops;
int rc;
rc = __netdev_start_xmit(ops, skb, dev, more);
if (rc == NETDEV_TX_OK)
txq_trans_update(txq);
return rc;
}
And the function to update txq->trans_start:
static inline void txq_trans_update(struct netdev_queue *txq)
{
if (txq->xmit_lock_owner != -1)
txq->trans_start = jiffies;
}
However, I didn't find anywhere updates the "txq->trans_start" again, so my question is:
How come a successful transmit doesn't make the watchdog timer timeout?
I'm new to Linux USB driver development, and I absolutely love it so far! I'm currently creating a driver for an Xbox One controller, and I have a question for you guys. In the below code, you will see that I fill an interrupt IN URB in the open function, and I would like to print the contents of the URB buffer in the xb1_int_in_callback() function. What would be the best way to do this? At the moment, I'm using printk(KERN_INFO "int_in_buffer: %s", dev->int_in_buffer), however I don't see the entire URB buffer contents printed, and get a weird string printed to dmesg.
Sorry if this is a simple question, I'm new to programming and C, so I'm still learning as I go, but I absolutely love it so far!
Code:
static void xb1_int_in_callback(struct urb *int_in_urb) {
struct xb1_controller *dev = int_in_urb->context;
printk(KERN_INFO "xb1_int_in_callback successfully called");
printk(KERN_INFO "int_in_buffer: %s", dev->int_in_buffer);
}
static int xb1_open(struct inode *inode, struct file *file) {
printk(KERN_INFO "open function called..");
struct xb1_controller *dev;
struct usb_interface *interface;
int subminor;
int retval = 0;
subminor = iminor(inode);
interface = usb_find_interface(&xb1_driver, subminor);
if(!interface) {
printk(KERN_INFO "Unable to locate interface in open
function");
retval = -ENODEV;
goto exit;
}
dev = usb_get_intfdata(interface);
if(!dev) {
printk(KERN_INFO "Unable to locate dev structure in open
function");
retval = -ENODEV;
goto exit;
}
usb_fill_int_urb(dev->int_in_urb, dev->udev, usb_rcvintpipe(dev-
>udev, dev->int_in_endpoint->bEndpointAddress),
dev->int_in_buffer, dev->int_in_endpoint-
>wMaxPacketSize, xb1_int_in_callback,
dev, dev->int_in_endpoint->bInterval);
dev->int_in_running = 1;
retval = usb_submit_urb(dev->int_in_urb, GFP_KERNEL);
if(retval) {
printk(KERN_INFO "Unable to submit int_in_urb in open
function");
dev->int_in_running = 0;
goto exit;
}
file->private_data = dev;
exit:
return retval;
}
static int xb1_probe(struct usb_interface *interface, const struct
usb_device_id *id) {
struct usb_device *udev = interface_to_usbdev(interface);
struct xb1_controller *dev = NULL;
struct usb_host_interface *iface_desc;
struct usb_endpoint_descriptor *endpoint;
int i;
int retval = -ENODEV;
if(!udev) {
printk(KERN_INFO "udev is NULL in probe function");
xb1_abort(dev);
return retval;
}
dev = kzalloc(sizeof(struct xb1_controller), GFP_KERNEL);
if(!dev) {
printk(KERN_INFO "Unable to allocate memory for dev in probe
function");
xb1_abort(dev);
return retval;
}
dev->udev = udev;
dev->interface = interface;
iface_desc = interface->cur_altsetting;
for(i=0; i<iface_desc->desc.bNumEndpoints; i++) {
endpoint = &iface_desc->endpoint[i].desc;
if(((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ==
USB_DIR_IN)
&& ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
USB_ENDPOINT_XFER_INT)) {
dev->int_in_endpoint = endpoint;
}
}
if(!dev->int_in_endpoint) {
printk(KERN_INFO "Unable to locate interrupt in endpoint for
interface in probe function");
xb1_abort(dev);
return retval;
}
else {
printk(KERN_INFO "Interrupt in endpoint found!");
}
For printing a small buffers (up to 64 bytes long) use printk format:
Raw buffer as a hex string:
%*ph 00 01 02 ... 3f
%*phC 00:01:02: ... :3f
%*phD 00-01-02- ... -3f
%*phN 000102 ... 3f
For larger buffers use print_hex_dump(). Reference here.
I'm trying to develop a sound card driver with ALSA.
My goal eventually is to use:
PCM through I2S.
Control the volume through SPI.
For simplicity I started with volume control ONLY, no PCM.
I wrote the driver and compiled but I'm having 2 issues:
I cannot successfully call snd_card_free in the module exit. I tried storing the snd_card pointer which is successfully allocated in the module init in my custom "snd_adsp21479_chip" structure and use container_of to get the snd_card but the card is always null in the module_exit (it was not null in the module_init and is successfully allocated, I have the checks, I could also successfully call snd_card_free(card) in the module init function, but nowhere else). So every time I reload the module to the kernel, I end up with one more sound card and a new control in /dev/snd/ and proc/asound/devices
My biggest problem now is after loading the module I do see a new control (control1) in /dev/snd/ and a new card there, however using amixer I do not see the card's Volume control. I run amixer conctrols, and I do not see my new control.
Here's the code:
#include <linux/wait.h>
#include <sound/control.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <linux/spi/spi.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#define LOW_SPEED_SPIDEV_SPI_BUS 0
#define LOW_SPEED_SPIDEV_SPI_CS 0
#define LOW_SPEED_SPIDEV_MAX_CLK_HZ 100000
#define SOUND_CARD_NAME "adsp21479"
static struct spi_board_info cal_spi_board_info = {
.modalias = "spidev",
.bus_num = LOW_SPEED_SPIDEV_SPI_BUS,
.chip_select = LOW_SPEED_SPIDEV_SPI_CS,
.max_speed_hz = LOW_SPEED_SPIDEV_MAX_CLK_HZ,
};
static struct spi_device *spi = NULL;
struct snd_adsp21479_chip {
struct spi_device *spi;
struct snd_card *card;
};
/*******************************CONTROL METHODS *******************************/
static int snd_adsp21479_stereo_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info * uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 2;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 100;
return 0;
}
static int snd_adsp21479_stereo_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
return 0;
}
static int snd_adsp21479_stereo_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
return 0;
}
static struct snd_kcontrol_new volume_control = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Playback Volume",
.index = 0,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.info = snd_adsp21479_stereo_info,
.get = snd_adsp21479_stereo_get,
.put = snd_adsp21479_stereo_put
};
static int snd_adsp21479_init(struct spi_device *spi)
{
struct snd_card * card = NULL;
int return_value;
char id[10] = SOUND_CARD_NAME;
struct snd_adsp21479_chip *chip;
pr_info("snd_adsp21479_init start\n");
return_value = snd_card_new(&spi->dev,-1,id,THIS_MODULE,sizeof(struct snd_adsp21479_chip),&card);
if(return_value<0)
{
pr_err("snd_card_new failed\n");
return return_value;
}
if(!card)
{
pr_err("snd_card_new did not allocate card");
return -ENODEV;
}
pr_info("sound card=%p created successfully",card);
chip = card->private_data;
chip->card = card;
chip->spi = spi;
strcpy(card->mixername,"adsp21479 Mixer");
return_value = snd_ctl_add(card,snd_ctl_new1(&volume_control,chip));
if(return_value<0)
{
pr_err("snd_ctl_add faile adding control\n");
return return_value;
}
pr_info("created a mixer control");
static struct snd_device_ops ops = { NULL};
strcpy(card->driver,SOUND_CARD_NAME);
strcpy(card->shortname,SOUND_CARD_NAME);
strcpy(card->longname,"DAQRI ADSP21479 Sound Driver");
//create an ALSA device component
return_value = snd_device_new(card, SNDRV_DEV_LOWLEVEL,chip,&ops);
if(return_value<0)
{
printk(KERN_ALERT"creating an ALSA device component failed");
return return_value;
}
pr_info("created an ASLA device SNDRV_DEV_LOWLEVEL");
return_value = snd_card_register(card);
if(return_value<0)
{
pr_err("snd_card_register failed\n");
return return_value;
}
if(!card)
{
pr_err("card turned into null\n");
}
pr_info("registered sound card\n");
pr_info("adsp21479_snd_driver loaded successfully\n");
return 0;
// card here is always ok, and if I call snd_card_free(card) here it works.
}
static int __init adsp21479_spi_module_init(void)
{
struct spi_master *master=NULL;
int err;
pr_info("module init\n");
err = -ENODEV;
master = spi_busnum_to_master(LOW_SPEED_SPIDEV_SPI_BUS);
pr_info("master=%p\n", master);
if (!master)
{
pr_err("spi_busnum_to_master failed");
return err;
}
spi = spi_new_device(master, &cal_spi_board_info);
pr_info("spi device =%p\n", spi);
if (!spi)
{
pr_err("spi_new_device failed");
return err;
}
pr_info("spi device registered\n");
err = 0;
return snd_adsp21479_init(spi);
}
static void __exit adsp21479_spi_module_exit(void)
{
struct snd_adsp21479_chip *chip;
pr_info("module exit");
if (spi)
{
chip = container_of(&spi,struct snd_adsp21479_chip,spi);
if(chip)
{
pr_info("chip is not null\n");
if(chip->spi == spi)
{
pr_info("spi match\n");
}
pr_info("card = %p",chip->card);
if(chip->card != NULL)
{
pr_info("freeing sound card=%p\n",chip->card); // I never get here, it's always null, or if I store card in a global variable, I get an exception. And if I store it with drv_set_drvdata(&spi->dev,card) in the init function and try to obtain it here with a dev_get_drvdata(&spi->dev); and try to free the card with snd_card_free I get an exception as well.
snd_card_free(chip->card);
}
}
pr_info("unregistering spi device\n");
spi_unregister_device(spi);
}
}
module_init(adsp21479_spi_module_init);
module_exit(adsp21479_spi_module_exit);
/***************************SPI DRIVER METHODS ****************************/
MODULE_LICENSE("GPL"); ///WE NEED TO CHANGE IT LATER MAYBE
MODULE_DESCRIPTION(" Sound driver for the ADSP-21479");
So to reiterate: No PCM here ,I'm just trying to control the volume through SPI, and my control doesn't show up in amixer, and I can't free my card. I'm using the spidev as my SPI BUS master, and this driver is for the Sound card SPI slave.