콘텐츠로 바로가기

now0930 일지

이런저런 생각

  • 홈
  • 비공개
  • 강좌
  • 잔여 작업 조회
  • 위치

xbox 360 드라이버 만지기1

내 라즈베리 파이에 usb 타입 xbox360 패드를 붙여 보고싶다. 리눅스 커널 xpad.c에 마이크로소프트 거의 모든 제품 usb vendor, product id가 등록되어 있다. xpad.c가 조금 복작하여 usb 인터럽트 사용 방법을 잘 이해할 수 없다. 키 입력 설정을 어떻게 할지는 나중 문제고… usb-skeletion.c 문서 역시 어렵다. 여기는 인터럽트 대신 대용량 데이터 전송을 목표로 했다.

그 중 가장 비슷한 usb 마우스 드라이버를 커널에서 찾았다. 커널이 성경과 같아 모든 물음에 대한 답을 한다. 200줄 약간 넘어가는 코드로 정말 인터럽트 핵심만 작성했다.

대략 구성은 아래와 같이 된다.

  • usb 구조체 선언.
  • 사용가능한 usb 번호 찾음, 등록.
  • 입력 설정.
  • 인터럽트 설정.

usb_fill_int_urb로 call 함수를 등록하여 인터럽트를 사용하면 된다. usb 인터럽트가 일정 주기로 발생한다. 계속 실행하기 위해 인터럽트로 실행할 함수 안에 usb_submit_urb를 넣는다. 알고보면 참 쉬운데 처음 이해하기 어렵다. xpad.c도 같은 방식이다. 처음에 왜 안보였는지…아직 키 입력을 설정하지 않았고, 커널에 test를 출력하도록 했다.

usb 구조체는 여러 정보를 쉽게 사용하려 군더더기를 많이 붙이는 듯 하다.

#include <linux/kernel.h>
#include <linux/input.h>
#include <linux/module.h>
#include <linux/usb/input.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/usb/input.h>
#include <linux/slab.h>
#define USB_XBOX_MINOR_BASE	1
		
//구조체 선언.
struct usb_xpad{
	struct input_dev *dev;		/* input device interface */
	struct usb_device *udev;	/* usb device */
	struct usb_interface *intf;	/* usb interface */
	__u8    irq_in_endpointAddr;    /* interrupt in address*/
	__u8    irq_out_endpointAddr;   /* interrupt out address*/
	struct usb_endpoint_descriptor *endpoint_in, *endpoint_out;
	signed char *data;		/* input data */
	struct urb *irq_in;		/* urb for interrupt in report*/
	struct work_struct work;	/* init/remove device from callback */
};
static struct usb_xpad *myPad;
static int xpad_init_input(struct usb_xpad *xpad);
static void xpad_deinit_input(struct usb_xpad *xpad);
static void usb_xpad_irq(struct urb *urb){
	pr_info("test\n");
	usb_submit_urb(urb, GFP_KERNEL);
}
static int xpad_init_urb(struct usb_interface *intf, struct usb_xpad *xpad);
static ssize_t xbox_read(struct file *file, char *buffer, size_t count,
			 loff_t *ppos)
{
	pr_info("device was read.\n");
	return 0;
}
static ssize_t xbox_write(struct file *file, const char *user_buffer,
			  size_t count, loff_t *ppos)
{
	return 0;
}
static int xbox_open(struct inode *inode, struct file *file)
{
	int retval;
	struct urb *ptr_urb;
	ptr_urb = usb_alloc_urb(0, GFP_KERNEL);
	pr_info("device was opened.\n");
	if (!ptr_urb) {
		retval = -ENOMEM;
		goto error;
	}
	return 0;
error:
	if(ptr_urb)
	{
		usb_free_urb(ptr_urb);
	}
	return retval;
}
static int xbox_release(struct inode *inode, struct file *file)
{
	pr_info("xbox was released\n");
	return 0;
}
static int xbox_flush(struct file *file, fl_owner_t id)
{
	return 0;
}
static const struct file_operations xboxtest_fops = {
	.owner =	THIS_MODULE,
	.read =		xbox_read,
	.write =	xbox_write,
	.open =		xbox_open,
	.release =	xbox_release,
	.flush =	xbox_flush,
	.llseek =	noop_llseek,
};
/*
 * usb class driver info in order to get a minor number from the usb core,
 * and to have the device registered with the driver core
 */
