drivers/hwmon/arctic_fan_controller.c

Source file repositories/reference/linux-study-clean/drivers/hwmon/arctic_fan_controller.c

File Facts

System
Linux kernel
Corpus path
drivers/hwmon/arctic_fan_controller.c
Extension
.c
Size
12459 bytes
Lines
375
Domain
Driver Families
Bucket
drivers/hwmon
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

struct arctic_fan_data {
	struct hid_device *hdev;
	struct device *hwmon_dev;	/* stored for explicit unregister in remove() */
	spinlock_t in_report_lock;	/* protects fan_rpm, ack_status, write_pending, pwm_duty */
	struct completion in_report_received; /* ACK (ID 0x02) received in raw_event */
	int ack_status;			/* 0 = OK, negative errno on device error */
	bool write_pending;		/* true while an OUT report ACK is in flight */
	u32 fan_rpm[ARCTIC_NUM_FANS];
	u8 pwm_duty[ARCTIC_NUM_FANS];	/* 0-255 matching sysfs range; converted to 0-100 on send */
	/*
	 * OUT report buffer passed to hid_hw_output_report(). Embedded in the
	 * devm_kzalloc'd struct so it is heap-allocated and passes
	 * usb_hcd_map_urb_for_dma(). Exclusively accessed by write(), which
	 * the hwmon core serializes.
	 */
	__dma_from_device_group_begin();
	u8 buf[ARCTIC_REPORT_LEN];
	__dma_from_device_group_end();
};

/*
 * Parse RPM values from the periodic status report (10 x uint16 LE at rpm_off).
 * pwm_duty is not updated from the report: the device is manual-only, so the
 * host cache is the authoritative source for PWM.
 * Called from raw_event which may run in IRQ context; must not sleep.
 */
static void arctic_fan_parse_report(struct arctic_fan_data *priv, u8 *buf,
				    int len, int rpm_off)
{
	unsigned long flags;
	int i;

	if (len < rpm_off + 20)
		return;

	spin_lock_irqsave(&priv->in_report_lock, flags);
	for (i = 0; i < ARCTIC_NUM_FANS; i++)
		priv->fan_rpm[i] = get_unaligned_le16(&buf[rpm_off + i * 2]);
	spin_unlock_irqrestore(&priv->in_report_lock, flags);
}

/*
 * raw_event: IN reports.
 *
 * Status report: Report ID 0x01, 32 bytes:
 *   byte 0 = report ID, bytes 1-10 = PWM 0-100%, bytes 11-30 = 10 x RPM uint16 LE.
 *   Device pushes these at ~1 Hz; no GET_REPORT.
 *
 * ACK report: Report ID 0x02, 2 bytes:
 *   byte 0 = 0x02, byte 1 = status (0x00 = OK, 0x01 = ERROR).
 *   Sent once after accepting and applying an OUT report (ID 0x01).
 */
static int arctic_fan_raw_event(struct hid_device *hdev,
				struct hid_report *report, u8 *data, int size)
{
	struct arctic_fan_data *priv = hid_get_drvdata(hdev);
	unsigned long flags;

	hid_dbg(hdev, "arctic_fan: raw_event id=%u size=%d\n", report->id, size);

	if (report->id == ARCTIC_ACK_REPORT_ID && size == ARCTIC_ACK_REPORT_LEN) {
		spin_lock_irqsave(&priv->in_report_lock, flags);
		/*
		 * Only deliver if a write is in flight. This prevents a
		 * late-arriving ACK from a timed-out write from erroneously
		 * satisfying a subsequent write's completion wait.
		 */
		if (priv->write_pending) {
			priv->ack_status = data[1] == 0x00 ? 0 : -EIO;
			complete(&priv->in_report_received);
		}
		spin_unlock_irqrestore(&priv->in_report_lock, flags);
		return 0;
	}

	if (report->id != ARCTIC_OUTPUT_REPORT_ID || size != ARCTIC_REPORT_LEN) {
		hid_dbg(hdev, "arctic_fan: raw_event id=%u size=%d ignored\n",
			report->id, size);
		return 0;
	}

	arctic_fan_parse_report(priv, data, size, ARCTIC_RPM_OFFSET);
	return 0;
}

static umode_t arctic_fan_is_visible(const void *data,
				     enum hwmon_sensor_types type,
				     u32 attr, int channel)
{
	if (type == hwmon_fan && attr == hwmon_fan_input)

Annotation

Implementation Notes