Hallo, ich heiße Florian und bin Student. Innerhalb meines 6. Semesters habe ich in einer Firma ein BAC-Praktikum zu absolvieren, in dem ich eine BAC-Arbeit schreiben soll. Als Aufgabenstellung wurde mir die Implementierung eines Linux-Gerätetreibers aufgegeben, für den BeagleBone Black(Sitara AM3358). Dieser soll eine SPI Schnittstelle zur Verfügung stellen, in der der BeagleBone als Slave arbeitet. Damit ich das bewerkstelligen kann, möchte ich einen Linux-Treiber schreiben. Ich beschäftige mich nun zum ersten mal praktisch mit Linux-Treibern, also bitte ich um Nachsicht... ;) Ich bin nun schon so weit, dass der Char Device Treiber läuft. Um nun die Register des Prozessors zu manipulieren, hätte ich nun mit request_mem_region() mir einen Speicher allokiert und mit ioremap() die Register in den virtuellen Speicher geholt. Leider funktioniert die Funktion ioread() nicht, obwohl ich ihr den Pointer von ioremap() übergebe. ich hoffe, jemand weiß weiter... LG - ein schönes Wochenende Flo
Florian R. schrieb: > Dieser soll eine SPI Schnittstelle zur Verfügung stellen, in > der der BeagleBone als Slave arbeitet. https://groups.google.com/d/topic/beagleboard/0ge_TozyTIE https://groups.google.com/d/topic/beagleboard/UC6pUfQXsWg > ich hoffe, jemand weiß weiter... Ohne Quellcode?
Da das mit den oben genannten Möglichkeiten nicht geht, würde ich es gerne mit dem Linuxkernel implementieren, der Prozessor kann als Slave arbeiten, jedoch muss ich dazu auf die Register des Prozessors zugreifen können. Anbei der Code:
1 | #include <linux/module.h> |
2 | #include <linux/kernel.h> |
3 | #include <linux/fs.h> |
4 | #include <linux/ioport.h> |
5 | #include <asm/uaccess.h> |
6 | #include <asm/io.h> |
7 | |
8 | int init_module(void); |
9 | void cleanup_module(void); |
10 | static int SPI_open(struct inode *, struct file *); |
11 | static int SPI_release(struct inode *, struct file *); |
12 | static ssize_t SPI_read(struct file *, char *, size_t, loff_t *); |
13 | static ssize_t SPI_write(struct file *, const char *, size_t, loff_t *); |
14 | |
15 | #define SUCCESS 0
|
16 | #define FAIL -1
|
17 | #define DEVICE_NAME "hello_1" // DEV name as it appears in /proc/devices
|
18 | #define BUF_LEN 80 //Max length of the message from the device
|
19 | |
20 | static int Major; //Major number assigned to our device driver |
21 | static int Device_Open = 0; //Device Open? ==> Prevent multiple access to device |
22 | static char msg[BUF_LEN]; //The msg the device will give when asked |
23 | static char *msg_Ptr; |
24 | |
25 | unsigned long rev_address_start = 0x4803014C; |
26 | unsigned long len = 4; |
27 | struct resource* spi0_region = NULL; |
28 | void* pMcspi0 = NULL; |
29 | int register_value = 0; |
30 | |
31 | static struct file_operations fops = { |
32 | .read = SPI_read, |
33 | .write = SPI_write, |
34 | .open = SPI_open, |
35 | .release = SPI_release |
36 | };
|
37 | |
38 | //////////////////// INIT AND EXIT MODULE ///////////////////////////////////////////
|
39 | |
40 | int init_module(void) |
41 | {
|
42 | Major = register_chrdev(0, DEVICE_NAME, &fops); |
43 | |
44 | if(Major < 0) |
45 | {
|
46 | printk(KERN_ALERT "Registering char device failed with %d\n", Major); |
47 | return FAIL; |
48 | }
|
49 | |
50 | printk(KERN_INFO "I was assigned major number %d. To talk to\n", Major); |
51 | printk(KERN_INFO "the driver, create a dev file with\n"); |
52 | printk(KERN_INFO "'mknod /dev/%s c %d 0'.\n", DEVICE_NAME, Major); |
53 | printk(KERN_INFO "Try various minor numbers. Try to cat and echo to\n"); |
54 | printk(KERN_INFO "the device file.\n"); |
55 | printk(KERN_INFO "Remove the device file and module when done.\n"); |
56 | |
57 | printk(KERN_INFO "Try to request_mem_region() for IO-memory!"); |
58 | |
59 | spi0_region = request_mem_region(rev_address_start, len, DEVICE_NAME); |
60 | |
61 | if(spi0_region == NULL) |
62 | {
|
63 | printk(KERN_INFO "Error in allocating memory region!!!"); |
64 | return FAIL; |
65 | }
|
66 | |
67 | printk(KERN_INFO "Allocating memory region (request_mem_region()) was successful!"); |
68 | printk(KERN_INFO "Try to ioremap() IO-memory!"); |
69 | |
70 | printk(KERN_INFO "START: %d", (*spi0_region).start); |
71 | printk(KERN_INFO "START: %d", (*spi0_region).end); |
72 | printk(KERN_INFO "NAME: %s", (*spi0_region).name); |
73 | //printk(KERN_INFO "NAME: %l", (*spi0_region).flags);
|
74 | |
75 | /* pMcspi0 = ioremap(rev_address_start, len);
|
76 |
|
77 | if(pMcspi0 == NULL)
|
78 | {
|
79 | printk(KERN_INFO "Error in remapping memory !!!!!");
|
80 |
|
81 | printk(KERN_INFO "Deallocation of memory_region: release_mem_region()");
|
82 | release_mem_region(rev_address_start, len);
|
83 |
|
84 | return FAIL;
|
85 | }
|
86 |
|
87 | printk(KERN_INFO "Remapping IO-memory was successful!");
|
88 |
|
89 | printk(KERN_INFO "Try to read from IO-memory!");
|
90 |
|
91 | register_value = ioread32(pMcspi0);
|
92 |
|
93 | printk(KERN_INFO "Tried to read from IO-memory!");
|
94 |
|
95 | printk(KERN_INFO "The requested revision address holds the value: %d", register_value);
|
96 |
|
97 | */
|
98 | |
99 | printk(KERN_INFO "Initialization of SPI module was successful"); |
100 | |
101 | return SUCCESS; |
102 | }
|
103 | |
104 | void cleanup_module(void) |
105 | {
|
106 | //int ret = unregister_chrdev(Major, DEVICE_NAME);
|
107 | //returns void !!!
|
108 | //if(unregister_chrdev(Major, DEVICE_NAME) < 0)
|
109 | //{
|
110 | //printk(KERN_ALERT "Error in unregister_chrdev: %d\n", ret)
|
111 | //}
|
112 | |
113 | printk(KERN_INFO "Free allocated memory space"); |
114 | |
115 | //free allocated memory space
|
116 | //iounmap(pMcspi0);
|
117 | release_mem_region(rev_address_start, len); |
118 | |
119 | unregister_chrdev(Major, DEVICE_NAME); |
120 | printk(KERN_ALERT "Unregister char device completed!"); |
121 | }
|
122 | |
123 | ///////////////////// FILE-OPERATIONS ///////////////////////////////////////////
|
124 | |
125 | //called when a process tries to open the device file, like "cat /dev/mycharfile"
|
126 | static int SPI_open(struct inode *inode, struct file *file) |
127 | {
|
128 | static int counter = 0; |
129 | |
130 | if (Device_Open) |
131 | {
|
132 | return -EBUSY; |
133 | }
|
134 | |
135 | Device_Open++; |
136 | sprintf(msg, "I already told you %d times Hello world!\n", counter++); |
137 | msg_Ptr = msg; |
138 | try_module_get(THIS_MODULE); |
139 | |
140 | return SUCCESS; |
141 | }
|
142 | |
143 | //called when a process closes the device file
|
144 | static int SPI_release(struct inode *inode, struct file *file) |
145 | {
|
146 | Device_Open--; //We are ready for our next caller |
147 | |
148 | module_put(THIS_MODULE); |
149 | |
150 | return 0; |
151 | }
|
152 | |
153 | //called when a process, which already opened the dev file, attemps to read from it
|
154 | static ssize_t SPI_read(struct file *filp, char *buffer, size_t length, loff_t *offset) |
155 | {
|
156 | //filp: see include/linux/fs.h
|
157 | //buffer: buffer to fill with data
|
158 | //length: length of the buffer
|
159 | |
160 | //Number of bytes actually written to the buffer
|
161 | int bytes_read = 0; |
162 | |
163 | //if end if message => sign EOF
|
164 | if(*msg_Ptr == 0) |
165 | {
|
166 | return 0; |
167 | }
|
168 | |
169 | //Put data into buffer
|
170 | while(length && *msg_Ptr) |
171 | {
|
172 | //buffer is in the user data segment, not kernel,
|
173 | //therefore "*" wont work => we have to use put_user
|
174 | //put_user copies data from kernel data segment to user data segment
|
175 | put_user(*(msg_Ptr++), buffer++); |
176 | |
177 | length--; |
178 | bytes_read++; |
179 | }
|
180 | |
181 | //Most read functions are returning the number of bytes put into buffer
|
182 | return bytes_read; |
183 | }
|
184 | |
185 | //called when a process writes to dev file: "echo "hi" > /dev/hello
|
186 | static ssize_t SPI_write(struct file *filp, const char *buff, size_t len, loff_t *off) |
187 | {
|
188 | printk(KERN_ALERT "Sorry, this operation is not supported.\n"); |
189 | return -EINVAL; |
190 | }
|
Florian R. schrieb: > Um nun die Register des Prozessors zu manipulieren, Was hat das mit einem SPI-Devicetreiber zu tun? Was für Daten soll der "Slave"-Treiber denn dem Master zur Verfügung stellen? Das klingt reichlich wirr.
Moin, von einem solchen Vorhaben kann ich gleich abraten, ein Linux-Host kann niemals zuverlässig als SPI Slave arbeiten. Das hat schlicht damit zu tun, dass das Timing von Linux nicht garantiert werden kann. Was geht, ist i2c. Tut mir leid, aber hier erschleicht sich der Eindruck, dass der Aufgabensteller nicht viel nachgedacht hat. Für einen SPI Slave ist auf jeden Fall eine HW-Implementierung nötig. Also, wenn es denn eine Übungsaufgabe mit Sinn sein soll: Gleich zum FPGA greifen. Grüsse, - Strubi
Es wäre vielleicht eine bessere Idee, den SPI-(Master-)Treiber entsprechend anzupassen: https://e2e.ti.com/support/arm/sitara_arm/f/791/p/281877/987362 https://patchwork.kernel.org/patch/76677/
Strubi schrieb: > ein Linux-Host kann niemals zuverlässig als SPI Slave arbeiten. Eine allgemeine SPI-Schnittstelle, bei der man in Echtzeit reagieren muss, ist in der Tat nicht möglich. Allerdings haben die McSPI FIFOs und DMA-Unterstützung; je nach Art des tatsächlich verwendeten Protokolls könnte man damit durchaus etwas anfangen.
Ich kann hier nur auf die Möglichkeit verweisen mit dem reservieren eines der Cores für den Echtzeit betrieb. Stichwörter „Mercury” und „Cobalt”. So würde das Linux um den Echtzeit fähigen „Cobalt” erweitert.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.