Cover image for Linux 1-Wire Bus

Linux 1-Wire Bus


Timeline

Timeline

2025-12-23

init

This article introduces the basic concepts of the Linux 1-Wire bus, communication steps, and the application of the DS18B20 temperature sensor. It first outlines the protocol features and hardware structure of the 1-Wire bus, which requires only one signal line for bidirectional data transmission, then analyzes the communication timing in master-slave mode, and finally details the performance specifications, pin configuration, and package type of the DS18B20 chip.

Linux Driver Notes

Table of ContentsLinks
1. Linux Driver Framework
2. Linux Driver Loading Logic
3. Character Device Basics
4. Concurrency and Race Conditions
5. Advanced Character Device Progression
6. Interrupts
7. Platform Bus
8. Device Tree
9. Device Model
10. Hot Plug
11. pinctrl Subsystem
12. GPIO Subsystem
13. Input Subsystem
14. One-Wire Bus
15. I2C
16. SPI
17. UART
18. PWM
19. RTC
20. Watchdog
21. CAN
22. Network Device
23. ADC
24. IIO
25. USB
26. LCD

Introduction to One-Wire Bus

Overview of One-Wire Bus

One-Wire is a serial communication protocol and hardware bus, used to transmit data and control signals between electronic devices. It was developed by the independent chip manufacturer Dallas Semiconductor and has been widely used in various applications.

Unlike serial data communication methods such as SPI and I2C, the One-Wire bus is characterized byrequiring only one signal line, which can transmit both clock and data, and the data is bidirectional. Therefore, the One-Wire bus has the characteristics of saving I/O pins, simple structure, and ease of expansion and maintenance.

1-Wire is used in various applications, includingtemperature sensors, humidity sensors, EEPROM memory, clocksand so on. It has been widely applied in many fields, such as industrial automation, home automation, the Internet of Things, and electronic device monitoring. This article uses the DS18B20 temperature sensor as an example to learn about 1-Wire.

Hardware Structure of 1-Wire
Hardware Structure of 1-Wire

  1. Signal Line: 1-Wire uses a single signal line for data transmission and communication. This line is called the data line and also serves as the power supply line. All devices on the 1-Wire bus are connected to this signal line.
  2. Pull-up Resistor: 1-Wire requires a pull-up resistor connected between the signal line and the power supply to ensure that the signal line remains at a high level (logic 1) when no device is transmitting data. The value of the pull-up resistor is typically between 4.7 kilohms and 10 kilohms. (GPIO set to pull-up)
  3. Devices: 1-Wire supports multiple devices connected to the signal line. Each device has a unique 64-bit address, which is used to identify and select the target device for communication. Devices can be various types of sensors, memory, clocks, etc.
  4. Processor: The processor is the controller on the 1-Wire bus. The processor is responsible for sending commands, reading responses, and controlling the slave devices on the 1-Wire bus.

Communication Steps of 1-Wire

1-Wire is amaster-slave structure,**The slave only responds when the master calls it.**Therefore,**the master must strictly follow the command timing of the 1-Wire bus.**If the command timing is incorrect, the device will not respond.

The communication steps of the 1-Wire bus typically include the following phases.

  1. Initialization: Before communication begins,**the master device sends an initialization signal to ensure no other device is communicating on the 1-Wire bus.**The initialization signal is a specific sequence, typically pulling the data line low for a period and then releasing it.
  2. ROM Operation Commands
  3. Function Commands

DS18B20 Introduction

Reference:

Chip Overview

The DS18B20 is a digital temperature sensor chip,Provides temperature measurement with 9 to 12-bit resolution, can beprogrammable non-volatile memory cellsto implement temperature lower and upper limit alarms.

It is a device based on the 1-Wire communication protocol, requiring only one signal wire and one ground wire.

The DS18B20 can measure temperature with high precision, accurate to 0.0625°C. It has a wide measurement range, typically between -55°C and +125°C.

The DS18B20 chip can obtain power from the master device via the 1-Wire bus, or it can be powered by an external power supply. This allows flexible power supply selection in some low-power applications.

