[태그:] linux

  • linux usb driver xbox360(1)

    집에서 놀고 있던 8년 전에 구매한 xbox 360 유선 usb 패드를 raspberry pi4에 등록하여 사용하고 싶다. 나온 지 오래되어 이미 xpad 리눅스 드라이버가 공개되어 있다. 그러나 usb, input을 한번에 사용하여 잘 이해되지 않는다. 남은 출장 기간 중 천천히 공부 하기로 했다.

    usb를 등록하려면 vendor id, product id, 연결되면 실행되는 call back probe, disconnect 등 을 알아야 한다. 인터넷에 공개된 좋은 자료, 소스를 공부했다. 관련 코드는 여기에 저장했다.

    usb-devices로 xbox 패드를 확인하면 (I가 interface 인듯하다) 0, 1, 2, 3인터페이스가 등록되지 않았다.

    pi@raspberrypi:~/rasp/xbox_driver $ usb-devices 
    T:  Bus=01 Lev=02 Prnt=02 Port=00 Cnt=01 Dev#=  3 Spd=12  MxCh= 0
    D:  Ver= 2.00 Cls=ff(vend.) Sub=ff Prot=ff MxPS= 8 #Cfgs=  1
    P:  Vendor=045e ProdID=028e Rev=01.14
    S:  Manufacturer=©Microsoft Corporation
    S:  Product=Controller
    S:  SerialNumber=0C59FC4
    C:  #Ifs= 4 Cfg#= 1 Atr=a0 MxPwr=500mA
    I:  If#=0x0 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=5d Prot=01 Driver=(none)
    I:  If#=0x1 Alt= 0 #EPs= 4 Cls=ff(vend.) Sub=5d Prot=03 Driver=(none)
    I:  If#=0x2 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=5d Prot=02 Driver=(none)
    I:  If#=0x3 Alt= 0 #EPs= 0 Cls=ff(vend.) Sub=fd Prot=13 Driver=(none)

    usb_register_dev로 interface 0, 1을 등록했다.

    pi@raspberrypi:~/rasp/xbox_driver $ usb-devices 
    T:  Bus=01 Lev=02 Prnt=02 Port=00 Cnt=01 Dev#=  3 Spd=12  MxCh= 0
    D:  Ver= 2.00 Cls=ff(vend.) Sub=ff Prot=ff MxPS= 8 #Cfgs=  1
    P:  Vendor=045e ProdID=028e Rev=01.14
    S:  Manufacturer=©Microsoft Corporation
    S:  Product=Controller
    S:  SerialNumber=0C59FC4
    C:  #Ifs= 4 Cfg#= 1 Atr=a0 MxPwr=500mA
    I:  If#=0x0 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=5d Prot=01 Driver=xbox360
    I:  If#=0x1 Alt= 0 #EPs= 4 Cls=ff(vend.) Sub=5d Prot=03 Driver=xbox360
    I:  If#=0x2 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=5d Prot=02 Driver=(none)
    I:  If#=0x3 Alt= 0 #EPs= 0 Cls=ff(vend.) Sub=fd Prot=13 Driver=(none)
    
    pi@raspberrypi:~/rasp/xbox_driver $ dmesg 
    [48226.848081] xpad is 0c15d4e5, xpad->udev is 14758ec9
    [48226.848101] interface is 913f883f
    [48226.848126] xbox360 1-1.1:1.0: interrupt in, out found. 55429370, 0119bb5d
    [48226.848528] xbox360 1-1.1:1.0: usb xbox360 driver was registerd
    [48226.848710] xpad is a5c87389, xpad->udev is 14758ec9
    [48226.848726] interface is f687e098
    [48226.848746] xbox360 1-1.1:1.1: interrupt in, out found. efcdcd2e, 2fcb2b2f
    [48226.849000] xbox360 1-1.1:1.1: usb xbox360 driver was registerd
    [48226.849162] xpad is 5c5394a1, xpad->udev is 14758ec9
    [48226.849178] interface is 7fa06804
    [48226.849196] xbox360 1-1.1:1.2: Could not find both interrupt-in and interrpt-out endpoints
    [48226.849211] error -6
    [48226.849304] xpad is 5c5394a1, xpad->udev is 14758ec9
    [48226.849320] interface is c9ea30ca
    [48226.849337] xbox360 1-1.1:1.3: Could not find both interrupt-in and interrpt-out endpoints
    [48226.849352] error -6
    [48226.849490] usbcore: registered new interface driver xbox360

    참조 사이트.

  • Essential Linux Device Drivers

    Essential Linux Device Drivers

    isbn: 9788960771499

    한글판 제목이 너무 길어 원서 제목을 썼다. 임베디드 개발자면 쉽게 이해하겠지만 배경 지식이 없는 나에게는 너무 어렵다. 그럼에도 기록을 남겨, 나중에 어떤 책이 좋은지 다시 판단하는 시간을 줄이고, 다시 보려 한다.

    책 내용에 코드가 많은데 독자가 실행시킬 수 없어 어떻게 돌아가는지 알 수 없다. 커널을 설명한 부분도 있는데, 이미 알고 있다면 안 읽어도 될 듯 하다.

    작가 꿈이 큰지 usb, i2c, 캐릭터 디바이스, 심지어 그래픽 드라이버까지 설명했다. 나같이 취미로 생각하는 사람이 이런 기술을 알 필요는 없다. 5~10년 사이에 사라질 수 도 있다. 다 읽기는 어렵고, 지루하고, 쓸모없어 대략 다음 장 개념 정도만 보면 될 듯 하다. 코드는 최신 리눅스 커널을 보는게 맞다.

    • 문자 드라이버
    • 직력 드라이버
    • 입력 드라이버
    • usb
    • 네트웍 인터페이스 카드

    Linux Device Drivers(isbn: 9780596005900)를 읽고 난 후 다시 봐야겠다. 이 분야 원탑 이라는데 인터넷에서 무료로 구할 수 있다. 절판이고 하고.

  • xbox 360 드라이버 만지기1

    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
  • 리눅스 커널의 구조와 원리

    리눅스 커널의 구조와 원리

    isbn: 9791158391980

    저자가 디버깅, 커널을 강조하다보니, 제목이 너무 길어졌다. 다행히 초심자가 쉽게 이해할 수 있는 내용이다. 책 2권이다 보니 사기 아까웠지만, 읽고나면 살만한 책이다. 이 책이 자세하게 설명하여 커널이 어떻게 동작하는지 알 수 있다. 내용이 인터넷에도 있겠지만, 내가 찾는 수고를 저자가 했고, 틀린 내용을 저자가 걸러줬다. 이렇게 보면 지출할 만한 가격이다.

    20년 전 리눅스를 시작하려 해도 괜찮은 책, 문서가 없었다. 그 시절 인터넷도 잘 발달되지 않아 따라 하기 어려웠다. 커널 구조, 동작 방식을 설명하는 책도 찾기 어려웠고, 파편화 된 영문 문서를 종합하여 이해할만한 시간, 능력, 의지도 없었다. PC 성능도 낮아 한번 커널 컴파일하면 4시간 정도 걸렸다. 커널 패닉에 닥치면 어떻게 해결할지 막막했다. 지금같이 휴대폰으로 찾아볼 수 없다보니. 진심이 아닌 취미로 접근하기 너무나도 어려웠다.

    다행히 시대가 변해 성능좋고 값싼 하드웨어를 쉽게 구할 수 있다. 이 책과 같은 좋은 교재로 짧은 시간에 익힐 수 있다. 커널에서 지원하는 디버그 툴이 함수 호출 내역을 표시한다. 인터넷도 발달되어 남이 작성한 문서를 쉽게 찾고, 사용할 수 있다. 의지만 있다면 과거 5년 학습 결과를 지금 6개월에 달성할 수 있어 보인다.

    책 차례를 보면 커널을 어떻게 접근할지 보인다. OS 기본이 프로세스이므로 task descriptor에서 시작한다. 인터럽트 또한 중요하여 다음에 있다. softirq, workqueue도 인터럽트 다음으로 쉽게 알 수 있다. 2권을 보면 그 외 타이머, 동기화, 스케줄러 등 기본 지식을 학습할 수 있다.

    책을 읽고 나면 커널을 쉽게 접근할 수 있고, 남이 작성한 문서를 이해할 수 있다는 자신감-실력이 아닌-을 갖는다. IoT 시대를 대비할 수 있는 지금 배우지 않을 이유가 없다. ON/OFF 스위치를 만들더라도 인터넷도 되고, 터치 패드 있는 제품이 뽀대 난다. 얼마인지 모르겠으나 정말 있다!! 수화물 5kg를 희생하여 미국까지 들고 온 보람있다.

  • spinlock, mutex 실습

    모듈을 로딩하면 kernel thread부터 만들어야 한다. 같은 책 초반에 나왔는데, 다시 보니 기억할 수 없다. 스레드를 만든 후 함수와 데이터를 넘겨야 하는데, 데이터가 void 포인터다. struct로 캐스팅 하고 싶은데, 에러가 났다. kernel 코드를 보고 괄호를 몇 번 붙였다. 아! ㅅㅂ. 구글 찾아보기보다 시(간)성비가 더 좋다.

    pi@raspberrypi:~/linux $ grep -wn "kthread_create" -r ./drivers
    ./drivers/usb/usbip/usbip_common.h:285:		= kthread_create(threadfn, data, namefmt, ## __VA_ARGS__); \
    ./drivers/usb/atm/ueagle-atm.c:2228:	sc->kthread = kthread_create(uea_kthread, sc, "ueagle-atm");
    ./drivers/usb/atm/usbatm.c:977:	t = kthread_create(usbatm_do_heavy_init, instance, "%s",
    ./drivers/usb/gadget/file_storage.c:3527:	fsg->thread_task = kthread_create(fsg_main_thread, fsg,
    ./drivers/usb/gadget/function/u_serial.c:1059:	info->console_thread = kthread_create(gs_console_thread,
    ./drivers/usb/gadget/function/f_mass_storage.c:2924:			kthread_create(fsg_main_thread, common, "file-storage");
    ./drivers/usb/host/dwc_common_port/dwc_common_fbsd.c:981:	retval = kthread_create((void (*)(void *))func, data, &thread->proc,
    ./drivers/iio/adc/ina2xx-adc.c:840:	task = kthread_create(ina2xx_capture_thread, (void *)indio_dev,
    
    pi@raspberrypi:~/linux $ vi ./drivers/usb/host/dwc_common_port/dwc_common_fbsd.c
    dwc_thread_t *DWC_THREAD_RUN(dwc_thread_function_t func, char *name, void *data)
    {
            int retval;
            dwc_thread_t *thread = DWC_ALLOC(sizeof(*thread));
    
            if (!thread) {
                    return NULL;
            }
    
            thread->abort = 0; 
            retval = kthread_create((void (*)(void *))func, data, &thread->proc,
                                    RFPROC | RFNOWAIT, 0, "%s", name);
            if (retval) {
                    DWC_FREE(thread);
                    return NULL;
            }
    
            return thread;
    }

    spinlock을 만든 후 초기화 하지 않으면, 사용할 수 없다. kernel에 메모리를 할당 받으면 초기화는 꼭 해줘야 하는 느낌이다.

    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/init.h>
    #include <linux/irq.h>
    #include <linux/interrupt.h>
    #include <linux/gpio.h>     //GPIO
    
    
    #include <linux/cdev.h>
    #include <linux/device.h>
    #include <linux/uaccess.h>
    #include <linux/fcntl.h>
    
    
    #define GPIO_10_OUT (10)
    #define DEVICE_DATA_MAX 256
    
    #include <linux/kthread.h>
    #include <linux/delay.h>
    #include <linux/spinlock.h>
    
    
    unsigned int GPIO_irqNumber;
    
    /*사용자 데이터 부분*/
    struct my_device_data{
    	struct cdev cdev;
    	int index;
    	char my_string[DEVICE_DATA_MAX];
    	struct timer_list simple_timer;
    	spinlock_t lock;
    	struct task_struct *kthread1;
    	struct task_struct *kthread2;
    } ;
    
    int run_this_thread1(void* data);
    int run_this_thread2(void* data);
    
    int run_this_thread1(void* data)
    {
    	//while(1)로 하면 kthread_stop을 할 수 없음.
    	//루프가 없으면 kthread_stop을 불렀을 경우, segment error.
    	//thread가 없다면, 종료를 할 수 없어 보임..
    	while(!kthread_should_stop())
    	{
    	//data is my_device_data
    	struct my_device_data *ptr_main_data;
    	//ptr_main_data = data;
    	ptr_main_data = (struct my_device_data(*))data;
    
    	//공통이 구조체 접근.
    	spin_lock(&ptr_main_data->lock);
    	ptr_main_data->index++;
    	//pr_info("spin lock is %0x\n",ptr_main_data->lock);
    	pr_info("==============\n");
    	pr_info("[+]index is %d\n", ptr_main_data->index);
    	pr_info("==============\n");
    	spin_unlock(&ptr_main_data->lock);
    	//pr_info("spin lock is %0x\n",ptr_main_data->lock);
    
    	msleep(500);
    	}
    	return 0;
    
    }
    
    int run_this_thread2(void* data)
    {
    	while(!kthread_should_stop())
    	{
    	//data is my_device_data
    	struct my_device_data *ptr_main_data;
    	ptr_main_data = (struct my_device_data(*))data;
    	//
    	//공통이 구조체 접근.
    	spin_lock(&ptr_main_data->lock);
    	ptr_main_data->index++;
    	pr_info("==============\n");
    	pr_info("[+]index is %d\n", ptr_main_data->index);
    	pr_info("==============\n");
    	spin_unlock(&ptr_main_data->lock);
    	msleep(500);
    	}
    
    	return 0;
    
    }
    
    static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
    {
    	/*Scheduling work queue*/
    	return IRQ_HANDLED;
    
    }
    
    
    //device driver 작성 부분.
    /*************드라이버 함수 ******************/
    static int mydriver_open(struct inode *inode, struct file *file);
    static int mydriver_release(struct inode *inode, struct file *file);
    static ssize_t mydriver_read(struct file *flip,
    		char *buf, size_t len, loff_t *off);
    static ssize_t mydriver_write(struct file *flip,
    		const char *buf, size_t len, loff_t *off);
    /********************************************/
    
    
    
    //file operation structure
    
    static struct file_operations fops = 
    {
    	.owner = THIS_MODULE,
    	.read = mydriver_read,
    	.write = mydriver_write,
    	.open = mydriver_open,
    	.release = mydriver_release,
    };
    
    static int mydriver_open(struct inode *inode, struct file *file)
    {
    	pr_info("Deviced file was opend.\n");
    	return 0;
    
    }
    
    static int mydriver_release(struct inode *inode, struct file *file)
    {
    	pr_info("Deviced file was closed.\n");
    	return 0;
    }
    
    
    static int mydriver_read(struct file *file,
    		char *buf, size_t len, loff_t *off)
    {
    	pr_info("read\n");
    	return 0;
    }
    
    
    static int mydriver_write(struct file *flip,
    		const char *buf, size_t len, loff_t *off)
    {
    	return 0;
    
    }
    
    dev_t dev = 0;
    static struct cdev my_cdev;
    static struct class *dev_class;
    struct my_device_data *main_data;
    static int __init init_hw(void)
    {
    	//디바이스 등록
    	if(( alloc_chrdev_region(&dev, 0, 1, "test_device") < 0))
    	{
    		pr_err("[!]character device was not allocated\n");
    		goto r_unreg;
    
    	}
    	pr_info("[=]%d-%d, was allocated\n", MAJOR(dev), MINOR(dev));
    
    
    	//초기화
    	cdev_init(&my_cdev, &fops);
    	pr_info("[=]driver was initialized\n");
    
    
    	//시스템에 추가
    	if((cdev_add(&my_cdev, dev, 1)) < 0)
    	{
    		pr_err("[!]cannot add device to kernel\n");
    		goto r_del;
    
    	}
    
    
    	//class 만듦.
    	if((dev_class=class_create(THIS_MODULE, "my_class")) == NULL)
    	{
    		pr_err("[!]cannot add class\n");
    		goto r_class;
    	}
    
    
    	if((device_create(dev_class, NULL, dev, NULL, "my_device")) == NULL)
    	{
    
    		pr_err("[!]cannot create device\n");
    		goto r_device;
    	}
    
    
    	//gpio 10번을 사용.
    	//export하여 간단히 사용.
    	//입력은 값을 써 넣을 수 없음. 출력으로 설정.
    	GPIO_irqNumber = gpio_to_irq(GPIO_10_OUT);
    	pr_info("[=]irq %d was assinged\n",GPIO_irqNumber);
    
    	//interrupt 등록 필요
    	if (request_irq(GPIO_irqNumber,
    				(void*)gpio_irq_handler,
    				IRQF_TRIGGER_RISING,
    				"my_device",
    				NULL))
    	{
    		pr_err("[!]my_device: cannot register IRQ\n");
    		goto r_gpio;
    	}
    	pr_info("[=]module was installed\n");
    
    
    	//메모리 공간 할당.
    	main_data = kmalloc(sizeof(struct my_device_data), GFP_KERNEL);
    
    
    	//spin lock init
    	spin_lock_init(&main_data->lock);
    	main_data->index=0;
    	if(!main_data)
    	{
    		pr_err("[!]cannot alloc memory\n");
    		goto r_memory;
    	}
    	pr_info("[=]got memory\n");
    
    	//thread create.
    	main_data->kthread1 = kthread_create(run_this_thread1, main_data, "my_thread1");
    	main_data->kthread2 = kthread_create(run_this_thread2, main_data, "my_thread2");
    
    	if(main_data->kthread1)
    	{
    		wake_up_process(main_data->kthread1);
    		pr_info("wake up thread1 at %p\n", main_data->kthread1);
    	}
    	if(main_data->kthread2)
    	{
    		wake_up_process(main_data->kthread2);
    		pr_info("wake up thread2 at %p\n", main_data->kthread2);
    	}
    
    	return 0;
    r_gpio:
    	gpio_free(GPIO_10_OUT);
    r_device:
    	device_destroy(dev_class,dev);
    
    r_class:
    	class_destroy(dev_class);
    r_del:
    	cdev_del(&my_cdev);
    
    r_unreg:
    	unregister_chrdev_region(dev,1);
    r_memory:
    	;
    
    	return -1;
    }
    
    static void __exit exit_hw(void) {
    	free_irq(GPIO_irqNumber, NULL);
    	gpio_free(GPIO_10_OUT);
    	//flush_work(struct work_struct *work);
    	device_destroy(dev_class,dev);
    	//class_unregister(dev_class);
    	class_destroy(dev_class);
    	cdev_del(&my_cdev);
    	unregister_chrdev_region(dev,1);
    	if(main_data->kthread1)
    		kthread_stop(main_data->kthread1);
    	if(main_data->kthread2)
    		kthread_stop(main_data->kthread2);
    	pr_info("kthread was stopped\n");
    	pr_info("wake up thread1 at %p\n", main_data->kthread1);
    	pr_info("wake up thread2 at %p\n", main_data->kthread2);
    
    
    	kfree(main_data);
    	printk(KERN_INFO "module was removed\n");
    }
    
    
    module_init(init_hw);
    module_exit(exit_hw);
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("noname");
    MODULE_DESCRIPTION("Hello, world!");
    

    spin lock을 적용하지 않을 경우, 겹쳐쓴다. 적용하면 순서대로 실행된다 믿는다.

    [ 1749.848372] ==============
    [ 1749.848376] [+]index is 108
    [ 1749.848380] ==============
    [ 1750.378381] ==============
    [ 1750.378392] [+]index is 109
    [ 1750.378399] ==============
    [ 1750.378414] ==============
    [ 1750.378421] [+]index is 110
    [ 1750.378428] ==============
    [ 1750.898416] ==============
    [ 1750.898431] [+]index is 111
    [ 1750.898439] ==============
    [ 1750.898461] ==============
    [ 1750.898470] [+]index is 112
    [ 1750.898478] ==============
    [ 1751.418404] ==============
    [ 1751.418417] [+]index is 113
    [ 1751.418426] ==============
    [ 1751.418446] ==============
    [ 1751.418455] [+]index is 114
    [ 1751.418463] ==============
    [ 1751.938485] ==============
    [ 1751.938501] [+]index is 115
    [ 1751.938512] ==============
    [ 1751.938537] ==============
    [ 1751.938547] [+]index is 116
    [ 1751.938556] ==============
    [ 1752.458509] ==============
    [ 1752.458529] [+]index is 117
    [ 1752.458538] ==============
    [ 1752.458560] ==============
    [ 1752.458570] [+]index is 118
    [ 1752.458578] ==============
    [ 1752.978388] ==============
    [ 1752.978394] [+]index is 119
    [ 1752.978398] ==============
    [ 1753.498497] kthread was stopped
    [ 1753.498515] wake up thread1 at 39208d2a
    [ 1753.498524] wake up thread2 at 5bdf5278
    [ 1753.498536] module was removed
    [ 1773.404079] [=]236-0, was allocated
    [ 1773.404089] [=]driver was initialized
    [ 1773.406890] [=]irq 54 was assinged
    [ 1773.406942] [=]module was installed
    [ 1773.406952] [=]got memory
    [ 1773.410837] wake up thread1 at 6acc0d97
    [ 1773.410859] wake up thread2 at 9613521a
    [ 1773.411132] ==============
    [ 1773.411143] [+]index is 1
    [ 1773.411151] ==============
    [ 1773.411794] ==============
    [ 1773.411805] [+]index is 2
    [ 1773.411812] ==============
    [ 1773.528622] ==============
    [ 1773.528626] ==============
    [ 1773.528636] [+]index is 4
    [ 1773.528637] [+]index is 4
    [ 1773.528640] ==============
    [ 1773.528641] ==============
    [ 1773.648584] ==============
    [ 1773.648585] ==============
    [ 1773.648589] [+]index is 6
    [ 1773.648590] [+]index is 6
    [ 1773.648593] ==============
    [ 1773.648594] ==============
    [ 1773.768583] ==============
    [ 1773.768584] ==============
    [ 1773.768588] [+]index is 8
    [ 1773.768589] [+]index is 8
    [ 1773.768591] ==============
    [ 1773.768594] ==============
    [ 1773.888583] ==============
    [ 1773.888587] [+]index is 9
    [ 1773.888589] ==============
    [ 1773.888591] ==============
    [ 1773.888595] [+]index is 10
    [ 1773.888598] ==============
    [ 1774.008583] ==============
    [ 1774.008585] ==============
    [ 1774.008588] [+]index is 12
    [ 1774.008591] [+]index is 12
    [ 1774.008594] ==============
    [ 1774.008596] ==============
    [ 1774.128592] ==============
    [ 1774.128593] ==============
    [ 1774.128597] [+]index is 14
    [ 1774.128599] [+]index is 14
    [ 1774.128601] ==============
    [ 1774.128604] ==============
    [ 1774.248635] ==============
    [ 1774.248638] ==============
    [ 1774.248648] [+]index is 16
    [ 1774.248652] [+]index is 16
    [ 1774.248657] ==============
    [ 1774.248663] ==============
    [ 1774.368631] ==============
    [ 1774.368642] [+]index is 17
    [ 1774.368649] ==============
    [ 1774.378613] ==============
    [ 1774.378622] [+]index is 18
    https://embetronicx.com/tutorials/linux/device-drivers/linux-device-drivers-tutorial-kernel-thread/

    https://embetronicx.com/tutorials/linux/device-drivers/spinlock-in-linux-kernel-1/

    https://embetronicx.com/tutorials/linux/device-drivers/linux-device-driver-tutorial-mutex-in-linux-kernel/