I2C 장치 구동 분석 - imx 6 에서 tsc 2007 저항 터치 스크린 구동 기반

9379 단어 Linux 커 널 구동
I2C 장치 구동 분석 - imx 6 에서 tsc 2007 저항 터치 스크린 장치 구동 기반
1. 주요 데이터 구조 설명
1.of_device_id (include/linux/mod_devicetable.h)
/*              */
struct of_device_id {
	char	name[32];
	char	type[32];
	char	compatible[128];
	const void *data;
};

2. struct device_driver (include/linux/device.h)
struct device_driver {
	/*      */
	const char		*name;
    /*    device    bus */
	struct bus_type *bus;
    /*     THIS_MODULE */
	struct module *owner;
	const char *mod_name; /* used for built-in modules */
	bool suppress_bind_attrs; /* disables bind/unbind via sysfs */
    /*        ,          */
	const struct of_device_id	*of_match_table;
	const struct acpi_device_id	*acpi_match_table;
	int (*probe) (struct device *dev);
	int (*remove) (struct device *dev);
	void (*shutdown) (struct device *dev);
	int (*suspend) (struct device *dev, pm_message_t state);
	int (*resume) (struct device *dev);
	const struct attribute_group **groups;
	const struct dev_pm_ops *pm;
    /*        */
	struct driver_private *p;
}

3. struct i2c_driver (include/linux/i2c.h)
i2c 장치 구동 방법 을 묘사 하 였 다.
struct i2c_driver {
	unsigned int class;
    /*   attach_adapter     */
	int (*attach_adapter)(struct i2c_adapter *) __deprecated;
    /* device driver       */
	int (*probe)(struct i2c_client *, const struct i2c_device_id *);
	int (*remove)(struct i2c_client *);
	void (*shutdown)(struct i2c_client *);
	void (*alert)(struct i2c_client *, unsigned int data);
	int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
	struct device_driver driver;
    /*         ID   */
	const struct i2c_device_id *id_table;
	int (*detect)(struct i2c_client *, struct i2c_board_info *);
	const unsigned short *address_list;
	struct list_head clients;
};

4. struct i2c_client() (include/linux/i2c.h)
이름, 주소 등 을 포함 한 실제 i2c 장 치 를 설명 합 니 다.
struct i2c_client {
	unsigned short flags;		/* div., see below		*/
	unsigned short addr;		/* chip address - NOTE: 7bit	*/
					/* addresses are stored in the	*/
					/* _LOWER_ 7 bits		*/
	char name[I2C_NAME_SIZE];
	struct i2c_adapter *adapter;	/* the adapter we sit on	*/
	struct device dev;		/* the device structure		*/
	int irq;			/* irq issued by device		*/
	struct list_head detected;
#if IS_ENABLED(CONFIG_I2C_SLAVE)
	i2c_slave_cb_t slave_cb;	/* callback for slave mode	*/
#endif
}

5. struct tsc2007
struct tsc2007 {
	/* input_dev */
	struct input_dev	*input;
	char				phys[32];
    /*    */
	struct i2c_client	*client;
	u16					model;
	u16					x_plate_ohms;
    /*      */
	u16					max_rt;
	unsigned long		poll_period; /* in jiffies */
	int					fuzzx;
	int					fuzzy;
	int					fuzzz;
	unsigned			gpio;
    /*     */
	int					irq;
    /*       */
	wait_queue_head_t	wait;
    /*          */
	bool				stopped;
    /*              */
	int	(*get_pendown_state)(struct device *);
};