Each DS18B20 has a globally unique 64-bit serial number, allowing multiple DS18B20s to be daisy-chained on the same 1-Wire bus for networking. Only one processor is needed to control multiple DS18B20s distributed over a large area. This networking method is particularly suitable for applications such as HVAC environmental control, buildings, equipment, grain temperature measurement, industrial temperature measurement, and process monitoring and control.

Basic Performance

The DS18B20 is a digital temperature sensor chip with the following basic performance characteristics:

  • Uses a 1-Wire interface, requiring only one port pin for communication
  • Each chip has a globally unique 64-bit serial number
  • Has multi-point distributed temperature measurement function without the need for external components
  • Can be powered via the data line, with a supply voltage range of 2.5V to 5.5V
  • Temperature measurement range is -55°C to +125°C
  • Accuracy of ±0.4°C in the range of -10°C to 70°C
  • Temperature resolution selectable from 9 to 12 bits
  • Temperature conversion time less than 750ms at maximum 12-bit resolution
  • User-defined nonvolatile temperature alarm settings
  • Alarm search command identifies and flags devices exceeding the programmed temperature
  • Strong electrostatic discharge protection: HBM 8000V, MM 800V
  • Available in surface-mount MSOP8, SOP8 packages and 3-pin TO-92, TO-92S packages

Pin Configuration and Package

DS18B20 chip pin configuration:

  • VDD: Power supply pin, provides positive power to the chip
  • DQ: Data pin, used for 1-Wire communication and data transfer
  • GND: Ground pin, connects to the chip’s ground (negative power supply)

The DS18B20 chip is available in different package types, with the most common being TO-92 and TO-92-3 packages. These are small three-pin packages suitable for through-hole and surface-mount installation.

The TO-92 package is a common small plastic package with pins arranged in order: VDD, DQ, and GND.

The TO-92-3 package is similar to the TO-92 package but with a slightly different pin order. The pin order for TO-92-3 is GND, DQ, and VDD.

In addition to these common package types, the DS18B20 can also be used in other package types, such as SOT-23 and TO-263 packages, which may have different pin arrangements and sizes. As shown in the figure below:

DS18B20 Package Types
DS18B20 Package Types

DC Electrical Characteristics
DC Electrical Characteristics

Internal Structure

The DS18B20 is a digital temperature sensor chip, and its internal structure mainly includes the following components, as shown in the figure below:

DS18b20 Block Diagram
DS18b20 Block Diagram

  • Temperature Sensor: The DS18B20 integrates a temperature sensor internally for measuring ambient temperature. The sensor typically detects temperature based on changes in a reference voltage and converts it into a digital signal.
  • A/D Converter: The DS18B20 chip contains an analog-to-digital converter (A/D converter) internally, which is used to convert the analog temperature value measured by the sensor into a corresponding digital representation. This allows temperature data to be processed and transmitted in digital form.
  • Memory: The DS18B20 chip also has internal memory for storing configuration information and temperature measurement results. The memory can store a unique 64-bit address, temperature resolution, and other related settings.
  • Control Logic: The DS18B20 chip includes control logic circuitry for managing temperature measurement, communication, and other related functions. The control logic coordinates the operation of various parts and communicates with the master device.
  • 1-Wire Interface: The DS18B20 uses the 1-Wire communication protocol, and its internal structure includes a data line and a pull-up resistor for communication with the master device. The 1-Wire interface simplifies wiring for connection and communication, allowing multiple DS18B20 sensors to be conveniently daisy-chained on the same bus.

Memory Map

64-Bit Lasered ROM Code

64-Bit Lasered ROM Code
64-Bit Lasered ROM Code

PartMeaning
28Family Code (DS18B20 fixed as 0x28)
FF 1C 46 93 16 03Unique Serial Number
5ACRC Check

Memory

The memory consists of 9 bytes, allocated as shown in the table below

DS18B20 Memory Map
DS18B20 Memory Map

DS18B20 has built-in CRC check. CRC uses polynomial X⁸ + X⁵ + X⁴ + 1. The host must recalculate the CRC itself. If the two CRCs match → data is correct; if not → indicates communication error. The chip does not automatically prevent errors.