static struct usb_class_driver xbox_class = {
	.name =		"xbox%d",
	.fops =		&xboxtest_fops,
	.minor_base =	USB_XBOX_MINOR_BASE,
};
static struct usb_driver xpad_test_driver;
static const struct usb_device_id xpad_table[] = {
	//{ USB_INTERFACE_INFO('X', 'B', 0) },	/* X-Box USB-IF not approved class */
	//XPAD_XBOX360_VENDOR(0x045e),		/* Microsoft X-Box 360 controllers */
	{USB_DEVICE(0x045e, 0x028e)},
	{ }
};
static const signed short xpad_common_btn[] = {
	BTN_A, BTN_B, BTN_X, BTN_Y,			/* "analog" buttons */
	BTN_START, BTN_SELECT, BTN_THUMBL, BTN_THUMBR,	/* start/back/sticks */
	-1						/* terminating entry */
};
MODULE_DEVICE_TABLE(usb, xpad_table);
static int xpad_init_input(struct usb_xpad *xpad)
{
	struct input_dev *input_dev;
	int i, error;
	int pipe, maxp;
	input_dev = input_allocate_device();
	if (!input_dev)
		return -ENOMEM;
	xpad->dev = input_dev;
	usb_to_input_id(xpad->udev, &input_dev->id);
	input_set_drvdata(input_dev, xpad);
	/* set up standard buttons */
	for (i = 0; xpad_common_btn[i] >= 0; i++)
		input_set_capability(input_dev, EV_KEY, xpad_common_btn[i]);
	pipe = usb_rcvintpipe(xpad->udev, xpad->irq_in_endpointAddr);
	maxp = usb_maxpacket(xpad->udev, pipe, usb_pipeout(pipe));
	usb_fill_int_urb(xpad->irq_in, xpad->udev,
			pipe, xpad->data,
			(maxp > 8 ? 8 : maxp),
			usb_xpad_irq, xpad, 
			xpad->endpoint_in->bInterval);
	error = input_register_device(input_dev);
	if (error)
		goto err_free_dev;
	pr_info("usb input was registered\n");
	usb_submit_urb(xpad->irq_in, GFP_KERNEL);
	return 0;	//return ok;
err_free_dev:
	input_free_device(input_dev);
	return error;
}
static void xpad_deinit_input(struct usb_xpad *xpad)
{
	pr_info("xpad is %p, ->dev is %p.\n",xpad, xpad->dev);
	if(xpad->dev)
		input_unregister_device(xpad->dev);
}
static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
	struct usb_xpad *xpad;
	struct usb_device *udev;
	struct usb_endpoint_descriptor *ep_irq_in,*ep_irq_out;
	struct usb_host_interface *intf_tmp;
	int retval;
	udev = interface_to_usbdev(intf);
	xpad = kzalloc(sizeof(struct usb_xpad), GFP_KERNEL);
	if (!xpad)
		return -ENOMEM;
	//초기화
	//kzallocd으로 0으로 초기화
	//xpad->odata_serial = 0;
	xpad->udev = udev;
	xpad->intf = intf;
	myPad = xpad;
	pr_info("xpad is %p, myPad is %p\n", xpad->intf, myPad->intf);
	pr_info("interface is %p\n", intf);
	//if (intf->cur_altsetting->desc.bNumEndpoints != 2)
	//	return -ENODEV;
	intf_tmp = intf->cur_altsetting;
	// find common usb endpoint helper 사용
	//https://lkml.org/lkml/2020/9/21/1239
	
	/* set up the endpoint information */
	/* use only the first bulk-in and bulk-out endpoints */
	retval = usb_find_common_endpoints(intf_tmp,
			NULL, NULL, &ep_irq_in, &ep_irq_out);
	if (retval) {
		dev_err(&intf->dev,
			"Could not find both irq-in and irq-out endpoints\n");
		goto error;
	}
	xpad->irq_in_endpointAddr = ep_irq_in->bEndpointAddress;
	xpad->irq_out_endpointAddr = ep_irq_out->bEndpointAddress;
	xpad->endpoint_in = ep_irq_in;
	xpad->endpoint_out = ep_irq_out;
	xpad->irq_in = usb_alloc_urb(0, GFP_KERNEL);
	if (!xpad->irq_in) {
		retval = -ENOMEM;
		goto err_free_in_urb;
	}
	usb_set_intfdata(intf, xpad);
	pr_info("[info]: found interrupt end point, in: %d, out: %d", ep_irq_in->bEndpointAddress, ep_irq_out->bEndpointAddress);
	pr_info("probe \n");
	//pr_info("%04X:%04X pluged \n", id->idVendor, id->idProduct);
	retval = usb_register_dev(intf, &xbox_class);
	if (retval< 0) {
		pr_err("usb_register failed for the "__FILE__ "driver."
				"Error number %d", retval);
		usb_set_intfdata(intf, NULL);
		goto error;
	}
	/* let the user know what node this device is now attached to */
	dev_info(&intf->dev,
		 "USB Skeleton device now attached to USBSkel-%d",
		 intf->minor);
	//input으로 등록.
	xpad_init_input(xpad);
	
	return 0;
error:
	kfree(xpad);
	return retval;
