int __iio_device_attr_init() {... case IIO_SEPARATE: if (chan->indexed) name = kasprintf(GFP_KERNEL, "%s_%s%d_%s", iio_direction[chan->output], iio_chan_type_name_spec[chan->type], chan->channel, full_postfix); //in_voltage0_input // .......... }
switch (chan->type) { case IIO_ANGL_VEL: /* 读取陀螺仪数据, channel2为X、Y、Z轴*/ ret = icm20608_sensor_show(dev, ICM20_GYRO_XOUT_H, chan->channel2, val); break; case IIO_ACCEL: /* 读取加速度计数据,channel2为X、Y、Z轴 */ ret = icm20608_sensor_show(dev, ICM20_ACCEL_XOUT_H, chan->channel2, val); break; case IIO_TEMP: /* 读取温度 */ ret = icm20608_sensor_show(dev, ICM20_TEMP_OUT_H, IIO_MOD_X, val); break; default: ret = -EINVAL; break; } return ret; }
/* * @description : 设置ICM20608的陀螺仪计量程(分辨率) * @param - dev : icm20608设备 * @param - val : 量程(分辨率值)。 * @return : 0,成功;其他值,错误 */ staticinticm20608_write_gyro_scale(struct icm20608_dev *dev, int val) { int result, i; u8 d;
for (i = 0; i < ARRAY_SIZE(gyro_scale_icm20608); ++i) { if (gyro_scale_icm20608[i] == val) { d = (i << 3); result = regmap_write(dev->regmap, ICM20_GYRO_CONFIG, d); if (result) return result; return0; } } return -EINVAL; }
/* * @description : 设置ICM20608的加速度计量程(分辨率) * @param - dev : icm20608设备 * @param - val : 量程(分辨率值)。 * @return : 0,成功;其他值,错误 */ staticinticm20608_write_accel_scale(struct icm20608_dev *dev, int val) { int result, i; u8 d;
for (i = 0; i < ARRAY_SIZE(accel_scale_icm20608); ++i) { if (accel_scale_icm20608[i] == val) { d = (i << 3); result = regmap_write(dev->regmap, ICM20_ACCEL_CONFIG, d); if (result) return result; return0; } } return -EINVAL; }
structregmap { union { structmutexmutex; struct { spinlock_t spinlock; unsignedlong spinlock_flags; }; }; regmap_lock lock; regmap_unlock unlock; void *lock_arg; /* This is passed to lock/unlock functions */ structdevice *dev;/* Device we do I/O on */ void *work_buf; /* Scratch buffer used to format I/O */ structregmap_formatformat;/* Buffer format */ conststructregmap_bus *bus; void *bus_context; constchar *name;
structregmap_config { constchar *name;//名字 int reg_bits;//寄存器地址位数,必填字段。 int reg_stride;//寄存器地址步长。 int pad_bits; //寄存器和值之间的填充位数 int val_bits; //寄存器值位数,必填字段
/* * Release IO and memory resources used by the port. * This includes iounmap if necessary. */ void (*release_port)(struct uart_port *); /* * Request IO and memory resources used by the port. * This includes iomapping the port if necessary. */ int (*request_port)(struct uart_port *); void (*config_port)(struct uart_port *, int); int (*verify_port)(struct uart_port *, struct serial_struct *); int (*ioctl)(struct uart_port *, unsignedint, unsignedlong); #ifdef CONFIG_CONSOLE_POLL int (*poll_init)(struct uart_port *); void (*poll_put_char)(struct uart_port *, unsignedchar); int (*poll_get_char)(struct uart_port *); #endif };
//printf("input: %s, len = %d\n", cmd_buf, strlen(cmd_buf)); //------------向uart发送数据------------------- for (i = 0; i < 9; i++) printf("%#X ", cmd_buf1[i]);
Device Drivers ->Real Time Clock []Set system time from RTC on startup and resume [](rtc0) RTC used to set the system time []Set the RTC time based on NTP synchronization [](rtc0) RTC used to synchronize NTP adjustment []RTC debug support []RTC non volatile storage support []/sys/class/rtc/rtcN (sysfs) []/proc/driver/rtc (procfs for rtcN) []/dev/rtcN (character devices)
structrtc_task *irq_task; spinlock_t irq_task_lock; int irq_freq; int max_user_freq;
structtimerqueue_headtimerqueue; structrtc_timeraie_timer; structrtc_timeruie_rtctimer; structhrtimerpie_timer;/* sub second exp, so needs hrtimer */ int pie_enabled; structwork_structirqwork; /* Some hardware can't support UIE mode */ int uie_unsupported; };
/* Read 64 bit timer register, which could be in inconsistent state */ static u64 rtc_read_lpsrt(struct snvs_rtc_data *data){ u32 msb, lsb; regmap_read(data->regmap, data->offset + SNVS_LPSRTCMR, &msb); regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &lsb); return (u64)msb << 32 | lsb; }
/* Read the secure real time counter, taking care to deal with the cases of the * counter updating while being read. */ static u32 rtc_read_lp_counter(struct snvs_rtc_data *data){ u64 read1, read2; unsignedint timeout = 100;
/* As expected, the registers might update between the read of the LSB * reg and the MSB reg. It's also possible that one register might be * in partially modified state as well. */ read1 = rtc_read_lpsrt(data); do { read2 = read1; read1 = rtc_read_lpsrt(data); } while (read1 != read2 && --timeout); if (!timeout) dev_err(&data->rtc->dev, "Timeout trying to get valid LPSRT Counter read\n"); /* Convert 47-bit counter to 32-bit raw second count */ return (u32) (read1 >> CNTR_TO_SECS_SH); }
/* * Frame buffer operations * * LOCKING NOTE: those functions must _ALL_ be called with the console * semaphore held, this is the only suitable locking mechanism we have * in 2.6. Some may be called at interrupt time at this point though. * * The exception to this is the debug related hooks. Putting the fb * into a debug state (e.g. flipping to the kernel console) and restoring * it must be done in a lock-free manner, so low level drivers should * keep track of the initial console (if applicable) and may need to * perform direct, unlocked hardware writes in these hooks. */ structfb_ops { /* open/release and usage marking */ structmodule *owner; int (*fb_open)(struct fb_info *info, int user); int (*fb_release)(struct fb_info *info, int user); /* For framebuffers with strange non linear layouts or that do not * work with normal memory mapped access */ ssize_t (*fb_read)(struct fb_info *info, char __user *buf, size_t count, loff_t *ppos); ssize_t (*fb_write)(struct fb_info *info, constchar __user *buf, size_t count, loff_t *ppos); /* checks var and eventually tweaks it to something supported, * DO NOT MODIFY PAR */ int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info); /* set the video mode according to info->var */ int (*fb_set_par)(struct fb_info *info); /* set color register */ int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green, unsigned blue, unsigned transp, struct fb_info *info); /* set color registers in batch */ int (*fb_setcmap)(struct fb_cmap *cmap, struct fb_info *info); /* blank display */ int (*fb_blank)(int blank, struct fb_info *info); /* pan display */ int (*fb_pan_display)(struct fb_var_screeninfo *var, struct fb_info *info); /* Draws a rectangle */ void (*fb_fillrect) (struct fb_info *info, conststruct fb_fillrect *rect); /* Copy data from area to another */ void (*fb_copyarea) (struct fb_info *info, conststruct fb_copyarea *region); /* Draws a image to the display */ void (*fb_imageblit) (struct fb_info *info, conststruct fb_image *image); /* Draws cursor */ int (*fb_cursor) (struct fb_info *info, struct fb_cursor *cursor); /* wait for blit idle, optional */ int (*fb_sync)(struct fb_info *info); /* perform fb specific ioctl (optional) */ int (*fb_ioctl)(struct fb_info *info, unsignedint cmd, unsignedlong arg); /* Handle 32bit compat ioctl (optional) */ int (*fb_compat_ioctl)(struct fb_info *info, unsigned cmd, unsignedlong arg); /* perform fb specific mmap */ int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma); /* get capability given var */ void (*fb_get_caps)(struct fb_info *info, struct fb_blit_caps *caps, struct fb_var_screeninfo *var); /* teardown any resources to do with this framebuffer */ void (*fb_destroy)(struct fb_info *info); /* called at KDB enter and leave time to prepare the console */ int (*fb_debug_enter)(struct fb_info *info); int (*fb_debug_leave)(struct fb_info *info); };
structfb_fix_screeninfo { char id[16]; /* identification string eg "TT Builtin" */ unsignedlong smem_start; /* Start of frame buffer mem */ /* (physical address) */ __u32 smem_len; /* Length of frame buffer mem */ __u32 type; /* see FB_TYPE_* */ __u32 type_aux; /* Interleave for interleaved Planes */ __u32 visual; /* see FB_VISUAL_* */ __u16 xpanstep; /* zero if no hardware panning */ __u16 ypanstep; /* zero if no hardware panning */ __u16 ywrapstep; /* zero if no hardware ywrap */ __u32 line_length; /* length of a line in bytes */ unsignedlong mmio_start; /* Start of Memory Mapped I/O */ /* (physical address) */ __u32 mmio_len; /* Length of Memory Mapped I/O */ __u32 accel; /* Indicate to driver which */ /* specific chip/card we have */ __u16 capabilities; /* see FB_CAP_* */ __u16 reserved[2]; /* Reserved for future compatibility */ };
intunregister_framebuffer(struct fb_info *fb_info) { structfb_eventevent; int i, ret = 0; //检查传入的fb_info是否已经注册过 i = fb_info->node; if (!registered_fb[i]) { ret = -EINVAL; goto done; }
if (!lock_fb_info(fb_info)) return -ENODEV; event.info = fb_info;
//通知发生FB_EVENT_FB_UNBIND事件,绑定了该帧缓冲设备的都解绑 ret = fb_notifier_call_chain(FB_EVENT_FB_UNBIND, &event); unlock_fb_info(fb_info);
if (ret) { ret = -EINVAL; goto done; }
//释放掉申请的fb_info->pixmap.addr if (fb_info->pixmap.addr && (fb_info->pixmap.flags & FB_PIXMAP_DEFAULT)) kfree(fb_info->pixmap.addr);
staticintfb_open(struct inode *inode, struct file *file) __acquires(&info->lock) __releases(&info->lock) { //获取次设备号 int fbidx = iminor(inode); structfb_info *info; int res = 0;
//判断次设备号是否在合法范围 if (fbidx >= FB_MAX) return -ENODEV;
//根据次设备号找到对应的struct fb_info结构体指针 info = registered_fb[fbidx]; if (!info) //如果数组下标fbidx的变量是NULL,手动加载帧缓冲设备 request_module("fb%d", fbidx);
//再次从registered_fb数组中获取对应的struct fb_info结构体指针 info = registered_fb[fbidx]; if (!info) return -ENODEV; mutex_lock(&info->lock); if (!try_module_get(info->fbops->owner)) { res = -ENODEV; goto out; }
-> Device Drivers -> Graphics support -> Bootup logo(LOGO [=y]) -> Standard black and white Linux logo -> Standard 16-color Linux logo -> Standard 224-color Linux logo
三个选项分别对应黑白、16 位、24 位色彩格式的 logo,我们把这三个都选 中,都编译进 Linux 内核里面。
intmain() { int fp=0; structfb_var_screeninfovinfo; structfb_fix_screeninfofinfo; void *fbp = NULL; char *test_fbp = NULL; int x = 0, y = 0; long location = 0; int i; int num = 2; int pix_size=0;
fp = open("/dev/fb0", O_RDWR);
if(fp < 0) { printf("Error : Can not open framebuffer device/n"); exit(1); }
printf("-- Default fb info --\n"); _fb_get_info(fp, &finfo, &vinfo);
ABS_MT 事件是用于多点触摸的,ABS_MT 事件定义在文件 include/uapi/linux/input.h 中, 上报给 linux 内核。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
#define ABS_MT_SLOT 0x2f /* MT slot being modified */ #define ABS_MT_TOUCH_MAJOR 0x30 /* Major axis of touching ellipse */ #define ABS_MT_TOUCH_MINOR 0x31 /* Minor axis (omit if circular) */ #define ABS_MT_WIDTH_MAJOR 0x32 /* Major axis of approaching ellipse */ #define ABS_MT_WIDTH_MINOR 0x33 /* Minor axis (omit if circular) */ #define ABS_MT_ORIENTATION 0x34 /* Ellipse orientation */ #define ABS_MT_POSITION_X 0x35 /* Center X touch position */ #define ABS_MT_POSITION_Y 0x36 /* Center Y touch position */ #define ABS_MT_TOOL_TYPE 0x37 /* Type of touching device */ #define ABS_MT_BLOB_ID 0x38 /* Group a set of packets as a blob */ #define ABS_MT_TRACKING_ID 0x39 /* Unique ID of initiated contact */ #define ABS_MT_PRESSURE 0x3a /* Pressure on contact area */ #define ABS_MT_DISTANCE 0x3b /* Contact hover distance */ #define ABS_MT_TOOL_X 0x3c /* Center X tool position */ #define ABS_MT_TOOL_Y 0x3d /* Center Y tool position */
ABS_MT_POSITION_X 和 ABS_MT_POSITION_Y 用来上 报触摸 点的 (X,Y) 坐标 信息。ABS_MT_SLOT 用来上 报触摸点 ID , 对 于 Type B 类型的设备,需要用到 ABS_MT_TRACKING_ID 事件来区分触摸点。
staticint __init spi_init(void) { int status; buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL); if (!buf) { status = -ENOMEM; goto err0; } status = bus_register(&spi_bus_type); if (status < 0) goto err1; status = class_register(&spi_master_class); if (status < 0) goto err2; if (IS_ENABLED(CONFIG_SPI_SLAVE)) { status = class_register(&spi_slave_class); if (status < 0) goto err3; }
if (IS_ENABLED(CONFIG_OF_DYNAMIC)) WARN_ON(of_reconfig_notifier_register(&spi_of_notifier)); if (IS_ENABLED(CONFIG_ACPI)) WARN_ON(acpi_reconfig_notifier_register(&spi_acpi_notifier));
if (!bitbang->txrx_bufs) { bitbang->use_dma = 0; bitbang->txrx_bufs = spi_bitbang_bufs; if (!master->setup) { if (!bitbang->setup_transfer) bitbang->setup_transfer = spi_bitbang_setup_transfer; master->setup = spi_bitbang_setup; master->cleanup = spi_bitbang_cleanup; } }
/* driver may get busy before register() returns, especially * if someone registered boardinfo for devices */ ret = spi_register_master(spi_master_get(master)); if (ret) spi_master_put(master);
SPI slave nodes must be children of the SPI master node and can contain the following properties. - reg - (required) chip select address of device. - compatible - (required) name of SPI device following generic names recommended practice - spi-max-frequency - (required) Maximum SPI clocking speed of device in Hz - spi-cpol - (optional) Empty property indicating device requires inverse clock polarity(CPOL) mode - spi-cpha - (optional) Empty property indicating device requires shifted clock phase(CPHA) mode - spi-cs-high - (optional) Empty property indicating device requires chip select active high - spi-3wire - (optional) Empty property indicating device requires 3-wire mode. - spi-lsb-first - (optional) Empty property indicating device requires LSB first mode. - spi-tx-bus-width - (optional) The bus width(number of data wires) that used for MOSI. Defaults to 1 if not present. - spi-rx-bus-width - (optional) The bus width(number of data wires) that used for MISO. Defaults to 1 if not present.
if (spi->dev.of_node && !of_match_device(spidev_dt_ids, &spi->dev)) { dev_err(&spi->dev, "buggy DT: spidev listed directly in DT\n"); WARN_ON(spi->dev.of_node && !of_match_device(spidev_dt_ids, &spi->dev)); }
spidev_probe_acpi(spi); /* Allocate driver data */ spidev = kzalloc(sizeof(*spidev), GFP_KERNEL); if (!spidev) return -ENOMEM; /* Initialize the driver data */ spidev->spi = spi; spin_lock_init(&spidev->spi_lock); mutex_init(&spidev->buf_lock); INIT_LIST_HEAD(&spidev->device_entry); /* If we can allocate a minor number, hook up this device. * Reusing minors is fine so long as udev or mdev is working. */ mutex_lock(&device_list_lock); minor = find_first_zero_bit(minors, N_SPI_MINORS); if (minor < N_SPI_MINORS) { structdevice *dev; spidev->devt = MKDEV(SPIDEV_MAJOR, minor); dev = device_create(spidev_class, &spi->dev, spidev->devt, spidev, "spidev%d.%d", spi->master->bus_num, spi->chip_select); status = PTR_ERR_OR_ZERO(dev); } else { dev_dbg(&spi->dev, "no minor number available!\n"); status = -ENODEV; } if (status == 0) { set_bit(minor, minors); list_add(&spidev->device_entry, &device_list); } mutex_unlock(&device_list_lock); spidev->speed_hz = spi->max_speed_hz; if (status == 0) spi_set_drvdata(spi, spidev); else kfree(spidev);
/* * i2c_adapter is the structure used to identify a physical i2c bus along * with the access algorithms necessary to access it. */ structi2c_adapter { structmodule *owner; unsignedintclass;/* classes to allow probing for */ conststructi2c_algorithm *algo;/* the algorithm to access the bus */ void *algo_data; /* data fields that are valid for all devices */ structrt_mutexbus_lock; int timeout; /* in jiffies */ int retries; structdevicedev;/* the adapter device */ int nr; //总线的编号 char name[48]; structcompletiondev_released; structmutexuserspace_clients_lock; structlist_headuserspace_clients; structi2c_bus_recovery_info *bus_recovery_info; conststructi2c_adapter_quirks *quirks; };
structi2c_algorithm { /* * If an adapter algorithm can't do I2C-level access, set master_xfer * to NULL. If an adapter algorithm can do SMBus access, set * smbus_xfer. If set to NULL, the SMBus protocol is simulated * using common I2C messages. * * master_xfer should return the number of messages successfully * processed, or a negative value on error */ int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); int (*master_xfer_atomic)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); int (*smbus_xfer)(struct i2c_adapter *adap, u16 addr, unsignedshort flags, char read_write, u8 command, int size, union i2c_smbus_data *data); int (*smbus_xfer_atomic)(struct i2c_adapter *adap, u16 addr, unsignedshort flags, char read_write, u8 command, int size, union i2c_smbus_data *data); /* To determine what the adapter supports */ u32 (*functionality)(struct i2c_adapter *adap); #if IS_ENABLED(CONFIG_I2C_SLAVE) int (*reg_slave)(struct i2c_client *client); int (*unreg_slave)(struct i2c_client *client); #endif };
structi2c_driver { unsignedintclass; /* Notifies the driver that a new bus has appeared. You should avoid * using this, it will be removed in a near future. */ int (*attach_adapter)(struct i2c_adapter *) __deprecated;
/* Standard driver model interfaces */ int (*probe)(struct i2c_client *, conststruct i2c_device_id *); int (*remove)(struct i2c_client *);
/* New driver model interface to aid the seamless removal of the * current probe()'s, more commonly unused than used second parameter. */ int (*probe_new)(struct i2c_client *);
/* driver model interfaces that don't relate to enumeration */ void (*shutdown)(struct i2c_client *);
/* Alert callback, for example for the SMBus alert protocol. * The format and meaning of the data value depends on the protocol. * For the SMBus alert protocol, there is a single bit of data passed * as the alert response's low bit ("event flag"). * For the SMBus Host Notify protocol, the data corresponds to the * 16-bit payload data reported by the slave device acting as master. */ void (*alert)(struct i2c_client *, enum i2c_alert_protocol protocol, unsignedint data); // 警告回调函数(例如SMBus警报协议)
/* a ioctl like command that can be used to perform specific functions * with the device. */ int (*command)(struct i2c_client *client, unsignedint cmd, void *arg);//类似于ioctl 的命令控制函数 structdevice_driverdriver; conststructi2c_device_id *id_table;// 这个i2c驱动支持的设备链表
//下面的宏定义就是消息特征的标志 #define I2C_M_TEN 0x0010 /* this is a ten bit chip address */ #define I2C_M_RD 0x0001 /* read data, from slave to master */ #define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_PROTOCOL_MANGLING */ #define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */ #define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */ #define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */ #define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */
inti2c_register_driver(struct module *owner, struct i2c_driver *driver);//include/linux/i2c.h /* use a define to avoid include chaining to get THIS_MODULE */ #define i2c_add_driver(driver) \ i2c_register_driver(THIS_MODULE, driver);
/** * builtin_driver() - Helper macro for drivers that don't do anything * special in init and have no exit. This eliminates some boilerplate. * Each driver may only use this macro once, and calling it replaces * device_initcall (or in some cases, the legacy __initcall). This is * meant to be a direct parallel of module_driver() above but without * the __exit stuff that is not used for builtin cases. * * @__driver: driver name * @__register: register function for this driver type * @...: Additional arguments to be passed to __register * * Use this macro to construct bus specific macros for registering * drivers, and do not use it on its own. */ #define builtin_driver(__driver, __register, ...) \ staticint __init __driver##_init(void) \ { \ return __register(&(__driver) , ##__VA_ARGS__); \ } \ device_initcall(__driver##_init);
/* We must initialize early, because some subsystems register i2c drivers * in subsys_initcall() code, but are linked (and initialized) before i2c. */ postcore_initcall(i2c_init); module_exit(i2c_exit);
staticinti2c_device_match(struct device *dev, struct device_driver *drv) { structi2c_client *client = i2c_verify_client(dev); structi2c_driver *driver; if (!client) return0; /* Attempt an OF style match */ if (of_driver_match_device(dev, drv)) return1; /* Then ACPI style match */ if (acpi_driver_match_device(dev, drv)) return1; driver = to_i2c_driver(drv); /* match on an id table if there is one */ if (driver->id_table) return i2c_match_id(driver->id_table, client) != NULL; return0; }
/* Set up clock divider */ i2c_imx->bitrate = I2C_MAX_STANDARD_MODE_FREQ; ret = of_property_read_u32(pdev->dev.of_node, "clock-frequency", &i2c_imx->bitrate); if (ret < 0 && pdata && pdata->bitrate) i2c_imx->bitrate = pdata->bitrate; i2c_imx->clk_change_nb.notifier_call = i2c_imx_clk_notifier_call; clk_notifier_register(i2c_imx->clk, &i2c_imx->clk_change_nb); i2c_imx_set_clk(i2c_imx, clk_get_rate(i2c_imx->clk));
/* Set up chip registers to defaults */ imx_i2c_write_reg(i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN, i2c_imx, IMX_I2C_I2CR); imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx, IMX_I2C_I2SR);
/* Init optional bus recovery function */ ret = i2c_imx_init_recovery_info(i2c_imx, pdev); /* Give it another chance if pinctrl used is not ready yet */ if (ret == -EPROBE_DEFER) goto clk_notifier_unregister;
/* Add I2C adapter */ ret = i2c_add_numbered_adapter(&i2c_imx->adapter); if (ret < 0) goto clk_notifier_unregister;
/* Start I2C transfer */ result = i2c_imx_start(i2c_imx, atomic); if (result) { /* * Bus recovery uses gpiod_get_value_cansleep() which is not * allowed within atomic context. */ if (!atomic && i2c_imx->adapter.bus_recovery_info) { i2c_recover_bus(&i2c_imx->adapter); result = i2c_imx_start(i2c_imx, atomic); } }
if (result) goto fail0; /* read/write data */ for (i = 0; i < num; i++) { if (i == num - 1) is_lastmsg = true;