Register Introduction

  • Temperature Register: The temperature register stores the result of the most recent temperature measurement. It is a 16-bit register containing the raw data of the temperature value. By reading the data from the temperature register and combining it with the resolution setting, the actual temperature value can be calculated.
  • Configuration Register: The configuration register is used to set the operating mode and temperature resolution of the DS18B20. It is an 8-bit register, with each bit corresponding to a configuration option. By writing to the configuration register, you can select the temperature resolution, trigger temperature conversion, and choose the power supply mode, etc.

Configuration Register

The configuration register is used to set the operating mode and temperature resolution of the DS18B20. It is an 8-bit register, with each bit corresponding to a configuration option, as shown in the figure below

Configuration Register
Configuration Register

Note: The default power-on settings are R0=1, R1=1 (12-bit resolution).

**There is a direct relationship between accuracy and conversion time.**Bits 7 and bits 0 to 4 of the configuration register are reserved by the device and must not be written to. The temperature resolution setting table is as follows:

Relationship between accuracy and conversion time
Relationship between accuracy and conversion time

Temperature Register

The temperature register of the DS18B20 chip is a 16-bit register used to store the raw data of the most recent temperature measurement. The bit layout of the temperature register is as follows:

Temperature Register Format
Temperature Register Format

The least significant bit (LSB) of the temperature register is the 24 2^{-4} bit, indicating a minimum temperature resolution of 0.0625°C. The other bits represent higher temperature resolutions in sequence, as follows:

  • 23 2^{-3} (0.125)
  • 22 2^{-2} (0.25)
  • 21 2^{-1} (0.5)
  • 20 2^{0} (1)
  • 21 2^{1} (2)
  • 22 2^{2} (4)
  • 23 2^{3} (8)

The raw data stored in the temperature register of the DS18B20 chip can be used to calculate the actual temperature value through the following steps:

  1. The 16-bit data read from the temperature register can be interpreted as a signed integer, where the most significant bit (MSB) represents the sign bit. If the sign bit is 0, it indicates a positive temperature; if the sign bit is 1, it indicates a negative temperature.
  2. Extract the lower 11 bits (bits 4 to 15) from the temperature register. These bits represent the absolute value of the temperature, where a bit value of 1 indicates that the corresponding temperature resolution is valid.
  3. Combine these 11 bits of data with the sign bit to form a signed integer.
  4. Based on the selected temperature resolution,multiply the signed integer by the corresponding resolution factor, to obtain the actual temperature value.
  5. When the temperature is greater than 0, the sign bit is 0, and the measured temperature value multiplied by the resolution factor gives the actual temperature.
  6. When the temperature is less than 0, the sign bit is 1, and the measured temperature value is inverted, incremented by one, and then multiplied by the resolution factor to obtain the actual temperature.

For example, suppose a 12-bit temperature resolution is selected, and the data read from the temperature register is 0x1FFF.

The binary representation of 0x1FFF is: 0001 1111 1111 1111. The most significant bit is 0, indicating a positive temperature. Extract the lower 11 bits: 111 1111 1111. Combine these 11 bits with the sign bit to get the signed integer: 0111 1111 1111 (corresponding to 0x07FF).

For a 12-bit temperature resolution, the resolution factor is 0.0625°C. Multiply the signed integer 0x07FF by the resolution factor: 0x07FF * 0.0625 = 127.9375°C.

Therefore, the data 0x1FFF read from the temperature register corresponds to an actual temperature value of approximately 127.94°C.

Instruction Introduction

According to the DS18B20 communication protocol, the host (microcontroller) must go through three steps to control the DS18B20 to complete a temperature conversion:

  • A reset operation must be performed on the DS18B20 before each read or write.
  • After a successful reset, send a ROM command.
  • Finally, send a RAM command to perform the intended operation on the DS18B20.

The reset requires the main CPU to pull the data line low for 500 microseconds, then release it. After the DS18B20 receives the signal, it waits about 16 to 60 microseconds, then sends a presence low pulse lasting 60 to 240 microseconds. When the main CPU receives this signal, it indicates a successful reset.

ROM Command

After the host detects the presence pulse, it can send aROM command. These commands act on the unique64-bit ROM codeof each slave device, allowing the host to select a specific device when multiple devices are present on the 1-Wire bus.

These commands also allow the host to:

  • Determine how many devices are on the bus
  • Confirm the device type
  • Detect if any device has triggered an alarm state

There are a total of5 ROM commands, each command is8 bits long