2. 주요 함수 설명
1. probe 함수
probe 함 수 는 device 와 driver 가 일치 할 때 실 행 됩 니 다. 주로 구동 에 필요 한 메모리, 인 터 럽 트 등 자원 을 신청 하고 구동 관련 구조 체 구성원 을 초기 화 하 며 마지막 으로 input 을 신청 하고 등록 합 니 다.dev 장치.
/*       */
static int tsc2007_probe_dt(struct i2c_client *client, struct tsc2007 *ts)
{
	struct device_node *np = client->dev.of_node;
	u32 val32;
	u64 val64;

	if (!np) 
		return -EINVAL;

	if (!of_property_read_u32(np, "ti,max-rt", &val32))
		ts->max_rt = val32;
	else
		ts->max_rt = MAX_12BIT;

	if (!of_property_read_u32(np, "ti,fuzzx", &val32))
		ts->fuzzx = val32;

	if (!of_property_read_u32(np, "ti,fuzzy", &val32))
		ts->fuzzy = val32;

	if (!of_property_read_u32(np, "ti,fuzzz", &val32))
		ts->fuzzz = val32;

	if (!of_property_read_u64(np, "ti,poll-period", &val64))
		ts->poll_period = msecs_to_jiffies(val64);
	else
		ts->poll_period = msecs_to_jiffies(1);

	if (!of_property_read_u32(np, "ti,x-plate-ohms", &val32))
		ts->x_plate_ohms = val32;
	else
		return -EINVAL;

	/*   GPIO */
	ts->gpio = of_get_gpio(np, 0);
	if (gpio_is_valid(ts->gpio))
		ts->get_pendown_state = tsc2007_get_pendown_state_gpio;

	return 0;
}

/* probe function */
static int tsc2007_probe(struct i2c_client *client, 
			const struct i2c_device_id *id)
{
	struct tsc2007 *ts;
	struct input_dev *input_dev;
	int err;

	/*  ts    ,devm_kzalloc             */
	ts = devm_kzalloc(&client->dev, sizeof(struct tsc2007), GFP_KERNEL);
	if (!ts)
		return -ENOMEM;
	/*          ts */
	err = tsc2007_probe_dt(client, ts);
	if (err)
		return err;

	/*  input_dev     */
	input_dev = devm_input_allocate_device(&client->dev);
	if (!input_dev)
		return -ENOMEM;
	
	i2c_set_clientdata(client, ts);

	ts->client = client;
	ts->irq = client->irq;
	ts->input = input_dev;
	/*          */
	init_waitqueue_head(&ts->wait);
	input_dev->name = "TSC2007 TouchScreen";
	input_dev->phys = ts->phys;
	input_dev->id.bustype = BUS_I2C;
	input_dev->open = tsc2007_open;
	input_dev->close = tsc2007_close;
	input_set_drvdata(input_dev, ts);

	//        
	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
	//       
	input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
	/*      */
	/*         ,   */
	input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, ts->fuzzx, 0);
	input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, ts->fuzzy, 0);
	input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, ts->fuzzz, 0);

	/*       */
	err = devm_request_threaded_irq(&client->dev, ts->irq, tsc2007_hard_irq,
								tsc2007_soft_irq, IRQF_ONESHOT,
								client->dev.driver->name, ts);
	if (err)
		return err;
	tsc2007_stop(ts);

	/*   input   */
	err = input_register_device(input_dev);
	if (err)
		return err;
	
	return 0;
}

2. open, close, stop 함수
open 과 close 함 수 는 구동 의 열 림 과 닫 기 를 책임 집 니 다.
/* open   */
static int tsc2007_open(struct input_dev *input_dev)
{
	struct tsc2007 *ts = input_get_drvdata(input_dev);
	int err;

	/*         */
	ts->stopped = false;
	mb();

	/*        */
	enable_irq(ts->irq);

	/*  tsc2007  ,      */
	err = tsc2007_xfer(ts, PWRDOWN);
	if (err < 0) {
		tsc2007_stop(ts);
		return err;
	}
	return 0;
}

/* close   */
static void tsc2007_close(struct input_dev *input_dev)
{
	struct tsc2007 *ts = input_get_drvdata(input_dev);

	tsc2007_stop(ts);
}

