应用端读写
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <error.h>
#include <stdint.h>
#include <sys/ioctl.h>
#define SET_ADDR_CMD _IOW('S', 0x01, int)
#define SET_REGNUM_CMD _IOW('S', 0x02, uint32_t)
int main(int argc, char **argv)
{
int fd = open("/dev/switch-dev", O_RDWR);
if (fd < 0)
{
perror("Failed to open device");
return -1;
}
// 设置addr和regnum
int ret = ioctl(fd, SET_ADDR_CMD, 0x04);
ioctl(fd, SET_REGNUM_CMD, 0x01);
printf("ret: %d\n", ret);
// 读取上面设置addr和regnum下寄存器的值
int val;
ret = read(fd, &val, sizeof(val));
printf("val: 0x%x, ret: %d\n", val, ret);
val = 0x12;
write(fd, &val, sizeof(val));
close(fd);
return 0;
}
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/mdio.h>
#include <linux/of.h>
#include <linux/of_mdio.h>
#define DRIVER_NAME "rtl8306"
#define DEVICE_NAME "switch-dev"
#define NUM_MINORS 1
#define MAGIC_DATA 'S'
#define SET_ADDR_CMD _IOW(MAGIC_DATA, 0x01, int)
#define SET_REGNUM_CMD _IOW(MAGIC_DATA, 0x02, u32)
struct rtl8306_switch_phy_data {
int addr;
u32 regnum;
u16 val;
};
struct rtl8306_switch_data {
struct cdev cdev;
struct device *device;
struct mii_bus *bus;
struct rtl8306_switch_phy_data phy;
};
static struct class *rtl8306_switch_class;
static dev_t rtl8306_switch_dev_number;
static struct rtl8306_switch_data rtl8306_switch;
static int rtl8306_switch_open(struct inode *inode, struct file *filp)
{
struct rtl8306_switch_data *data;
data = container_of(inode->i_cdev, struct rtl8306_switch_data, cdev);
filp->private_data = data;
return 0;
}
static ssize_t rtl8306_switch_read(struct file *filp, char __user *buf, size_t count, loff_t *offp)
{
struct rtl8306_switch_data *data = filp->private_data;
struct mii_bus *bus = data->bus;
struct rtl8306_switch_phy_data *phy = &data->phy;
u16 val;
// printk("+++ func: %s, line: %d +++, phy->addr: 0x%x, phy->regnum: 0x%x\n", __func__, __LINE__, phy->addr, phy->regnum);
val = mdiobus_read(bus, phy->addr, phy->regnum);
if (val < 0)
return val;
if (copy_to_user(buf, &val, sizeof(val)))
return -EFAULT;
return sizeof(val);
}
static ssize_t rtl8306_switch_write(struct file *filp, const char __user *buf, size_t count, loff_t *offp)
{
struct rtl8306_switch_data *data = filp->private_data;
struct mii_bus *bus = data->bus;
struct rtl8306_switch_phy_data *phy = &data->phy;
u16 val;
if (copy_from_user(&val, buf, sizeof(val)))
return -EFAULT;
// printk("+++ func: %s, line: %d +++, val: 0x%x\n", __func__, __LINE__, val);
return mdiobus_write(bus, phy->addr, phy->regnum, val);
}
static long rtl8306_switch_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct rtl8306_switch_data *data = filp->private_data;
struct rtl8306_switch_phy_data *phy = &data->phy;
switch (cmd) {
case SET_ADDR_CMD:
phy->addr = arg;
// printk("+++ func: %s, line: %d +++, phy->addr: 0x%x\n", __func__,__LINE__, phy->addr);
break;
case SET_REGNUM_CMD:
phy->regnum = arg;
// printk("+++ func: %s, line: %d +++, phy->regnum: 0x%x\n", __func__, __LINE__, phy->regnum);
break;
default:
return -EINVAL;
}
return 0;
}
static struct file_operations rtl8306_switch_fops = {
.owner = THIS_MODULE,
.open = rtl8306_switch_open,
.read = rtl8306_switch_read,
.write = rtl8306_switch_write,
.unlocked_ioctl = rtl8306_switch_ioctl,
};
static int rtl8306_switch_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *mdio_np;
int ret;
// printk("+++ func: %s, line: %d +++\n", __func__, __LINE__);
mdio_np = of_parse_phandle(dev->of_node, "mdio-bus", 0);
if (!mdio_np) {
dev_err(dev, "Failed to get MDIO bus node\n");
return -ENODEV;
}
rtl8306_switch.bus = of_mdio_find_bus(mdio_np);
of_node_put(mdio_np);
if (!rtl8306_switch.bus) {
dev_err(dev, "Failed to find MDIO bus\n");
return -ENODEV;
}
// Initialize character device
cdev_init(&rtl8306_switch.cdev, &rtl8306_switch_fops);
rtl8306_switch.cdev.owner = THIS_MODULE;
// Allocate device numbers
ret = alloc_chrdev_region(&rtl8306_switch_dev_number, 0, NUM_MINORS, DRIVER_NAME);
if (ret < 0) {
dev_err(dev, "Failed to allocate device numbers\n");
return ret;
}
// Create device class
rtl8306_switch.device = device_create(rtl8306_switch_class, dev, rtl8306_switch_dev_number, NULL, DEVICE_NAME);
if (IS_ERR(rtl8306_switch.device)) {
class_destroy(rtl8306_switch_class);
unregister_chrdev_region(rtl8306_switch_dev_number, NUM_MINORS);
dev_err(dev, "Failed to create device\n");
return PTR_ERR(rtl8306_switch.device);
}
// Add character device to the system
ret = cdev_add(&rtl8306_switch.cdev, rtl8306_switch_dev_number, NUM_MINORS);
if (ret < 0) {
device_destroy(rtl8306_switch_class, rtl8306_switch_dev_number);
class_destroy(rtl8306_switch_class);
unregister_chrdev_region(rtl8306_switch_dev_number, NUM_MINORS);
dev_err(dev, "Failed to add character device\n");
return ret;
}
dev_info(dev, "My Switch driver initialized\n");
return 0;
}
static int rtl8306_switch_remove(struct platform_device *pdev)
{
cdev_del(&rtl8306_switch.cdev);
device_destroy(rtl8306_switch_class, rtl8306_switch_dev_number);
class_destroy(rtl8306_switch_class);
unregister_chrdev_region(rtl8306_switch_dev_number, NUM_MINORS);
dev_info(&pdev->dev, "My Switch driver removed\n");
return 0;
}
static const struct of_device_id rtl8306_switch_of_match[] = {
{ .compatible = DRIVER_NAME, },
{ },
};
MODULE_DEVICE_TABLE(of, rtl8306_switch_of_match);
static struct platform_driver rtl8306_switch_driver = {
.probe = rtl8306_switch_probe,
.remove = rtl8306_switch_remove,
.driver = {
.name = DRIVER_NAME,
.of_match_table = rtl8306_switch_of_match,
},
};
module_platform_driver(rtl8306_switch_driver);
MODULE_AUTHOR("linshuoquan@126.com");
MODULE_DESCRIPTION("RTL8306 Switch driver");
MODULE_LICENSE("GPL");