Before sending a function command to the DS18B20, the host must first send an appropriate ROM command.

InstructionProtocol CodeFunction
Search ROMF0HUsed to determine the number of DS1820 devices on the same bus and identify the 64-bit ROM address. Prepares for operating each device.
Read ROM33HRead the code (i.e., 64-bit address) in the ROM of the DS1820 temperature sensor
Match ROM55HAfter issuing this command, the 64-bit ROM code is then sent to access the DS1820 on the single bus corresponding to that code, making it respond, preparing for subsequent read/write operations on that DS1820.
Skip ROMCCHIgnore the 64-bit ROM address and directly send a temperature conversion command to the DS1820. Suitable for single-chip operation.
Alarm SearchECHAfter execution, only devices whose temperature exceeds the set upper or lower limit will respond.

Search ROM [F0h]

After system power-up, the host must identify the ROM codes of all slave devices on the bus to know:

  • How many devices there are
  • The type of each device

The host completes this process using a method of elimination, which requires repeated executionSearch ROM 命令 + 数据交换multiple times until all devices are found.

If there is only one device on the bus, a simpler method can be usedRead ROM [33h]instead of Search ROM.

After each Search ROM process, the host must return to step 1 (initialization) of the communication flow.

Read ROM [33h]

This commandcan only be used when there is only one slave device on the bus

It allows the host to directly read the 64-bit ROM code of that device without performing the Search ROM process.

If there are multiple devices on the bus and this command is used:

  • All devices will respond simultaneously
  • Data conflicts will occur

Match ROM [55h]

The host sendsMatch ROM + 64 位 ROM 编码to precisely select a specific device.

Only the device with a fully matching ROM code will respond to subsequent function commands; other devices will wait for the next reset.

Applicable to:

  • Multi-device bus
  • Single-device bus

Skip ROM [CCh]

This command will:

  • Simultaneously select all devices on the bus
  • No need to send ROM code

For exampleSkip ROM+Convert T [44h]can make all DS18B20s start temperature conversion simultaneously.

NoteSkip ROM+Read Scratchpad [BEh]Can only be used when there is only one device on the bus.

If there are multiple devices on the bus but this command is used:

  • All devices will respond simultaneously
  • Data conflicts will occur

Alarm Search [ECh]

This command operates the same way as Search ROM,

but only devices with the alarm flag set will respond. It is used to detect if any device exceeded the alarm threshold during the most recent temperature conversion.

After each Alarm Search process, the master must return to the initialization step.

ROM Commands Timing Diagram

ROM commands Timing Diagram
ROM commands Timing Diagram

Function Command

DS18B20 Function Command Set
DS18B20 Function Command Set

Convert T [44h]

This command initiates a temperature measurement.

After conversion is complete:

  • The temperature result is written to the 2-byte temperature register in the scratchpad.
  • The chip enters a low-power idle state.

If it isparasite power mode

  • the master must, within 10µs after issuing the command,
  • enable a strong pull-up.
  • Lasts the entire conversion time tCONV

If it isexternal power supply mode

the host can continuously read time slots:

  • Returns 0 → still converting
  • Returns 1 → conversion complete

This query cannot be performed in parasitic power mode because the bus is strongly pulled up.

Write Scratchpad [4Eh]

Allows the host to write 3 bytes to the scratchpad:

  • 1st byte → TH (byte 2)
  • 2nd byte → TL (byte 3)
  • 3rd byte → configuration register (byte 4)

Data must:

  • LSB first
  • All three bytes must be written at once before a reset, otherwise data may be corrupted.

Read Scratchpad [BEh]

The host reads the scratchpad contents:

  • Starting from the least significant bit of byte 0
  • Read all the way to byte 8 (CRC)

If only partial data is needed:

  • You can reset at any time to terminate the read

Copy Scratchpad [48h]

Copy the scratchpad’s:

1
2
TH / TL / 配置寄存器
(byte 2,3,4)

Copy to EEPROM.

In parasitic power mode:

  • Must enable strong pull-up within 10µs
  • Last for at least 10ms

Recall E2 [B8h]

Read from EEPROM:

1
TH / TL / 配置

Reload to scratchpad.

The host can query the status:

  • 0 → Restoring
  • 1 → Complete

