I am reading a book on writing embedded system peripheral drivers and the author wrote a C callback function which i really don't understand and want to know the use of it, is it related to interrupts handling?
The function prototype is as following:
void Spi_CallbackRegister(SpiCallback_t Function, TYPE (*CallbackFunction)(type));
Short Answer: Callback function are function pointers with which you invoke them, when a specific event occurs.
Let us say you have an IO device like ethernet (MAC) device. Driver for such device will always be waiting for an events to occur, these events are either hardware or software interrupts, example:
A new packet has arrived.
A packet is going to dispatch.
Initiate packet transfer from device FIFO to host memory (DMA).
A protocol state has changed/etc.
To handle various interrupt(s) type you need a specific function for each of them, these functions are called Interrupt Service Routine(ISR) or Interrupt handler or a callback function. When an event is mapped to a function, it stores its pointer and thats why the term callback the function.
Related
I try to create a Linux kernel module that filters SCSI requests. The idea is very simple: I enumerate all Scsi_hosts by calling the function scsi_host_lookup() and modify the corresponding pointers
host->hostt->queuecommand.
This hooking works.
However, I need to do some complicated work during the dispatching scsi requests. I can't do it immediately in my hook function since it is called in the context that doesn't support interrupts.
I've already tried the following approach: I created a work queue, and in my hook function I put scsi requests into this queue and then returned zero. In the work queue functions I called original host->hostt->queuecommand functions. The system didn't crash but it was stuck. I think that it resulted because I returned constantly zeros in my hook function.
Could you help me with it?
So I'm writing drivers for UART on STM32 and even though I kind of have an idea on laying out the structure, I'd still want to clarify prior to implementing, and also considering how essential it is to keep the code clean and organized.
So I have main.c, sensor.c (application file that uses UART layer), hal_usart.c (which is the driver file).
I have heard different things regarding how application code should have no clue whatsoever about the driver APIs, and read up an article the other day that you could decouple the sensor code from HAL driver code using function pointers but not sure how I could do that here, and if that will decouple it considering I'm still passing a reference to USART_Handle struct that contains info like baudRate, parityControl, wordLength, reference to USART_TypeDef etc.
Below is a snippet of my idea:
// main.c
static USART_Handle pUSART;
int main(void) {
// initialize clocks/HAL
// ...initialize USART struct
// Get data via UART (calling application API)
GetData(&pUSART);
}
// sensor.c (application)
void GetData(USART_Handle *pUSART) {
HAL_USART_TX();
HAL_USART_RX(); // assuming data is stored in one of the struct members
}
// hal_usart.c (Driver file)
void HAL_USART_TX() {}
void HAL_USART_RX() {}
Since you would like to separate Application code and Driver code in an embedded system, I suggest you study how to implement callback functions in C also embedded systems.
Here is the reference.
The utility of function pointers is maximum when you want to change the pointer while the program is running; or, in other words, when you have at least two different functions and, based on some condition, you want to use one or another.
For example, let's say you have a routine which reads a sensor using a serial port, and the user can switch from a sensor to another (two serial ports or even two different methods). In that case, the sensor routine could call a function, to read, using a function pointer, and manage the back read data. The main program, by changing the function pointer, can instruct the sensor routine to use one or the other sensor.
If you don't need to change things at runtime, you can simply write your sensor routine making it call a reading function (external) defined in some other file. The documentation of the sensor routine simply has to state that somewhere must be defined a routine called "int sensor_get_data()".
This involves to design your own "internal protocol" based on what data goes and comes from the "detached drivers". For example, the sensor routine, which copes with a precise model of a sensor, can have the need to send a command and receive a response. You can write your sensor routine which construct the command and decodes the answer, and take away the low level details wrapping all them in the single function "int sensor_get_data(int command)". You then link or include the sensor code, and implement the function sensor_get_data() in the main.
The main() does not know the details of the sensor, it only knows that the sensor code, when needed, will call the function sensor_get_data(). But this function can use an UART, or an i2c or a spi, without the sensor routine even noticing that; moreover, this routine can use any of the three ports, perhaps basing on parameters the user can modify at runtime.
All this can be named "callback mechanism", and implements a sort of separation between declaration and implementation, as mentioned in the other answer. What I depicted is not different from that, but it is static - the program is compiled with a fixed callback, instead of passing a function pointer in every call.
I have created a project using STMCubeMX which includes a usbd driver configured as a virtual com port. I have it working and can receive data via the CDC_Receive_FS callback. My question is how is this callback called. Is it done at interrupt level, or is there some other mechanism. In particular, if I want to copy the data from the callback buffer into a queue which will be read by my main code, do I need some protection for concurrency (e.g. disable interrupts)?
Thanks.
It is called from an ISR. (Interrupt Service Routine)
Most likely it is called from:
OTG_HS_IRQHandler.
(with several levels of functions inbetween).
Here is a copy of my stack inside of a breakpoint.
CDC_Receive_HS() at usbd_cdc_if.c:456 0x801c758
USBD_CDC_DataOut() at usbd_cdc.c:699 0x8031592
USBD_LL_DataOutStage() at usbd_core.c:331 0x80318aa
HAL_PCD_DataOutStageCallback() at usbd_conf.c:249 0x801e486
HAL_PCD_IRQHandler() at stm32f7xx_hal_pcd.c:359 0x802d264
OTG_HS_IRQHandler() at stm32f7xx_it.c:288 0x801ab74
You most likely do NOT need to disable other interrupts just to copy this data to another buffer. I believe the buffer it uses should only be used by the usb receive. Copy the data to a separate buffer. The new buffer will need concurrency protection when used outside of this interrupt.
If you are using FreeRTOS, I recommend using the "xQueue" type as a buffer. It is thread safe. You use xQueueSendToBackFromISR inside of interrupts and xQueueSendToBack outside of interrupts.
I'd need to know where can I make zeroization for the received/transmitted network packets in the e1000 linux driver. I need to know this to pass one compliance requirement, but I'm not able to find in the code of the e1000 where to do zeroization of the network packet buffer (or if it already does the zeroization somewhere, that would be great)
I saw that it does ring zeroization when the interface goes up or down in the kernel in the file Intel_LAN_15.0.0_Linux_Source_A00/Source/base_driver/e1000e-2.4.14/src/netdev.c, in the e1000_clean_rx_ring() and e1000_clean_tx_ring() functions:
/* Zero out the descriptor ring */
memset(rx_ring->desc, 0, rx_ring->size);
But I'm not able to find where it should be done for each packet that the system receives/send.
So, does anybody know where is the place in the code where the buffer zeroization for the tx/rx packets should happen? I bet that it will introduce some overhead, but I have to do it anyway.
We're using the intel EF multi port network card: https://www-ssl.intel.com/content/www/us/en/network-adapters/gigabit-network-adapters/gigabit-et-et2-ef-multi-port-server-adapters-brief.html?
and the kernel 3.4.107
We're using the linux-image-3.4.107-0304107-generic_3.4.107-0304107.201504210712_amd64.deb kernel
EDIT: #skgrrwasme pointed correctly that the e1000_clean_tx_ring and e1000_clean_rx_ring functions seem to do the zeroize work, but as it is done only when the hw is down it is not valid for our compliance need.
So, it seems that the functions that are doing the work for each packet are e1000_clean_rx_irq and e1000_clean_tx_irq, but those functions doesn't zeroize data, they only free memory but doesn't make a memset() with 0 to overwrite memory (and that's what is required). So, what I think could be done is, as it is enough to zeroize data when rx or tx, inside e1000_clean_tx_irq() calls to e1000_unmap_and_free_tx_resource(), but in fact it only frees it, not zeroize it:
if (buffer_info->skb) {
dev_kfree_skb_any(buffer_info->skb);
buffer_info->skb = NULL;
}
So what I think is that we can wrote inside dev_kfree_skb_any(), the memset. That function calls to two functions:
dev_kfree_skb_any(struct sk_buff *skb)
{
if (in_irq() || irqs_disabled())
dev_kfree_skb_irq(skb);
else
dev_kfree_skb(skb);
}
So, something easy would be a call to skb_recycle_check(skb); that will do a:
memset(skb, 0, offsetof(struct sk_buff, tail));
Does this make sense? I think that with this, the memory will be overwritten with zeroes, and the work will be done, but I'm not sure...
TL;DR
As far as I can tell, both the transmit and receive buffers are already cleaned by the driver for both transmit and receive. I don't think you need to do anything.
Longer Answer
I don't think you have to worry about it. The transmit and receive buffer clearing functions, e1000_clean_rx__irq and e1000_clean_rx_irq, seem to be called in any interrupt configuration, and for both transmit and receive. Interrupts can be triggered with any of the following interrupt signaling methods: legacy, MSI, or MSI-X. It appears that ring buffer cleaning happens in any interrupt mode, but they call the cleaning functions in different locations.
Since you have two types of transfers (transmit and receive) and three different types of interrupt invocations (Legacy, MSI, and MSI-X), you have a total of six scenarious where you need to make sure things are cleaned. Fortunately, five of the six situations handle the packets by scheduling a job for NAPI. These scenarios are transmit and receive for Legacy and MSI interrupts, and receive for MSI-X. Part of NAPI handling those packets is calling the e1000_clean function as a callback. If you look at the code, you'll see that it calls the buffer cleaning functions for both TX and RX.
The outlier is the MSI-X TX handler. However, it seems to directly call the TX buffer cleaning function, rather than having NAPI handle it.
Here are the relevant interrupt handlers that weren't specifically listed above:
Legacy (both RX and TX)
MSI (both RX and TX)
MSI-X RX
Notes
All of my function references will open a file in the e1000e driver called netdev.c. They will open a window in the Linux Cross Reference database.
This post discusses the e1000e driver, but some of the function names are "e1000...". I think a lot of the e1000 code was reused in the newer e1000e driver, so some of the names carried over. Just know that it isn't a typo.
The e1000_clean_tx_ring and e1000_clean_rx_ring functions that you referred too appear to only be called when the driver is trying to free resources or the hardware is down, during any actual packet handling. The two I referenced above seem to, though. I'm not sure exactly what the difference between them is, but they appear to get the job done.
I'm using stm32f103 with GCC and have a task, which can be described with following pseudocode:
void http_server() {
transmit(data, len);
event = waitfor(data_sent_event | disconnect_event | send_timeout_event);
}
void tcp_interrupt() {
if (int_reg & DATA_SENT) {
emit(data_send_event);
}
}
void main.c() {
run_task(http_server);
}
I know, that all embedded OSes offer such functionality, but they are too huge for this single task. I don't need preemption, mutexes, queues and other features. Just waiting for flags in secondary tasks and raising these flags in interrupts.
Hope someone knows good tutorial on this topic or have a piece of code of context switching and wait implementation.
You will probably need to use an interrupt driven finite state machine.
There are a number of IP stacks that are independent of an operating system, or even interrupts. lwip (light weight ip) comes to mind. I used it indirectly as it was provided by xilinx. the freedos folks may have had one, certainly the crynwr packet drivers come to mind to which there were no doubt stacks built.
As far as the perhaps more simpler question. Your code is sitting in a foreground task in the waitfor() function which appears to want to be an infinite loop waiting for some global variables to change. And an interrupt comes along calls the interrupt handler which with a lot of stack work (to know it is a tcp interrupt) calls tcp_interrupt which modifies the flags, interrupt finishes and now waitfor sees the global flag change. The context switch is the interrupt which is built into the processor, no need for an operating system or anything fancy, a global variable or two and the isr. The context switch and flags/events are a freebie compared to the tcp/ip stack. udp is significantly easier, do you really need tcp btw?
If you want more than one of these waitfor() active, basically you don want to only have the one forground task sitting in one waitfor(). Then I would do one of two things. have the foreground task poll, instead of a waitfor(something) change it to an if(checkfor(something)) { then do something }.
Or setup your system so that the interrupt handler, which in your pseudo code is already very complicated to know this is tcp packet data, examines the tcp header deeper and knows to call the http_server() thing for port 80 events, and other functions for other events that you might have had a waitfor. So in this case instead of a multitasking series of functions that are waitfor()ing, create a single list of the events, and look for them in the ISR. Use a timer and interrupt and globals for the timeouts (reset a counter when a packet arrives, bump the counter on a timer interrupt if the counter reaches N then a timeout has occurred, call the timeout task handler function).