drivers/input/misc/powermate.c

Source file repositories/reference/linux-study-clean/drivers/input/misc/powermate.c

File Facts

System
Linux kernel
Corpus path
drivers/input/misc/powermate.c
Extension
.c
Size
14825 bytes
Lines
451
Domain
Driver Families
Bucket
drivers/input
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 powermate_device {
	signed char *data;
	dma_addr_t data_dma;
	struct urb *irq, *config;
	struct usb_ctrlrequest *configcr;
	struct usb_device *udev;
	struct usb_interface *intf;
	struct input_dev *input;
	spinlock_t lock;
	int static_brightness;
	int pulse_speed;
	int pulse_table;
	int pulse_asleep;
	int pulse_awake;
	int requires_update; // physical settings which are out of sync
	char phys[64];
};

static char pm_name_powermate[] = "Griffin PowerMate";
static char pm_name_soundknob[] = "Griffin SoundKnob";

static void powermate_config_complete(struct urb *urb);

/* Callback for data arriving from the PowerMate over the USB interrupt pipe */
static void powermate_irq(struct urb *urb)
{
	struct powermate_device *pm = urb->context;
	struct device *dev = &pm->intf->dev;
	int retval;

	switch (urb->status) {
	case 0:
		/* success */
		break;
	case -ECONNRESET:
	case -ENOENT:
	case -ESHUTDOWN:
		/* this urb is terminated, clean up */
		dev_dbg(dev, "%s - urb shutting down with status: %d\n",
			__func__, urb->status);
		return;
	default:
		dev_dbg(dev, "%s - nonzero urb status received: %d\n",
			__func__, urb->status);
		goto exit;
	}

	/* handle updates to device state */
	input_report_key(pm->input, BTN_0, pm->data[0] & 0x01);
	input_report_rel(pm->input, REL_DIAL, pm->data[1]);
	input_sync(pm->input);

exit:
	retval = usb_submit_urb (urb, GFP_ATOMIC);
	if (retval)
		dev_err(dev, "%s - usb_submit_urb failed with result: %d\n",
			__func__, retval);
}

/* Decide if we need to issue a control message and do so. Must be called with pm->lock taken */
static void powermate_sync_state(struct powermate_device *pm)
{
	if (pm->requires_update == 0)
		return; /* no updates are required */
	if (pm->config->status == -EINPROGRESS)
		return; /* an update is already in progress; it'll issue this update when it completes */

	if (pm->requires_update & UPDATE_PULSE_ASLEEP){
		pm->configcr->wValue = cpu_to_le16( SET_PULSE_ASLEEP );
		pm->configcr->wIndex = cpu_to_le16( pm->pulse_asleep ? 1 : 0 );
		pm->requires_update &= ~UPDATE_PULSE_ASLEEP;
	}else if (pm->requires_update & UPDATE_PULSE_AWAKE){
		pm->configcr->wValue = cpu_to_le16( SET_PULSE_AWAKE );
		pm->configcr->wIndex = cpu_to_le16( pm->pulse_awake ? 1 : 0 );
		pm->requires_update &= ~UPDATE_PULSE_AWAKE;
	}else if (pm->requires_update & UPDATE_PULSE_MODE){
		int op, arg;
		/* the powermate takes an operation and an argument for its pulse algorithm.
		   the operation can be:
		   0: divide the speed
		   1: pulse at normal speed
		   2: multiply the speed
		   the argument only has an effect for operations 0 and 2, and ranges between
		   1 (least effect) to 255 (maximum effect).

		   thus, several states are equivalent and are coalesced into one state.

		   we map this onto a range from 0 to 510, with:
		   0 -- 254    -- use divide (0 = slowest)
		   255         -- use normal speed

Annotation

Implementation Notes