This operation is automatically performed on power-up, so the scratchpad has valid data as soon as power is applied

Read Power Supply [B4h]

Detect power supply method:

Read a time slot after the host sends a command:

  • Bus pulled low → Parasitic power
  • Bus held high → External power

Function Commands timing diagram

DS18B20 Function Commands Flowchart
DS18B20 Function Commands Flowchart

Operation example

Only one DS18B20 on the 1-Wire bus

There is only one DS18B20 on the bus, and it uses parasitic power. The host writes TH, TL, and configuration registers to the DS18B20’s scratchpad, then reads the scratchpad and recalculates the CRC to verify the data. After that, the host copies the scratchpad contents to EEPROM.

Host modeData (LSB first)Description
TxResetHost sends reset pulse
RxPresenceDS18B20 returns presence pulse
TxCChHost sends Skip ROM (selects all devices on bus)
Tx4EhHost sends Write Scratchpad
Tx3 bytes of dataWrite TH, TL, and configuration registers
TxResetMaster resets again
RxPresenceDS18B20 returns presence pulse
TxCChSkip ROM
TxBEhRead Scratchpad
Rx9 bytes of dataRead scratchpad + CRC and verify
TxResetMaster reset
RxPresenceDS18B20 returns presence pulse
TxCChSkip ROM
Tx48hCopy Scratchpad
TxStrong pull-up ≥ 10msStrong pull-up power during EEPROM write

Multiple DS18B20s on the 1-Wire bus

There are multiple DS18B20s on the bus, and they use parasitic power. The master initiates a temperature conversion for a specified DS18B20, then reads its scratchpad and recalculates the CRC to verify data correctness.

Master modeData (LSB first)Description
TxResetHost sends reset pulse
RxPresenceDS18B20 returns presence pulse
Tx55hHost sends Match ROM command
Tx64-bit ROM codeHost specifies target DS18B20
Tx44hHost sends Convert T (start temperature measurement)
TxStrong pull-up to keep high levelHost provides strong pull-up power during the entire conversion time tCONV
TxResetHost sends reset pulse again
RxPresenceDS18B20 returns presence pulse
Tx55hHost sends Match ROM again
Tx64-bit ROM codeSelect the same device again
TxBEhHost sends Read Scratchpad
Rx9 bytes of dataThe host reads the scratchpad (including CRC) and calculates the CRC check itself.

Communication Timing

The DS18B20 uses a strict 1-Wire communication protocol to ensure data integrity. This protocol defines several signal types:

  • reset pulse
  • presence pulse
  • write 0
  • write 1
  • read 0
  • read 1

All data on the 1-Wire bus is transmitted LSB first (least significant bit first).

Reset Timing

1-WireAll communication on the bus begins with aninitialization sequence.

This initialization sequence includes:

  • areset pulse
  • sent by the host, followed by apresence pulse

The presence pulse tells the host that there are slave devices (e.g., DS18B20) on the bus and they are ready to communicate.

Initialization Procedure—Reset And Presence Pulses
Initialization Procedure—Reset And Presence Pulses

During initialization:

  1. The master sends a reset pulse by pulling the 1-Wire bus lowfor at least 480µs
  2. The master releases the bus and enters receive mode; after release, the bus is pulled high by a 5kΩ pull-up resistor
  3. The DS18B20 detects the rising edge and waits15µs ~ 60µs
  4. The DS18B20 sends a presence pulse by pulling the bus low60µs ~ 240µs
  5. The bus is pulled high by the pull-up resistor (RESISTOR PULLUP)

Read/Write Timing

  • The masterwrite time slotwrites data to the DS18B20 during the
  • During theread time slot, the master reads data from the DS18B20.

On the 1-Wire bus: each time slot transmits only 1 bit of data.

Read/Write Time Slot Timing Diagram
Read/Write Time Slot Timing Diagram

Write Time Slots

There are two types of write time slots:

  • Write 1
  • Write 0

Host uses: Write 1 time slot writes logic 1, Write 0 time slot writes logic 0

Write Slots
Write Slots

All write time slots must satisfy:

  • Each time slot ≥ 60µs and ≤ 120µs
  • Recovery time between adjacent time slots ≥ 1µs