/* stop   */
static void tsc2007_stop(struct tsc2007 *ts)
{
	/*         */
	ts->stopped = true;
	mb();
	wake_up(&ts->wait);
	/*        */
	disable_irq(ts->irq);
}


3. 소프트 인 터 럽 트 와 하 드 인 터 럽 트
터치 가 눌 렸 을 때 외부 에 들 어가 하 드 인 터 럽 트 를 중단 하고 하 드 인 터 럽 트 에서 소프트 인 터 럽 트 를 활성화 하 며 ad 값 을 읽 고 보고 하 는 등 소프트 인 터 럽 트 에서 진행 합 니 다.
/*     */
static irqreturn_t tsc2007_soft_irq(int irq, void *handler)
{
	struct tsc2007 *ts = handler;
	struct input_dev *input = ts->input;
	struct ts_event tc;
	u32 rt;

	/*  stop    ,            */
	while (!ts->stopped && tsc2007_is_pen_down(ts)) {
		/*   tsc2007     */
		tsc2007_read_values(ts, &tc);
		/*      */
		rt = tsc2007_calculate_pressure(ts, &tc);
		if (!rt && !ts->get_pendown_state)
			break;
		if (rt <= ts->max_rt) {
			input_report_key(input, BTN_TOUCH, 1);
			input_report_abs(input, ABS_X, tc.x);
			input_report_abs(input, ABS_Y, tc.y);
			input_report_abs(input, ABS_PRESSURE, rt);
			input_sync(input);
		}
		/*       */
		wait_event_timeout(ts->wait, ts->stopped, ts->poll_period);
	}
	input_report_key(input, BTN_TOUCH, 0);
	input_report_abs(input, ABS_PRESSURE, 0);
	input_sync(input);

	return IRQ_HANDLED;
}

/*     */
static  irqreturn_t tsc2007_hard_irq(int irq, void *handle)
{
	struct tsc2007 *ts = handle;
	/*    */
	if (tsc2007_is_pen_down(ts))
		return IRQ_WAKE_THREAD;

	return IRQ_HANDLED;
}

4. 계산 압력
/*      */
static u32 tsc2007_calculate_pressure(struct tsc2007 *tsc, struct ts_event *tc)
{
	u32 rt = 0;
	if (tc->x == MAX_12BIT)
		tc->x = 0;

	if (likely(tc->x && tc->z1)) {
		rt = tc->z2 - tc->z1;
		rt *= tc->x;
		rt *= tsc->x_plate_ohms;
		rt /= tc->z1;
		rt = (rt + 2047) >> 12;
	}
	return rt;
}

5. I2C 로 좌표 읽 기
/* i2c     */
static inline int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd)
{
	s32 data;
	u16 val;
	data = i2c_smbus_read_word_data(tsc->client, cmd);
	if (data < 0) {
		return data;
	}
	val = swab16(data) >> 4;
	return val;
}

/*  tsc2007        */
static void tsc2007_read_values(struct tsc2007 *tsc, struct ts_event *tc)
{
	tc->y = tsc2007_xfer(tsc, READ_Y);
	tc->x = tsc2007_xfer(tsc, READ_X);
	tc->z1 = tsc2007_xfer(tsc, READ_Z1);
	tc->z2 = tsc2007_xfer(tsc, READ_Z2);
	tsc2007_xfer(tsc, PWRDOWN);
}

6. 터치 스크린 을 눌 렀 는 지 확인
/*          */
static int  tsc2007_get_pendown_state_gpio(struct device *dev)
{
	struct i2c_client *client = to_i2c_client(dev);
	struct tsc2007 *ts = i2c_get_clientdata(client);

	/*     1 */
	return !gpio_get_value(ts->gpio);
}

/*          */
static bool tsc2007_is_pen_down(struct tsc2007 *ts)
{
	/*      ,      ,  true */
	if(!ts->get_pendown_state)
		return true;
	else
		/*        */
		return ts->get_pendown_state(&ts->client->dev);
}

좋은 웹페이지 즐겨찾기