err_free_in_urb:
	usb_free_urb(xpad->irq_in);
	return retval;
}
static void xpad_disconnect(struct usb_interface *intf)
{
	struct usb_xpad *xpad;
	xpad = usb_get_intfdata(intf);
	pr_info("xpad address is %p\n", xpad);
	xpad_deinit_input(xpad);
	usb_deregister_dev(intf, &xbox_class);
	usb_set_intfdata(intf, NULL);
	kfree(xpad);
	pr_info("disconnected\n");
}
static int xpad_suspend(struct usb_interface *intf, pm_message_t message)
{
	pr_info("suspendes\n");
	return 0;
}
static int xpad_resume(struct usb_interface *intf)
{
	pr_info("resumed\n");
	return 0;
}
static struct usb_driver xpad_test_driver = {
	.name		= "xbox360_test",
	.probe		= xpad_probe,
	.disconnect	= xpad_disconnect,
	.suspend	= xpad_suspend,
	.resume		= xpad_resume,
	.id_table	= xpad_table,
};
static int xpad_init_urb(struct usb_interface *intf, struct usb_xpad *xpad)
{
	int retval;
	xpad->irq_in= usb_alloc_urb(0, GFP_KERNEL);
	if (!xpad->irq_in) {
		retval = -ENOMEM;
		goto error;
	}
	return 0;
error:
	return retval;
}
	
// 모듈 loading, unloading 테스트를 위해 과거 방식으로 사용
//module_init(usb_xboxtest_init);
//module_exit(usb_xboxtest_exit);
//Macro
module_usb_driver(xpad_test_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("now0930");
MODULE_DESCRIPTION("Hello, xbox pad!");
[45180.903315] xpad is ea6ce566, myPad is ea6ce566
[45180.903332] interface is ea6ce566
[45180.903349] [info]: found interrupt end point, in: 129, out: 1
[45180.903360] probe 
[45180.903636] xbox360_test 1-1.2:1.0: USB Skeleton device now attached to USBSkel-1
[45180.903815] input: Unspecified device as /devices/virtual/input/input8
[45180.904290] usb input was registered
[45180.904475] xpad is 50c63d58, myPad is 50c63d58
[45180.904489] interface is 50c63d58
[45180.904506] [info]: found interrupt end point, in: 130, out: 2
[45180.904518] probe 
[45180.904685] xbox360_test 1-1.2:1.1: USB Skeleton device now attached to USBSkel-2
[45180.904841] input: Unspecified device as /devices/virtual/input/input9
[45180.905155] usb input was registered
[45180.905310] xpad is 3bfd76ed, myPad is 3bfd76ed
[45180.905323] interface is 3bfd76ed
[45180.905342] xbox360_test 1-1.2:1.2: Could not find both irq-in and irq-out endpoints
[45180.905432] xpad is 07d8d175, myPad is 07d8d175
[45180.905451] interface is 07d8d175
[45180.905469] xbox360_test 1-1.2:1.3: Could not find both irq-in and irq-out endpoints
[45180.905585] usbcore: registered new interface driver xbox360_test
[45180.909177] test
[45180.913170] test
[45210.971462] test
[45210.984592] test
[45210.997466] test
[45211.010595] test
[45211.075485] test
[45211.088606] test
[45211.109483] test
[45211.130612] test
[45211.151488] test
[45211.168617] test
[45211.181493] test
[45211.754713] test
[45211.799598] test
[45211.812711] test
[45212.329679] test
[45217.545918] usbcore: deregistering interface driver xbox360_test
[45217.546248] test
[45217.546378] xpad address is 99d70f22
[45217.546411] xpad is 99d70f22, ->dev is 8784f33d.
[45217.699130] disconnected
[45217.699578] test
[45217.699674] xpad address is 0764c1af
[45217.699692] xpad is 0764c1af, ->dev is 379dff77.
[45217.779137] disconnected

이 글 공유하기:

  • Tweet
발행일 2021-09-10글쓴이 이대원
카테고리 생활코딩 태그 driver, kernel, linux, raspberry, usb, xbox360

댓글 남기기응답 취소

이 사이트는 Akismet을 사용하여 스팸을 줄입니다. 댓글 데이터가 어떻게 처리되는지 알아보세요.

글 내비게이션

이전 글

docker cgoup issue

다음 글

곽재식의 세균 박람회

2025 5월
일 월 화 수 목 금 토
 123
45678910
11121314151617
18192021222324
25262728293031
4월    

최신 글

  • common mode, differential mode 2025-05-11
  • signal conditioner, 신호 처리기 2025-05-10
  • strain gage 2025-05-09
  • 칼만 필터 2025-05-01
  • positioner(I/P) 2025-04-26

카테고리

  • 산업계측제어기술사
  • 삶 자국
    • 책과 영화
    • 투자
  • 생활코딩
    • LEGO
    • ROS
    • tensorflow
  • 전기기사
  • 피아노 악보

메타

  • 로그인
  • 엔트리 피드
  • 댓글 피드
  • WordPress.org

페이지

  • 소개
  • 잔여 작업 조회
    • 작업 추가
    • 작업의 사진 조회
    • 작업 수정 페이지
  • 사진
    • GPS 입력된 사진
    • 사진 조회
  • 위치
    • 하기 휴가 방문지
    • 해외 출장

태그

android bash c docker driver FSM gps java kernel LEGO linux mysql network program opcua open62541 plc programmers python raspberry reinforcementLearning ros state space system program tensorflow transfer function 경제 미국 민수 삼국지 세계사 실기 에너지 역사 유전자 일본 임베디드 리눅스 전기기사 조선 중국 채윤 코딩 테스트 통계 한국사 한국어

팔로우하세요

  • Facebook
now0930 일지
WordPress로 제작.