Both write time slots are initiated by the host: first pulling the 1-Wire bus low

  • Write 1 rule

    • After the host pulls the bus low: must release within 15µs

    • After release: the 5kΩ pull-up resistor will pull the bus high

  • Write 0 rule

    • After the host pulls the bus low: maintain low level for ≥ 60µs

DS18B20 will detect the bus state within a sampling window: after the host starts the write time slot,15µs ~ 60µsbetween, if during this time:

  • Bus is high → write 1
  • Bus is low → write 0

Read Time Slots

DS18B20 Data can only be sent during read slots. So:

  • If the master wants to read data, it must actively create read slots
  • The slave cannot speak on its own

This is a type of:Master-driven synchronous communication

The master reads immediately after these commands:

  • Read Scratchpad [BEh]
  • Read Power Supply [B4h]

Additionally, read slots can be used to query status:

  • Convert T [44h] (whether temperature conversion is complete)
  • Recall E2 [B8h]

Read Slots
Read Slots

Read slots are initiated by the master:

  1. Master pulls bus low ≥1µs
  2. Master releases the bus
  3. DS18B20 sends 0 or 1 within this window
    • Send 1 → do nothing → line is pulled up → high level
    • Send 0 → actively pull the bus low (DS18B20 releases the bus at the end of the time slot, pull-up resistor restores high level)

EntireRead Slot must be ≥ 60µs longRecovery between time slots ≥ 1µs

Therefore, DS18B20 data is valid within 15µs after the start of the read time slot, so the host must complete sampling within 15µs

Master Read1 Timing
Master Read1 Timing

Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/gpio/consumer.h>
#include <linux/delay.h>
#include <linux/uaccess.h>

// ROM Command
#define ROM_CMD_SEARCH_ROM 0xF0
#define ROM_CMD_READ_ROM 0x33
#define ROM_CMD_MATCH_ROM 0x55
#define ROM_CMD_SKIP_ROM 0xCC
#define ROM_CMD_ALARM_SEARCH 0xEC

// Function Command
#define FUNC_CMD_CONVERT_T 0x44
#define FUNC_CMD_WRITE_SCRATCHPAD 0x4E
#define FUNC_CMD_READ_SCRATCHPAD 0xBE
#define FUNC_CMD_COPY_SCRATCHPAD 0x48
#define FUNC_CMD_RECALL_E2 0xB8
#define FUNC_CMD_READ_POWER_SUPPLY 0xB4

struct ds18b20 {
dev_t dev_num;
struct cdev cdev;
struct class *class;
struct device *dev;
struct gpio_desc *gpiod;
struct fasync_struct *fa;
u8 precision;
// wait_queue_head_t wq;
};

int ds18b20_reset(struct gpio_desc *gpiod)
{
int timeout;

// host send reset pulse
gpiod_direction_output(gpiod, 0);
udelay(500); // >= 480 us

// host release the bus
gpiod_direction_input(gpiod);
udelay(60); // 15 us ~ 60 us

// ds18b20 send presence pulse
timeout = 240;
while (gpiod_get_value(gpiod)) { // 60 us ~ 240 us
if (timeout == 0)
goto err;
udelay(1);
timeout--;
}

// resistor pullup
timeout = 240;
while (!gpiod_get_value(gpiod)) { // 60 us ~ 240 us
if (timeout == 0)
goto err;
udelay(1);
timeout--;
}

udelay(480);

return 0;
err:
return -EIO;
}

void ds18b20_write_bit(struct gpio_desc *gpiod, char bit)
{
gpiod_direction_output(gpiod, 0);

if (bit) {
udelay(6);
gpiod_direction_input(gpiod);
udelay(80); // 60 ~ 120
} else {
udelay(86);
gpiod_direction_input(gpiod);
}

udelay(1);
}

void ds18b20_write_byte(struct gpio_desc *gpiod, char byte)
{
int i;
for (i = 0; i < 8; i++)
ds18b20_write_bit(gpiod, (byte >> i) & 0x1);
}

u8 ds18b20_read_bit(struct gpio_desc *gpiod)
{
char bit;

gpiod_direction_output(gpiod, 0);
udelay(2); // 1 us ~ 15 us, as smaller as possible

gpiod_direction_input(gpiod);
udelay(12); // wait for 12 us then read value

bit = gpiod_get_value(gpiod);
udelay(50); // >= 45us

return bit;
}

u8 ds18b20_read_byte(struct gpio_desc *gpiod)
{
int i;
char value = 0;
for (i = 0; i < 8; i++)
value |= ds18b20_read_bit(gpiod) << i;

return value;
}

int ds18b20_open(struct inode *inode, struct file *file)
{
struct ds18b20 *drv_data = container_of(inode->i_cdev, struct ds18b20, cdev);
//struct gpio_desc *gpiod = drv_data->gpiod;

file->private_data = drv_data;
// Precision set to 12
drv_data->precision = 12;

pr_info("%s success, default precision is %d\n", __func__, drv_data->precision);

return 0;
}

int ds18b20_release(struct inode *inode, struct file *file)
{
pr_info("%s\n", __func__);
return 0;
}

ssize_t ds18b20_read(struct file *file, char __user *buf, size_t size, loff_t *offset)
{
char ls_byte, ms_byte;
struct ds18b20 *drv_data = file->private_data;
struct gpio_desc *gpiod = drv_data->gpiod;
int16_t raw_temp;
size_t count = min(sizeof(int16_t), size);
int ret;

// Reset
ret = ds18b20_reset(gpiod);
if (ret < 0) {
pr_err("%s: reset fail\n", __func__);
return ret;
}
// Only one DS18B20, send Skip ROM
ds18b20_write_byte(gpiod, ROM_CMD_SKIP_ROM);
// Function Command, send Convert T
ds18b20_write_byte(gpiod, FUNC_CMD_CONVERT_T);

switch (drv_data->precision) {
case 12:
msleep(750);
break;
case 11:
msleep(375);
break;
case 10:
msleep(188);
break;
case 9:
msleep(94);
break;
default:
pr_err("unsupported precision\n");
return -EFAULT;
}

// Reset
ret = ds18b20_reset(gpiod);
if (ret < 0) {
pr_err("%s: reset fail\n", __func__);
return ret;
}
// Only one DS18B20, send Skip ROM
ds18b20_write_byte(gpiod, ROM_CMD_SKIP_ROM);
// Function Command, send Read Scratchpad
ds18b20_write_byte(gpiod, FUNC_CMD_READ_SCRATCHPAD);
// read val
ls_byte = ds18b20_read_byte(gpiod);
ms_byte = ds18b20_read_byte(gpiod);

raw_temp = (int16_t)(((u16)ms_byte << 8) | ls_byte);

pr_info("raw_temp is %d\n", raw_temp);

if (copy_to_user(buf, &raw_temp, count) != 0)
return -EFAULT;

// TODO CRC

return count;
}

#define DS_CMD_START _IO('D', 0)
#define DS_CMD_SET_PREC _IOW('D', 1, int)
#define DS_CMD_GET_TEMP _IOR('D', 2, int)

long ds18b20_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
char ls_byte, ms_byte;
struct ds18b20 *drv_data = file->private_data;
struct gpio_desc *gpiod = drv_data->gpiod;
int16_t raw_temp;
int ret;

switch (cmd) {
case DS_CMD_START:
// Reset
ret = ds18b20_reset(gpiod);
if (ret < 0) {
pr_err("%s: ds18b20 reset failed\n", __func__);
return ret;
}
// Only one DS18B20, send Skip ROM
ds18b20_write_byte(gpiod, ROM_CMD_SKIP_ROM);
// Function Command, Send Convert T
ds18b20_write_byte(gpiod, FUNC_CMD_CONVERT_T);

switch (drv_data->precision) {
case 12:
msleep(750);
break;
case 11:
msleep(375);
break;
case 10:
msleep(188);
break;
case 9:
msleep(94);
break;
default:
pr_err("unsupported precision\n");
return -EFAULT;
}
break;
case DS_CMD_SET_PREC:
break;
case DS_CMD_GET_TEMP:
// Reset
ret = ds18b20_reset(gpiod);
if (ret < 0) {
pr_err("%s: reset fail\n", __func__);
return ret;
}
// Only one DS18B20, send Skip ROM
ds18b20_write_byte(gpiod, ROM_CMD_SKIP_ROM);
// Function Command, Send Read Scratchpad
ds18b20_write_byte(gpiod, FUNC_CMD_READ_SCRATCHPAD);
// read val
ls_byte = ds18b20_read_byte(gpiod);
ms_byte = ds18b20_read_byte(gpiod);

raw_temp = (int16_t)(((u16)ms_byte << 8) | ls_byte);

pr_info("raw_temp is %d\n", raw_temp);

// if (copy_to_user((void *)arg, &raw_temp, sizeof(int16_t)) != 0)
// return -EFAULT;
if(put_user(raw_temp, (int16_t __user *)arg))
return -EFAULT;
break;
default:
pr_err("unknown ioctl cmd\n");
return -EFAULT;
}

return 0;
}

int ds18b20_fasync(int fd, struct file *file, int on)
{
struct ds18b20 *drv_data = file->private_data;
return fasync_helper(fd, file, on, &drv_data->fa);
}

struct file_operations ds18b20_fops = {
.owner = THIS_MODULE,
.open = ds18b20_open,
.release = ds18b20_release,
.read = ds18b20_read,
.llseek = no_llseek,
.unlocked_ioctl = ds18b20_unlocked_ioctl,
};

static int ds18b20_probe(struct platform_device *pdev)
{
int ret;
//struct device *dev = &pdev->dev;
struct ds18b20 *ds18b20;

ds18b20 = kzalloc(sizeof(*ds18b20), GFP_KERNEL);
if (!ds18b20) {
ret = -ENOMEM;
goto err_kzalloc;
}

ret = alloc_chrdev_region(&ds18b20->dev_num, 0, 1, "ds18b20");
if (ret < 0)
goto err_chrdev_region;

cdev_init(&ds18b20->cdev, &ds18b20_fops);
ds18b20->cdev.owner = THIS_MODULE;
ret = cdev_add(&ds18b20->cdev, ds18b20->dev_num, 1);
if (ret < 0)
goto err_cdev;

ds18b20->class = class_create(THIS_MODULE, "one-wire");
if (IS_ERR(ds18b20->class)) {
ret = PTR_ERR(ds18b20->class);
goto err_class;
}

// /sys/class/one-wire/ds18b20
ds18b20->dev = device_create(ds18b20->class, NULL, ds18b20->dev_num, NULL, "ds18b20");
if (IS_ERR(ds18b20->dev)) {
ret = PTR_ERR(ds18b20->dev);
goto err_device_create;
}

ds18b20->gpiod = gpiod_get_optional(&pdev->dev, "ds18b20", GPIOD_OUT_HIGH);
if (IS_ERR_OR_NULL(ds18b20->gpiod)) {
if (ds18b20->gpiod == NULL)
ret = -ENODEV;
else if (IS_ERR(ds18b20->gpiod))
ret = PTR_ERR(ds18b20->gpiod);
goto err_get_gpio;
}

platform_set_drvdata(pdev, ds18b20);

return 0;
err_get_gpio:
device_destroy(ds18b20->class, ds18b20->dev_num);
err_device_create:
class_destroy(ds18b20->class);
err_class:
cdev_del(&ds18b20->cdev);
err_cdev:
unregister_chrdev_region(ds18b20->dev_num, 1);
err_chrdev_region:
kfree(ds18b20);
err_kzalloc:
return ret;
}

static int ds18b20_remove(struct platform_device *pdev)
{
struct ds18b20 *ds18b20 = platform_get_drvdata(pdev);

gpiod_put(ds18b20->gpiod);
device_destroy(ds18b20->class, ds18b20->dev_num);
cdev_del(&ds18b20->cdev);
class_destroy(ds18b20->class);
unregister_chrdev_region(ds18b20->dev_num, 1);
kfree(ds18b20);

return 0;
}

const struct of_device_id match_table[] = {
{ .compatible = "even629,ds18b20" },
};

struct platform_driver ds18b20_pdrv = {
.driver = {
.name = "ds18b20",
.owner = THIS_MODULE,
.of_match_table = match_table,

},
.probe = ds18b20_probe,
.remove = ds18b20_remove,
};

module_platform_driver(ds18b20_pdrv);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("even629<asqwgo@outlook.com>");
MODULE_DESCRIPTION("This is a test sample for ds18b20");